zl程序教程

您现在的位置是:首页 >  IT要闻

当前栏目

效率提升10倍,网易游戏面向终态的应用交付实践

2023-03-20 15:38:43 时间

讲师介绍

林香鑫,网易游戏团队负责人。2014年加入网易游戏,目前任网易游戏技术中心平台服务组团队负责人、技术专家,长期负责运维开发方向的工作,当前主要负责 CMDB 和配置管理、流程引擎、交付平台、基础组件和工具等研发管理工作。

一、应用交付形态

1、应用交付

大家对应用交付都很熟悉,游戏应用交付简单来说就是将游戏代码分发到服务器上对外提供服务,然后玩家通过客户端连接,使玩家能够顺利进入体验游戏。

应用交付这一过程说起来简单,但在准备过程中,需要运维同学完成许多工作。例如资源管理、配置管理、多环境需求、面向游戏的业务运维。游戏的业务运维与其它类型应用的业务运维有很大区别,这也是我们在设计整套机制时遇到的问题,下文中的许多案例也会围绕这一点展开。

游戏的发布和运维具有特殊性,例如游戏会强调开服、关服时间,可能会对整个资源的交付时间以及开服时效有较高的要求。除此之外,游戏本身是一个交互式的服务,它可能会影响玩家的体验,所以对于日常服务运维过程中的问题排查,也会有较高的时间要求。

2、应用交付形态

在服务交付的过程中,我们都用什么工具或者手段来解决上文中提到的问题呢?

首先,shell脚本大家都很熟悉,在早期,运维同学会用这种方式完成比较简单的部署。之后逐渐用较为规范化的流程工具将日常的运维流程串联起来。在这一过程中,我们内部诞生了一个被称为Aladdin的自动化系统,它满足了现阶段大量游戏的自动化运维需要。后来随着云原生的演进,业界也逐渐出现了一些资源编排技术,这一阶段我们做过一些简单的尝试,但并未持续太久。

最后我们直接使用了以应用为中心的编排方式,在多环境下实现快速应用交付。在社区中,比较有代表性的是叫KubeVela的开源产品,在我们内部则是通过一个叫Atlasx的产品实现了一套应用编排机制。

在上图整个过程中,自动化程度是逐步提高的。前两个阶段自动化程度提高体现在工具或技术上,步入资源编排或应用编排之后,自动化体现在了基础设施是否可编程,甚至业务逻辑是否可编程。与前面两个阶段相比,后面两个阶段更强调整个应用编排的可编程能力,下面的分享我将围绕这一内容展开。

二、问题和解决方案

1、面临的挑战

上文中提到我们内部已经有一个大型的系统去承载标准化的流程运转,那么我们为什么还要去实现可编程能力呢?

如果大家经常玩游戏或进入过网易游戏官网的话,可以关注到仅页面展示的可能就有近百个游戏,虽然这些游戏看上去大同小异,但其实不然。例如有的游戏可能走国内IDC部署,也有可能走海外发行, 有可能是滚服,也有可能是单一服,甚至还有之前某一阶段出现的全球服,这些差别使我们的游戏运维存在很大差别。

这些差别首先体现在异构性上,一个游戏会因为引擎的不同导致其架构甚至业务类型的不同,例如制作页游经常选择滚服这一类型,这就意味它的管理模式也会不同。

上文中提到有的游戏走国内IDC部署,有的则走海外发行,我们除了自身私有云的构建,海外发行的游戏还会用上公有云,例如AWS、GCP等,这导致了我们的运维人员在资源准备或游戏部署的过程中,会面临基础设施的异构性问题。

除异构性外,管理的多样性也是一个问题。我们每天面对的资源数量极其庞大,仅仅一个计算资源、网络资源、存储资源拆分出来,我们就需要面对许多的实体,其中还未包括一些服务本身系统配置的维护。再回到运维操作本身,游戏运维操作会更复杂,这也是上文中提到游戏运维可能比传统业务运维更复杂的原因。根据我们的不完全统计,每个SRE在完成一些日常工作的过程中可能会涉及到100多个操作,这给运维工作带来了许多问题。

