摘要
Kubernetes,简称K8s,是Google开发的,目前最为流行的容器编排和管理引擎,它支持自动化部署、大规模可伸缩、应用容器化管理。K8s的特性使得它适用于架构复杂且要求高的服务,也就是游戏架构的搭建。
由于Cloud Ace为谷歌云代理公司,所以我们本次分享的实操是在谷歌开发的Google Kubernetes Engines上运行的,GKE是谷歌基于K8s所开发的Kubernetes管理平台,主要在谷歌云平台上运行。
Kubernetes与微服务架构
随着应用的发展,程序变得越来越复杂,传统一体化架构的服务会造成巨大的不便,比如说:新增功能与测试放在一起,使得程序十分复杂;开发和利用新语言和新框架的效率低;安全性低,所有的模块构建在一个process里,一旦出现bug可能牵一发而动全身。
而微服务 (microservices) 架构正好解决了这样的问题,将每一个具有商业逻辑的服务独立出来,例如不再将所有资料都写入同一个资料库,而是每个单独的服务都有一个最适合自己本身结构的资料库。好处是让每个服务都可以用最适合自己的语言、资料库来开发。在实操时,每一个商业功能/服务都可能是一台 VM 或者一个容器。
微服务架构的出现给了游戏一个新的选择,下图的两个架构就是游戏使用的架构,都是通过不同的功能模块来划分的,按功能模块划分不同的服务,前端通过HAProxy来代理用户请求,后端服务可以根据负载来实现扩缩容。在服务发现模块中,通过registrator来监视容器的启动和停止,根据容器暴露的端口和环境变量自动注册服务,后端存储使用了consul,结合consul-template来发现服务的变化时,可以更新业务配置,并重载。
K8s就是微服务架构
前面提到K8s适合微服务架构,为什么呢?
因为k8s的架构与微服务十分相似,在某些功能上是一致的,第一就是k8s的master和node,可以实现不同功能不同服务器之间的隔离;第二是k8s上面提供了API server,基本上可以等同于网关;第三是k8s的服务编排性能好,可以实现弹性伸缩;第四点是k8s的configmap基本上可以作为配置中心;第五是k8s可以通过在node上部署agent来实现日志的收集和监控。
Kubernetes 将数个容器组合起来成一个服务(Service,注:Service 是 K8S 的专有名词,下面会介绍),Kubernetes 也提供了良好的服务发现(Service discovery)机制,让每个服务彼此可以通信。最重要的是 K8S 强大的编程可以自动扩展服务,甚至还可以对大规模的容器作滚动更新 (Rolling update) 以及回滚机制 (Rolling back/Undo),更可以整合 CI/CD 等 DevOps 的工具。
K8s在游戏架构搭建上的优势
基于前两者,我们总结出来K8s在游戏架构搭建上的优势为:
- 定制的网络与调度方案,为游戏容器的运行提供基础环境;
- 域名服务与负载均衡,解决游戏高可用、弹性伸缩问题;
- 通过性能数据、日志的收集、统计分析,及时发现程序问题与性能瓶颈,保证游戏容器稳定、可持续性运行;
- 基于image的发布扩容,使得游戏部署流程更加标准化以及高效。
Google Kubernetes Engine(GKE)游戏架构搭建实操
我们的实操参考的是谷歌的技术文档来搭建的。
游戏:Open Arena(雷神之锤)—开源游戏
游戏架构如下图,今天我们只讲关于dedicated game server这一部分。
游戏环境设定如下:
游戏设定1:
游戏设定2:
为什么这么设置呢?
第一是处于占用资源上考虑,如果是地图比较大的游戏,打开地图就会需要比较长的时间去载入,同样的,你时间越长需要载入的资料越多,占用越多的资源,因此大部分游戏都会定义时间限制,因为一旦有玩家在玩,主机就无法关闭,造成大量的资源浪费。
第二点是出于成本控制上设置的,因为线上游戏需要弹性,高峰期和非高峰期的人数差异较大,因此需要尽可能的match更多的玩家进来,把玩家集中在一个服务器上玩,这样对vm的需求会比较少,有利于成本控制。
第三点是考虑到游戏技术上的难度,如果说玩家出现问题掉线了,那么就直接重开游戏,不要恢复它的状态,因为恢复状态对技术要求非常高。
GKE搭建后台示例如下:
首先我们先从右上角开始,右上角就是一个基本的游戏的组成,DGS也就是delicate game server ,它是一个open source的套件,可以在GKE上运行。
这里涉及到两个VM,第一个VM用来搭建image,第二个VM用于加载数据/资料片,也就是Asset Disk,我们把Asset Disk挂起来后,VM2我们就直接删掉,VM2是不会留下来的,我们只要留下Disk就可以,之后一旦GKE的class开起来,也就是把gke-dgs image部署出来,部署出来之后,这边就他会去把资料片挂起来。
在K8s上的话,有个点需要注意,如果pod想用到disk里面的资料,那么disk需要做成PV,也就是群集中的资源。PVC是对这些资源的请求,只有用户请求后发送了PVC才能访问存储的资料片。
一轮游戏一个回合,能够允许的玩家是有一定限制的,如果说node上有两个pod,两个pod能够允许20个人玩,那么当玩家数量超过了20个人的时候,就需要系统及时监控到这个情况,开新的pod,这里就涉及到了scaling manager。
Scaling manager翻译过来叫缩放管理器,它其实就是不断的在监控整个class里的每一个node的状态。在k8s里面有个功能叫HPA,Horizontal Pod Autoscaler,HPA可以实现 Pod自动弹性伸缩,HPA是怎么实现自动弹性伸缩的呢?是通过对Pod中运行的容器各项指标(CPU占用、内存占用、网络请求量)的检测,实现对Pod实例个数的动态新增和减少。
但是这里会产生一个问题,比如一个游戏回合,因为会设定游戏时间,因此可能玩家都退出了,但是游戏可能还在进行。HPA这里检测到的依然是游戏占用了很多资源,所以会认定pod不能关掉,但是玩家其实都退出了。这里涉及到了一些底层的监控和运算公式在里面,相对比较复杂。所以在实际的操作过程中,工程师需要去设置监控和弹性缩放的逻辑和算法。
游戏服务器经常会碰到的问题就是如何实现自动扩展和缩放,这里我们总结了一下:
Cordon指令是什么意思呢?就是说pod在缩放的时候,如果确定某个node的玩家都退出了,要把 node撤掉的时候,要标记为不可调度。因为GKE的规则是,只要今天有玩家进来玩,scaling manager就会自动分配pod给他。如果我们想删减node,可是scaling manager发现 node的上面没有任何的pod在跑,它就会自动在这个我们想删减的node上面开pod给玩家,就会发生冲突,因此如果我们要删除node,需要把它标记为不可调度。
当这两个程序如果之间没有做好沟通的话,一个在减少pod,一个在部署pod,两个就会互相冲突,要想办法让另外一个程序知道说这个Pod是不可调度的。
第一点就是abandon instance,按照谷歌云的instance group原先的规定,设定了node是20个,那么当我们缩减掉一个node的时候,instance group为了确保node数量满足20个,会自动增加新的node,这里也是冲突的,我们缩减一个node,它自动增加一个新的node,因此我们就需要通过abandon instance指令来缩减node。abandon instance命令下去之后, node会直接变少,不会增长。
以上就是我们本次分享的内容,这里附上qwiklab的链接,有需要的可以在qwiklab上进行游戏架构搭建的实操。
11月4日Cloud Ace工程师将分享关于谷歌推出的Cloud Spanner——全球分布且强一致性的企业级数据库服务,欢迎各位报名参加。