讲师介绍网易游戏团队负责人林祥鑫。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服务来运行一个游戏集群。游戏服务本身可能有指挥官、游戏、世界、大门这样的进程。对于一个游戏集群,我们可以将其拆解成相对独立的个体。在这个阶段,我们需要完成这些个体个体的定义。2)资源可以定义以MongoDB为例,我们声明它需要使用多少CPU,需要什么类型和大小的磁盘,部署在哪里,可能的分片数量,版本。这样,我们就将资源的定义固定下来了。以后大家对资源的需求都会这样申明,其他内容也一样。你接触过k8s、Docker等声明式语言。制定一个声明式逻辑并不难,但其背后的逻辑是整个基础设施的可编程性。那么我们整个基础设施的可编程性在哪里呢?得益于网易游戏这几年的云化和SaaS发展,我们内部有一套比较成熟的基础设施体系,同时可以以API的形式提供服务,给我们提供了很大的便利。无论是云网络、容器,还是MongoDB、MySQL、Etcd等服务,我们都实现了云化或者SaaS化,所以我们可以更好的对接这部分内容。看似所有的定义都不难,但背后的整个可编程能力才是重点。3)架构是可以定义的有了这些个体,随意组合在一起是无法形成架构的。从上面的架构我们可以看出,服务之间会相互调用,底层资源之间可能存在一些固定的调用关系。上图显示了我们实现的一个简单的游戏架构。从中我们可以发现,整个游戏架构包括很多东西,比如基础环境、项目配置、可以对外开放的端口范围、网络资源的使用和容量等。也是我们在架构定义中需要思考和完成的部分。这背后是我们对一个应用架构模型的定义。当你使用K8S、Docker、Compose的定义时,你会发现它们是不一样的。造成这种差异的原因是它们的模型定义不同,也就是它们的模型框架不同。所以,为了适应我们内部的系统,我们也定义了一套应用声明模型。下图显示了架构部分。4)运维可定义。对于游戏来说,在运维工作中会出现一些简单的web服务不会遇到的问题。比如有些游戏会切换服务器,这涉及到切换网络的动作,我们其实可以在内部进行克制。一般来说,切换网络包括几个动作。一是开放所有公网访问,然后开放玩家连接,二是只开放办公网络。QA可以连接到游戏服务器以验证当前版本。在关闭状态下,QA和玩家都无法访问它。但是历史经验告诉我们,这看似简单的几个状态之间的转换,但是在这些状态的实现背后可能有一些没有被注意到的点。比如我们要求在限制状态下禁止玩家连接,但是可能是因为打开状态切换到限制状态,此时外部播放器的连接状态并没有断开。这方面我们踩坑了,就是我们以为进入了极限状态,但实际上这个时候玩家还在玩。对于一些没有注意到这种情况的人员,如果使用其他的方法,比如上面提到的命令式方法,他可能会调用API来切换状态,保证新玩家无法进入,但是原本的外部玩家连接后可以继续播放,可能会导致失败。但是如果我们把这些东西以声明的方式收敛起来,定义上面说的运维策略,比如进入极限状态,肯定会清除剩余的连接流量,这就实现了我们运维操作的一致性。我们也做到了这一点。游戏还有一个特别的地方,就是有状态的,导致一个进程启动多个端口。除了为玩家提供服务的端口外,还需要额外的端口来调试代码和运行操作。如果像以前那样手动规划端口,其实很难保证一致性。但是如果我们把它的面向端口的分配定义为可声明的运维操作,其实很简单,就是声明周围有哪些端口,可以打开多少,通过系统自身的实现,这些端口可以分配成功。包括端口是否连续都可以在这样的运维策略中实现。以上就是上一篇文章中提到的可定义的应用、可定义的架构、可定义的运维。我们通过上面的方法逐渐实现了,但是我们真的定义一个应用就够了吗?我们大约在一年半前实现了第一个版本,定义的布局文件大约有1000行。大家都知道,如果一个代码文件行数太多,维护起来就很困难,而且一改就容易出问题。当你面对1000多行的排列时,你可能会崩溃,所以这个版本无法继续。接下来,我们进行了一些参考和研究。例如,AWS有一些原则供用户使用。这里我们主要关注一点。用户应该考虑架构,而不是基础设施。我们原版1000多行,内容很多,比较复杂。我们进一步了解到,阿里和微软也在推动OAM应用开发模式。关键点之一是区分用户和分离关注点。另外,他们的运维能力是可以模块化管理的,这也符合我们的目标,我们如何实现这个东西?我们可以借鉴上述模型,但不能应用。这与我们一些内部基础设施的状态有关。在此基础上,我们对模型进行了扩展。首先是环境,还有Stack和globaltrait。展开后,我们游戏的基本架构大致如上图右侧所示。我们的应用实现流程如上图所示,其中最复杂的是OAM解释器的逻辑和整个provider的执行逻辑。这里我们将根据上一篇文章中的架构定义来分析整个编排模型,然后形成一个工作计划供后面执行。之前定义的所有组件的执行过程都会在其中体现出来,最终整个业务流程的交付都会通过这个有向无环图来实现。这种关注点分离给我们带来了什么?如果我们在Istio微服务的路由转发需求下,想要了解之前那些纯粹的概念,我们需要知道如何配置gateway、VS、destinationrule。这部分内容对于大部分同学来说比较难,但是如果我们把这些内容封装抽象成一个基本的路由定义就容易多了。还有一个更复杂的问题就是运维能力如何管理,不同的环境有不同的要求。在研究环境中,我们可以直接连接。我们已经开放了Gate端口,以便客户端可以直接连接。此时不需要配置任何端口转发策略。但对于某款游戏的在线环境,可能需要集群独享一个EIP,通过DNAT访问。有些模式可能更特殊。比如在滚动服务器机制下,如果我们的量级是10000个服务,IP资源肯定不够用。所以在这种情况下,我们需要实现多集群共享EIP。以上只是运维能力的不同,其实Gate的管理是比较一致的。通过这样的能力管控,我们可以明确是否需要EIP,EIP的管理方式和分配方式是什么样的,从而定义运维能力,并允许在这样的配置下进行管理。最后,我们汇总了这些内容。过去,信息围绕着人,而现在我们通过一种安排来驱动所有这些内容。因为所有的信息都是独立的,所以和我们整个内部的运维配置数据系统(CMDB)很好的链接在一起。其中最有价值的是关系数据。游戏组交付后,就形成了这样一个业务拓扑,但是放到整个架构中只是其中的一小部分。这个时候,我们可以做很多事情。比如我们可以通过这种方式明确我是否连接到外部依赖的MongoDB,某个主机出现数组故障会不会影响我,以及一些告警收敛等。我们可以利用这一整套机制实现了。目前我们已经实现了十几二十个场景。3、目前的成果接下来,我们将分享目前的成果。这个机制我们用了快一年了,目前的重点项目正在逐步移交。我们可以适配各种游戏的架构和微服务。根据最保守的估计,我们还能够将交付效率提高10倍。比如以前一个多小时,现在我们控制在5分钟、3分钟以内。整体使用规模也在不断扩大,如今已经达到了千人级别。上图提到的每日发布操作频率也证明了这个机制一直在使用。4.未来展望上面提到的很多内容都离不开业务架构、应用模型、基础设施和代码这三个核心因素。对于我们来说,游戏引擎定义了业务架构。整个游戏行业在发展,引擎本身的能力也在发展。引擎开发好之后,会影响到业务架构。比如我们在做一些游戏微服务的工作。微服务的结果是架构的调整或改变。架构的改变需要在应用模型上进一步扩展,但是整个应用模型离不开底层的基础设施,依赖于基础设施的可编程性,所以这三个部分是密不可分的。这就决定了我们各级人员必须齐心协力解决问题。未来我们会继续围绕这方面进行开发,从而带动我们整个游戏的发展。该平台的发布是我们对基于OAM和游戏云应用管理交付场景的实践。在这个过程中,我们也迭代了一套方法来定义游戏的模型架构,目前还在持续使用中。整个游戏的运维有两大难点,一是状态,二是复杂的运维逻辑。有了整个云化和基础设施能力的结合,相信未来我们可以做得更好,让整个应用的开发和发布变得更加愉快。