于是,我们开始思考一个问题,如果围绕像图中这样一个游戏架构,人工重复部署100次,能保证这100次部署出的游戏服是等价的吗?

在过去我们无法回答,因为我们在一个游戏服交付过程中,不可避免会出现问题,那么原因是什么呢?因为这些事情都是人为进行的,人的能力、状态、知识背景等都会影响人的行为,导致其最终的交付成果不同。

2、应用交付的目标

人无法像函数式编程一样,在接收同样的输入后,执行固定的步骤,输出同样的结果。但是业务层面又要求不论何人在何时何地部署、部署多少次,都能得到同样的游戏服。因为对玩家来讲,他感受的是游戏服务的体验,我们不可能给玩家提供一个差异性非常高的游戏服务,导致可能玩家在体验时出现非常卡顿这种不好的体验。

我们的目标是消除这种差异性,所以我们最终要通过一些技术化手段提供标准化、统一化的应用,我们将这一过程称为一致性交付。

为实现这一过程,我们需要解决三方面的问题。

  • 不同的环境能够一致定义。我们需要解决环境的差异性问题。
  • 不同的游戏类型能够一致定义。我们需要有一个模型框架,能够把我们现在所有的游戏类型以这种模型框架定义出来。
  • 运维能力可描述、可管理、可插拔。上文中提到交付结果可能与人的能力、状态有关,我们应该把这种运维经验通过技术化或者工程化的手段积累、沉淀下来,最终做到知识驱动代替人工驱动,这也是我们整个项目的目标。

为达到上述目标,目前有两种方案可供选择。

3、解决方案

  • 基于命令式的交付。上图左侧是我们内部的流程,这些流程都是靠人工驱动,过往经验表明人为驱动不可能实现一致性交付。
  • 基于声明式编排交付。这一方式也更适合如今业界的发展。根据规范编写编排文件,通过提交编排启动服务。

但是声明式编排的底层逻辑是整个业务可编程。那么我们是怎么做的呢?

上文中提到的人为驱动模式不仅复杂,而且多种内容交叉。我们需要通过分层将各类内容进行区分,为了使用户得到一致性体验,我们需要达到每一层的一致性交互。

首先我们从底层的基础设施入手,现在不论是国内外研发,都有相应的基础设施,它能够为我们提供一些等价的环境能力,但是它暴露出的API服务对我们来说不一定一致,所以我们需要提供一个统一的资源访问层,不论是对底层资源还是系统编排进行操作,它都能提供一致的资源访问行为。

解决了基础设施的异构性问题,面对业务架构层面的不一致,我们又是怎么做的呢?

首先我们需要识别区分现在有多少种类型或多少种架构的游戏,通过上文中提到的模型框架将其组织起来,最终体现为一份编排。有了这份编排后,基本上就可以描述定义我们需要一致性定义的内容。

而到了业务层面,不论是开发阶段、测试阶段,还是生产阶段的任何一个操作,我们都可以基于这种编排实现它的变更,从而实现面向用户的一致性交付体验。

我们通过这一方式进行封装与交付之后,不论是SRE还是游戏研发的同学,都能够在最上层得到比较一致的体验。

有了上述模式后,我们重塑了整个游戏服的交付过程(如下图所示)。

首先我们要先完成游戏架构的定义,其次基于这样的定义实现整个游戏集群的编排,然后通过上文中提到的集群创建与交付模式,完成整个游戏集群的一致性交付,最终通过拓扑图的方式呈现整个集群的状态信息。

4、具体实践

接下来介绍这个过程中我们进行了哪些实践。

1)应用可定义

首先我们对架构进行分析,到底要有哪些组成部分才能够完成一个游戏集群的组装。


如上图所示,我们一个游戏集群运转起来可能需要mongo服务,可能需要etcd服务。游戏服务本身可能有这样一些进程,例如commander、game、world、gate。对于一个游戏集群,我们能够把它拆解成一个个比较独立的个体,在这一阶段我们要完成对这些单一个体的定义。

2)资源可定义

以MongoDB为例,我们声明它需要使用多少CPU,需要什么类型的、多大的磁盘,也包括它要部署在哪里,可能的shard数量、版本,通过这种方式,我们就把一个资源的定义固化。今后所有人对资源的需求都以这样的方式去声明,其他内容也是一样。

