内容简介:我们游戏按照业务逻辑划分,服务器可分为三种类型,前端服务器(客户端直接进行连接的)、后端服务器(只负责处理各种游戏逻辑不提供连接)、任务服务器(各种 cron、job 任务),其中前端服务器按照功能划分为 http 短连接服务器和 socket 长连接服务器,后端服务器按照业务划分 例如 matching 匹配服务器。在部署这些服务器的同时,我需要使用 kubernetes 达到的目标有:
作者介绍:段鹏举,边锋后端开发工程师。
本人一直做业务开发,不曾了解过运维知识,因为要对一个项目的技术部分负责,开发业务的同时还需要思考系统层面的事情,团队人数又少,不得不采用 k8s 这种能达到事半功倍效果的工具。本文是在阿里云 kubernetes 部署游戏业务的实战笔记,不涉及 k8s 原理等深层知识。我学习 k8s 的时间也比较短,如有理解错误的地方,还望海涵。
目标
我们游戏按照业务逻辑划分,服务器可分为三种类型,前端服务器(客户端直接进行连接的)、后端服务器(只负责处理各种游戏逻辑不提供连接)、任务服务器(各种 cron、job 任务),其中前端服务器按照功能划分为 http 短连接服务器和 socket 长连接服务器,后端服务器按照业务划分 例如 matching 匹配服务器。
在部署这些服务器的同时,我需要使用 kubernetes 达到的目标有:
-
对于每种类型的服务器,需要同时存在若干个版本
-
对于无状态服务器如 http、cron 可以比较方便的更新、回滚
-
对于有状态服务器如 socket、matching 可以业务无间断的进行更新、回滚,用户不会掉线、无感知
-
可以进行灰度发布
-
当服务器的负载变化时,能够自动伸缩服务器数量
-
当服务器异常宕机时,能够自我修复
准备 Docker 镜像
-
使用阿里云容器镜像服务准备好 docker 远程仓库
-
在应用(服务器代码)准备好之后,使用 Docker 构建镜像,并打上版本号,Push 到远程仓库(这一步骤可以通过 Jekins 自动完成,后续实践的时候会更新文档,目前就以手工进行)
部署应用
类似于 web 前端框架中的命令式(jquery)与声明式(react),我对 k8s 有一种理解与之类似:我们只需要通过配置文件“告诉”k8s 我们想要的最终结果就行,中间的过程无须再关心,k8s 会以各种机制保证这一结果
1.因为使用的 Docker 远程仓库是私有仓库,部署应用时就需要添加 imagePullSecrets,首先使用 kubectl 在 default 命名空间里创建 secret,如需指定命名空间添加 -n 参数,后面命令类似
2.根据服务器的特点创建部署 yaml 文件
-
http 无状态应用
创建对应的 service
创建对应的 ingress(路由)对外提供服务
此时已经可以通过域名进行访问了,这就是我们想要的“最终状态”,而具体实现细节以及如何维持这个状态不变,我们无需再关心
为何不直接使用 Service 对外提供服务?
其实我们只需要把 Service 的类型改成 LoadBlancer,阿里云(其他云服务商类似)会给 Service 添加一个监听的 nodePort,再自动创建一个负载均衡,通过 tcp 转发到 Service 的 nodePort 上(这地方阿里云有个 bug 每次更新 Service 它都会把转发类型改成 tcp),可想而知,当我们的 Service 越来越多时,nodePort 的管理成本也就越来越高, k8s 提供了另外一个资源解决这种问题,就是 Ingress
Ingress 工作机制
Ingress 其实就是从 kuberenets 集群外部访问集群的一个入口,将外部的请求根据配置的规则转发到集群内不同的 Service 上,其实就相当于 nginx、haproxy 等负载均衡代理服务器,我们直接使用 Nginx 也可以达到一样的目的,只是 nginx 这种方式当添加、移除 Service 时动态刷新会比较麻烦一点,Ingress 相当于都给你做好了,不需要再次实现一遍,Ingress 默认使用的 Controller 就是 nginx。
Ingress controller 可以理解为一个监听器,通过不断地与 kube-apiserver 打交道,实时的感知后端 service、pod 的变化,当得到这些变化信息后,Ingress controller 再结合 Ingress 的配置,更新反向代理负载均衡器,达到服务发现的作用。
配置 Ingress
可以通过 annotations 注解的方式告诉 Ingress 你的配置,例如:如果你使用的是 Nginx-Ingress-Controller,可以通过 nginx.ingress.kubernetes.io/cors-allow-origin:*
来配置 cors,和配置 Nginx 几乎是一样的,只是名称不一样而已。
所有的 Nginx-Ingress-Controller 的注解可以在这里查询 传送门
可以进入 nginx-Ingress-controller 的 pod 中,添加一些注解,更新,会看到 nginx 重新生成了配置,并“重新启动”,对比注解和 nginx.conf 很快就能理解 Ingress
Ingress 灰度发布
可以通过添加注解 nginx.ingress.kubernetes.io/service-match:'test-svc: header("Version", "1.0.0000")'
,来进行灰度发布,比如匹配 request headers中Version=1.0.0000
的流量转发到 test-svc,可以匹配 header、query、cookie,同时还可以配置权重等,例如修复问题时只把 10%的流量切进来,待问题验证得到解决后再设置 100。
我们每次游戏前端发布版本都会在 header 中添加一个 Version 参数,我设置灰度发布之后就可以把特定前端版本的流量自由的切到某个特定的服务中,比较灵活。
滚动更新
当不需要灰度发布时,仅仅需要对某个 Service 的 pod 进行更新,只需要更改上文 Deployment 中镜像版本即可,当 k8s 检测到 template 字段更改时,会根据设置的 rollingUpdate strategy 策略进行滚动更新,对于 http 这种无状态的服务,也能达到业务不间断更新
-
长连接 有状态应用
无状态: 该服务运行的实例不会在本地存储需要持久化的数据,并且多个实例对于同一个请求响应的结果是完全一致的
有状态:和上面的概念是对立的了,该服务运行的实例需要在本地存储持久化数据,比如 socket 长连接
Service 和 Ingress 与无状态 http 应用基本一致,参照上文部署即可。全部部署完成后,观察 k8s 后台可以看到,有 name 分别为 connector-prod-v100000-0、connector-prod-v100000-1、connector-prod-v100000-2 的三个 pod 正在运行,后面的 -n 是由于资源类型设置为 StatefulSet k8s 自动加上的以作区分。
在容器中获取 pod 信息
一般来说对于 StatefulSet 我们可能会在容器内知道这个 pod 的 name,这时候就可以采用类似于上面的方法,通过 valueFrom
fieldPath:metadata.name
把 pod name 信息注入到容器的环境变量中,这种特殊的语法是 Downward API,帮助我们获取许多 pod 的信息,可参照传送门进行学习
滚动更新
对于 StatefulSet 默认的滚动更新策略是 OnDelete, 也就是当这个 pod 被删除后,k8s 再次创建时会更新镜像。即使我们改变这个策略,那么可以直接对齐进行更新吗?对于大多数 StatefulSet 是不太合适的(比如 pod 上面有用户的长连接 如果直接更新用户会断线 影响体验),或者说对于 StatefulSet 的滚动更新一直都是个很复杂的话题,所以如果要更新,推荐使用 灰度发布 。
灰度发布的过程与上文 http 一致,对于我们的业务来说,用户的下一次连接会切到指定的版本上
-
matching 后端有状态应用
因为后端服务器不需要外界的访问,所以创建一个 StatefulSet 启动后端微服务就可以,启动后会监听消息队列进行处理并返回数据
RPC 设计
可以看到,我把许多 POD 信息注入到了容器中
-
环境变量 SERVER_ID 是名称-环境-版本号-序号命名的可以选择某一个服务器;
-
环境变量 SERVER_TYPE 可以选择某一类型的服务器;
-
环境变量 VERSION 可以选择某一版本的服务器。
因此就可以通过类似于标签选择的方式发送和接受消息队列,例如:
在代码中获取环境变量
如果想匹配某一个服务器:
如果想匹配某一类型的所有服务器:
在其他应用内发送 rpc(如在 http 应用内调用 matching 应用),按照上面的标签格式发送消息即可:
长连接客户端发送 rpc
和前端同学约定,在 socket 请求中按照 event 字段分为 4 个类型:Push、Notify、Request、Response
Request-Response
客户端主动发起,要求有回应,类似于 http:
Notify-Push
客户端主动发起,不要求回应
在服务器需要回应的地方发送 PUSH 事件
对于处理消息会发送到哪个后端服务器,写一个路由函数发起 rpc 即可
"灰度发布"
同理,有状态后端服务器也不适用滚动更新,因为会丢失业务信息。因为后端服务器外界不可访问,也不能用 Ingress 路由灰度发布的方式来更新,怎么办呢?
其实按照上面的 rpc 设计已经解决了这个问题,例如现在匹配服是 1.0.0000 版本,如果想要发布 1.0.0001 版本,只需要部署一个 matching-v100001 的应用,客户端在配置文件里把 Version 改成 1.0.0001,那么下一次请求就会匹配到 matching-v100001 的应用上,这样可以根据客户端配置随时切换服务器版本号,达到了灰度发布的效果。
-
cron 定时任务
部署之后定时器就开始运行了,非常简单。通过 spec.successfulJobsHistoryLimit
和 spec.failedJobsHistoryLimit
,表示历史限制,是可选的字段。它们指定了可以保留多少完成和失败的 Job,默认没有限制,所有成功和失败的 Job 都会被保留。然而,当运行一个 Cron Job 时,Job 可以很快就堆积很多,所以一般推荐设置这两个字段的值。如果设置限制的值为 0,那么相关类型的 Job 完成后将不会被保留。
更新
直接更改镜像版本号就可以了,下次运行的时候会以新的镜像版本运行
结束
至此,基本的游戏业务框架已经搭建完成,最初的目标都达成了。下一期更新 kubernetes 学习笔记 (四):自动化部署 k8s 实战
扫描下面的二维码(或微信搜索 k8s技术圈
)关注我们的微信公众帐号,在微信公众帐号中回复 加群 即可加入到我们的 kubernetes 讨论群里面共同学习。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- MongoDB Aggregation 业务场景实战
- Webpack 5 新特性业务落地实战
- 系统架构系列 (三):业务架构实战上篇
- 系统架构系列(四):业务架构实战下篇
- 迁移到 MySQL 的业务架构演进实战
- Spring Boot 实战之业务状态机
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Python编程初学者指南
[美]Michael Dawson / 王金兰 / 人民邮电出版社 / 2014-10-1
Python是一种解释型、面向对象、动态数据类型的高级程序设计语言。Python可以用于很多的领域,从科学计算到游戏开发。 《Python编程初学者指南》尝试以轻松有趣的方式来帮助初学者掌握Python语言和编程技能。《Python编程初学者指南》共12章,每一章都会用一个完整的游戏来演示其中的关键知识点,并通过编写好玩的小软件这种方式来学习编程,引发读者的兴趣,降低学习的难度。每章最后都会......一起来看看 《Python编程初学者指南》 这本书的介绍吧!