最近一年使用了容器化部署游戏服务端,效果可圈可点,适当做下总结。
特别说一下,容器化部署是指 使用 k8s/docker 形式部署游戏服,需要在游戏开发过程中做各种必要的适配。
结论
先说结论,说明其中的利弊。
优势:
1. 使用 k8s 容器部署,自动扩缩容,秒级部署
2. 使用容器迭代更新
3. 故障隔离和容错:节点故障自动恢复,流量自动切到正常节点
劣势:
1. 容器重启状态会丢失,不大适合长时间任务,功能开发更复杂
2. 文件热更新实现复杂
3. 故障排查比较困难:容器众多,环境难以保留
4. 成本比较高:k8s 组件消耗资源,通常还要搭配EKS托管
方案实现
1、数据存储
数据存储有几种方案:
1. 挂载网络存储/使用持久化卷: 适合离线数据,比如统计数据、非实时结算数据、重要日志
2. 使用中间件存储: 适合无状态设计,服务本身不记录数据,所有数据存储到中间件
3. 使用定时持久化+优雅退出存储:适合游戏状态维护,比如玩家数据、玩法数据
实际游戏开发中,根据功能业务的不同,组合使用以上的方案。
2、框架设计
不大适合单体设计,准确说,不适用单体的分区分服设计。
容器部署的形式,所有节点都是镜像复制的。玩家将会被打散到各个节点,不容易把玩家限制在某个节点,而且,限定在特定节点,也发挥不出容器部署的优势。
适合什么单体?
可复制单体,但要处理好玩家数据,无状态服务将数据存到中间件,有状态服务则要保证玩家同时只能登录其中一个节点。
但是,容器部署,更适合还是使用分布式设计,节点并行多开,共同分摊流量、计算、内存压力。
分布式的框架设计本身就容易实现容器部署,所不同的是,容器部署的特点是,动态调度、随时消亡。
集群需要重点处理节点突然的增加和减少,做好自适应。
利用 k8s 优雅启动与优雅退出,集群做好自适应。
1. 启动:定义 Readiness Probe,确保服务完全初始化(如加载配置、建立数据库连接、缓存预热)后才被注册到服务发现中接收流量。
2. 终止:Pod 被驱逐或缩容时,需利用 PreStop Hook 执行节点关闭流程:停止流量分配,踢出或迁移玩家,数据落库。设置 terminationGracePeriodSeconds,确保 Pod 有足够长的退出时间,不被 k8s 强杀。
3、发布更新
有很多方案,根据业务不同使用不同方案,简单的话直接使用 ArgoCD 就可以了。我们采用 Jenkins + ArgoCD 组合方案,完成CI/CD任务。
CI阶段 (Jenkins):负责从代码编译、构建 Docker 镜像,并将镜像推到仓库。
CD阶段 (ArgoCD):负责监听Git仓库中 K8s 部署清单的变化,自动同步部署新版本。
优势:分工明确,Jenkins 处理复杂的构建逻辑,ArgoCD提供安全、自动化的部署。
通过 ArgoCD 配置 k8s 集群,每种服务当做一个应用。
4、日志处理
容器日志需要输出到标准输出(stdout/stderr),再由ELK等平台统一收集、存储和查询
5、Pod唯一id
生成每个Pod的唯一id,用于节点通讯,或者辅助uuid生成。id比如从1到1024,怎么实现唯一且同时不重复?
1. 有状态应用,直接使用 StatefulSet 的稳定序号
2. 无状态应用,通过 InitContainer 和 kubectl API 获得
3. 使用 Redis 分配:使用Redis setnx,Pod 内部定时续约,优雅退出时del
写在最后
现在有AI生成内容后,我写博客的动力更低了,大部分内容可以直接通过AI可以生成出来,总结也比较到位。我手动写的反而略显粗糙,点到即止。先写到这里,后续我想到再补充。