大家接触过k8s、Docker等声明式的语言,制定一个声明式的逻辑并不难,但其背后逻辑还是整个基础设施的可编程能力。

那么我们整个基础设施的可编程能力处于什么样的阶段呢?得益于网易游戏过去几年云化与SaaS化的发展,我们内部有了一整套比较成熟的基础设施体系,同时它又能以API的方式去提供服务,这为我们提供了很大便利。不论是云网络、容器,还是MongoDB、MySQL、Etcd等服务,我们都实现了云化或SaaS化,所以能够较好地对接这部分内容。

看起来所有的定义都不难,但是背后整个可编程的能力才是重点。

3)架构可定义

有了这些个体,随意将它们组装到一起并不能形成一个架构。从上文中的架构中我们可以看出,服务间会互相调用,底层的资源间可能也有一些固化的调用关系。

上图中是我们实现的一个简单的游戏架构,从中可以发现,整个游戏架构驱动起来包括许多内容,例如基础环境、项目配置、可对外开放的端口范围,以及网络资源的使用与容量,这些东西也是我们在架构定义中需要去思考补齐的一部分。这背后体现出的是我们对一个应用架构模型的定义。

大家使用 K8S、Docker、Compose的定义时会发现不同,造成这种不同的原因是它们的模型定义不同,即它们的模型框架不同。所以我们为了适应自己内部的体系,我们也自己定义了一套应用声明的模型,下图中是架构部分。

4)运维可定义

对游戏来说,在运维工作上会遇到一些简单的Web类服务不会遇到的问题。

例如有些游戏会开关服,这里涉及到开关网络的动作,我们内部其实能够把它收敛下来。通常来说,开关网络包括几种动作,首先是公网全部开放访问,然后面向玩家开放连接,其次仅开放办公网,QA可以连接游戏服做当次版本的验证。而在close状态下,QA和玩家都无法访问。

但历史经验告诉我们,这看起来是简单的几个状态之间的转换,但要实现这些状态背后可能会有一些关注不到的点。例如我们要求在limit状态下禁止玩家连接,但可能因为是从open的状态切换到limit状态,这时外部玩家的连接状态是没有断掉的。我们在这方面是踩过坑的,即我们以为进入了limit状态,但其实这个时候玩家还在玩,对一些没有注意到这一情况的人员,如果他是用另外一种方式,例如上文提到的命令式的方式,他可能调用API实现了状态的切换,保证新的玩家进不来,但是原来处于连接状态的外部玩家还可以继续玩,这可能就导致了一个故障。

但如果我们通过声明式的方式把这些东西收敛,将上文中提到的运维策略定义出来,例如进去limit状态就一定会清除剩余的连接流量,这就实现了我们在运维操作上一致性。我们也已经实现了这一点。

游戏还有另一个特殊点,它是有状态的,这导致一个进程会启动多个端口,除了本身要去对玩家提供服务的端口外,调试代码、进行运营操作还需要额外提供端口。如果我们像之前一样通过人工规划端口,其实很难保证一致性。但如果我们把它面向端口的分配定义成一个可声明的运维操作,其实很简单,即声明周围有哪些端口,可能要开多少个,通过系统本身的实现,就可以成功分配好这些端口,包括端口是否连续等都可以在这样的运维策略中实现。

以上就是前文中提到的应用可定义、架构可定义以及运维可定义,我们是通过上述方式逐步实现的,但是我们真的把一个应用定义出来就够了吗?

大概在一年半前我们实现过第一版,定义出来的编排文件大概有1000多行。大家都知道一个代码文件如果行数过多是很难维护的,改动它也容易出问题。当你面对一个1000多行编排的时候,你可能是崩溃的,所以这一版本就无法继续了。

接下来我们进行了一些参考和调研,例如AWS针对用户使用方面有一些原则,这里我们重点考虑一点,用户应该考虑的是架构,而不是基础设施。我们原来那份1000多行的版本中,包括了许多内容,这是比较繁杂的。

我们进一步了解到阿里和微软也在推进OAM即应用开发模型,其中一个关键点就是区分使用者,关注点分离,另外是其运维能力能够模块化封装、可管理,这与我们的目标适配,这个东西我们到底如何去实现呢?

上图模型我们可以借鉴,但是无法套用,这与我们内部一些基础设施的状态有关。在这基础上我们对该模型进行了扩展。首先是环境,还有Stack以及全局trait。扩展后,我们的游戏基本架构大约如上图右侧所示。

我们的应用实现流程如上图,其中最复杂是OAM解释器逻辑以及整个provider的执行逻辑,这里我们会根据前文中的架构定义,对整个编排模型做解析,然后形成后续要去执行的工作计划。之前定义的所有组件的执行流程都会体现在里面,最终通过这种有向无环图的方式实现整个业务流程的交付。

这种关注点分离可以给我们带来什么呢?如果我们在Istio微服务这样的路由转发需求下,如果按以前我们要去理解那些纯概念的话,我们需要知道gateway、VS、destinationrule怎么配置,这部分内容对于大多同学来说较难上手,但如果我们把这些内容封装抽象形成一个基本的路由定义, 则容易很多。

还有一个更复杂的问题就是我们怎样实现运维能力的可管理,不同环境下要求不同。

研测环境下,我们是可以直连的。我们开放了 Gate端口让客户端可以直连,这时候不需要配任何的端口转发策略。但是对于某种游戏的线上环境,它可能要求集群独享一个EIP,并通过DNAT 的方式实现访问。有的模式可能更加特殊,例如在滚服机制下,假设我们这个量级是1万个服务的话,IP资源肯定是不够的。所以在这个情况下,我们要实现多集群共享EIP。

上述仅仅是运维能力上的不同,但事实上对Gate的管理是比较一致的。通过这样一种能力的管控,我们可以明确是否需要EIP,EIP的管理模式、分配模式是什么样子的,通过这种方式把运维能力定义出来,并且让它能够在这样的配置中去管理。

最后我们将这些内容汇总,以前信息是围绕人的,现在我们通过一份编排把这些内容都驱动了。因为所有信息都是自包含的,这就很好地跟我们整个内部的运维配置数据体系 (CMDB)实现了联动。

其中最有价值的是关系数据。游戏群组交付之后,形成了这样的一个业务拓扑,但是把它放到整个架构中,它仅仅是其中的一小部分。这时我们可以做许多事,例如我们通过这种方式明确我与外部依赖的MongoDB是否连通的、一个宿主如果出现数组故障是否会影响到我,还有一些报警收敛,我们都可以基于这样的整套机制实现,目前我们在十几二十个场景进行了落地。

三、当前成果

接下来分享一下当前的成果。

我们这套机制开始使用了近一年,当前重点项目也在逐步交接,我们能够适应多种游戏的架构与微服务。按最保守的评估方式,我们也能够将交付效率提高到10倍。例如从之前一个多小时,现在我们控制在5分钟或3分钟之内。整体的使用规模也在不断扩张,现在达到了千级别。上图提到的每天的发布操作频率也证明这套机制一直在使用状态。

四、未来展望

上文中提到的许多内容都离不开业务架构、应用模型、基础设施及代码这三个核心的因素。对我们来说,游戏引擎决定了业务架构。整个游戏行业在发展,引擎本身的能力也在发展,引擎发展之后,就会影响业务架构。

例如我们在做一些游戏微服务化的工作,微服务化的结果就是架构的调整或改变,架构的改变需要在应用模型上进一步拓展,但是整个应用模型离不开底层的基础设施,依赖于整个基础设施的可编程能力,所以这三个部分是不可分割的。这决定了我们在不同层的工作人员要联动才能解决问题,未来我们也都会围绕这一方面不断去发展,从而促进我们整个游戏的发展。

这套平台的发布,是我们针对基于OAM以及游戏上云应用管理交付使用场景的实践。在这个过程中,我们也迭代出了一套怎么面向游戏定义模型架构的方式,目前也在持续使用中。

整个游戏运维有两大难点,一个是状态,一个是复杂的运维逻辑,借助整个云化与基础设施能力的结合,我相信我们未来可以做得更好,能够让整个应用的开发与发布更加令人享受。​