将服务器应用打包为容器映像的做法迅速风靡了整个技术领域。此外,许多游戏公司还对使用容器提高虚拟机利用率感兴趣,并利用容器提供的独立运行时范式。虽然兴趣浓厚,但很多游戏公司不知道从何入手。我们建议使用容器编排框架 Kubernetes 来部署生产规模的专用游戏服务器。
本教程介绍了一种展开式架构,该架构用于在 Google Kubernetes Engine 上运行分局式实时多人游戏的专用游戏服务器。扩缩管理器进程可根据需要自动启动和停止虚拟机实例。托管实例组自动将计算机配置为 Kubernetes 节点。
本教程中介绍的在线游戏结构经过刻意简化,以便理解和实现。对于需要更高复杂性的情况,我们会适时指出。
目标
使用 Docker 在 Linux 上创建 OpenArena(一种热门的开源专用游戏服务器 (DGS))的容器映像。此容器映像仅将二进制文件和必需的库添加到基本 Linux 映像。
将资源存储在单独的只读永久性磁盘卷上,并在运行时将它们装载到容器中。
使用 Kubernetes 和 Google Cloud Platform API 设置和配置基本调度程序进程,以根据需要启动和关闭节点。
费用
本教程使用 Google Cloud Platform 的以下可计费组件:
GKE
永久性磁盘
您可以使用价格计算器根据您的预计使用情况来估算费用。
准备工作
本教程用于从 Linux 或 macOS 环境运行。
1、在 GCP Console 的项目选择器页面上,选择或创建 GCP 项目。
注意:如果您不打算保留在此过程中创建的资源,请创建新的 GCP 项目,而不要选择现有的 GCP 项目。完成本教程介绍的步骤后,您可以删除所创建的项目,并移除与该项目关联的所有资源。
转到项目选择器页面
2、确保您的 Google Cloud Platform 项目已启用结算功能。 了解如何确认您的项目已启用结算功能。
3、启用Compute EngineAPI。
启用 API
4、安装并初始化 Cloud SDK。
注意:在本教程中,您不能使用 Cloud Shell。必须安装 Cloud SDK。
5、安装 Kubernetes 的命令行界面
6、从 GitHub 克隆教程的代码库:
7、安装 Docker
本教程不以 root 用户的身份运行 Docker 命令,因此,请务必遵循以非 root 用户的身份管理 Docker 的安装后说明。
8、(可选)如果要在本教程结束时测试与游戏服务器的连接,请安装 OpenArena 游戏客户端。运行游戏客户端需要桌面环境。本教程介绍了如何使用 Linux 或 macOS 进行测试。
架构

云游戏基础架构概览页面介绍了许多在线游戏架构中常见的高层级组件。在本教程中,您将实现一项 Kubernetes DGS 集群前端服务和一项扩缩式管理器后端服务。完整的生产游戏基础架构还包括许多其他前端和后端服务,但这些服务不在本教程的探讨范围内。
本教程的设计限制
为生成一个既具说明性又易于扩展的示例,本教程假设具有以下游戏限制:
这是一款对局性实时游戏,具有模拟游戏状态的权威 DGS。
DGS 通过 UDP 与客户端通信。
每个 DGS 进程运行 1 个对局。
所有 DGS 进程生成大致相同的负载。
对局具有最大时长。
DGS 启动时间可以忽略不计,并且无需预热专用游戏服务器进程。
在峰值后缩减时,对局不会因尝试节省费用而过早结束。优先考虑的是避免对玩家体验产生影响。
如果 DGS 进程遇到错误并且无法继续,对局状态将丢失,玩家应使用游戏客户端加入另一对局。
DGS 进程从磁盘加载静态资源,但无需资源的写入访问权限。
这些限制在游戏行业中都有先例,并且代表了真实用例。
准备 Google Cloud Platform (GCP) 工作环境
为更加轻松地运行
命令,您可以设置属性,这样就不必在输入每条命令时都提供有关这些属性的选项。
1、使用
的项目 ID 设置默认项目:
2、使用
的首选地区设置默认的 Compute Engine 地区:
容器化专用游戏服务器
在本教程中,您将使用 OpenArena,该游戏被描述为“基于 GPL idTech3 技术的、由玩家社群制作的死亡竞赛 FPS”。虽然此游戏的技术已诞生超过十五年,但它仍是常见 DGS 模式的良好示例:
从与游戏客户端相同的代码库编译服务器二进制文件。
服务器二进制文件中包含的唯一的数据资源是服务器运行模拟所必需的资源。
游戏服务器容器映像仅将二进制文件和库添加到运行服务器进程所需的基本操作系统容器映像。
从单独的卷装载资源。
这种架构具有诸多好处:加速映像分发、减少更新负载(因为只替换了二进制文件),并减少磁盘空间使用。
创建容器映像
Dockerfile 描述了要构建的映像。本教程的 Dockerfile 在
处的代码库中提供。请从
目录运行 Docker 构建命令以生成容器映像,并将其标记为 OpenArena服务器的 0.8.8 版本:
生成资源磁盘
在大多数游戏中,二进制文件比资源要小几个数量级。因此,应创建仅包含二进制文件的容器映像。资源可以置于永久性磁盘上,并附加到运行 DGS 容器的多个虚拟机实例。这种架构可以节省资金,并且无需将资源分发给所有虚拟机实例。
1、使用
创建一个小型 Compute Engine 虚拟机实例:
2、创建永久性磁盘:
永久性磁盘必须与启动磁盘分开,并且您必须将其配置为在移除虚拟机时保持未删除状态。通过永久性磁盘在 GKE 中使用 Kubernetes persistentVolume 功能效果最佳。根据 Compute Engine,这些永久性磁盘应包含无分区表的单个 ext4 文件系统。
注意:对于某些永久性磁盘类型,以较大磁盘大小预配时的性能更佳。如需了解详情,请参阅有关优化永久性磁盘性能的文档。根据您的引擎读取频率,为获得更好的性能,应选择比仅保存数据所需的磁盘更大的磁盘。
3、将
永久性磁盘附加到
虚拟机实例:
4、设置新磁盘的格式。
a.登录
虚拟机实例并设置磁盘格式。
b.由于下一步中的
命令是一种具有破坏性的命令,因此,请务必确认
磁盘的设备 ID。如果您从一开始就按照本教程进行操作,并且使用的是新项目,则 ID 为
使用
命令对此进行验证,以查看附加的磁盘及其分区:
输出应将具有 1 个分区
的 10 GB 操作系统磁盘
以及没有分区的 50 GB
磁盘显示为设备
:
c.设置 openarena-assets 磁盘的格式:
注意:mkfs.ext4 命令是一种具有破坏性的命令。请务必使用您之前验证过的设备 ID。
5、在
虚拟机实例上安装 OpenArena,并将压缩的资源归档复制到
永久性磁盘。
对于本游戏,资源是位于
目录的
文件。为省去一些工作,下面的一系列命令会在安装之前将资源磁盘装载到此目录,以便安装过程将所有
文件置于磁盘上。请务必使用您之前验证过的设备 ID。
6、退出该实例,然后将其删除:
现在可将该磁盘用作 Kubernetes 中的永久性卷。
如果在游戏开发流水线中实现永久性磁盘,建议您配置构建系统,以创建包含相应目录结构中的所有资源文件的永久性磁盘。为此,您可以使用简单脚本来运行 gcloud 命令,也可以使用适用于所选构建系统的 GCP 特定插件。此外,我们还建议您为该永久性磁盘创建多个副本,并以平衡的方式将虚拟机实例连接到这些副本,以满足额外的吞吐量需求以及管理故障风险。
设置 Kubernetes 集群
Kubernetes 是一个开源社区项目,因此,可将其配置为在大多数环境(包括本地)中运行。
在 Kubernetes Engine 上创建 Kubernetes 集群
本教程使用一个标准 Kubernetes Engine 集群,该集群配有符合 OpenArena 使用模式配置的 n1-highcpu 机器类型。
1、为名为
的游戏创建一个 VPC 网络:
2、为 OpenArena 创建防火墙规则:
注意:本教程将打开 100 个端口,并且创建的每个 DGS 的端口号会自动递增。在生产系统中,您应创建更高级形式的端口管理。
3、使用
创建一个使用
网络的 3 节点集群,每个节点上具有 4 个虚拟 CPU 核心:
4、集群启动后,使用适当的 Kubernetes 身份验证凭据设置本地 shell 以控制新集群:
补充:每个虚拟机有多少个 vCPU?
在生产集群中,您将在每台计算机上运行的 vCPU 数目很大程度上受以下两个因素影响:
您计划运行的并发 DGS pod 的最大数目。 Kubernetes 集群池中允许的节点数存在限制(但是 Kubernetes 项目计划在未来版本中增加此限制数)。例如,如果每个虚拟 CPU (vCPU) 运行 1 个 DGS,则 1000 节点集群的
机器仅提供 2000 个 DGS pod 的容量。相比之下,1000 节点集群的
机器最多允许 32000 个 pod。
您的虚拟机实例粒度。 从集群添加或移除资源的最简单方法:增加集群创建期间所选类型的单个虚拟机实例。因此,如果您希望能够一次性添加或移除少于 32 个 vCPU 的容量,请勿选择 32 个 vCPU 的机器。
补充:为什么要停用自动扩缩和负载平衡?
默认情况下,GKE 使用的托管实例组功能包括虚拟机实例自动扩缩和 HTTP 负载平衡功能。但是,用于创建 Kubernetes 集群的命令通过使用
标志停用了这些功能。
由于 DGS 使用 UDP 而不是 TCP 与客户端通信,因此无需 HTTP 负载平衡。目前,自动调节程序只能根据 CPU 使用情况(这可能是 DGS 负载的一种具有误导性的指标)来扩缩实例组。许多 DGS 用于消耗空闲周期,以优化游戏模拟。
因此,许多游戏开发者实现了可识别 DGS 的自定义扩缩管理器进程,以处理此类工作负载的特定要求。但是,托管实例组仍会提供重要功能 - 其默认 GKE 映像模板包含所有必需的 Kubernetes 软件,并在启动时自动向主实例注册节点。
将容器映像上传到 GCP
Google 在 Container Registry (gcr.io) 中提供了私有 Docker 映像存储空间。
1、选择最接近您的 GEK 集群的 gcr.io 区域(例如
(表示美国)、
(表示欧洲)或
(表示亚洲),具体请参阅此文档),并将区域信息与项目 ID 一起放入一个环境变量中:
2、使用 gcr.io 注册表名称标记容器映像:
3、将容器映像上传到映像代码库:
推送完成后,即可在 GKE 集群中运行容器映像。请记下您的最终容器映像标记,因为您稍后需要将其置于 pod 规范文件中。
在 Kubernetes 中配置资源磁盘
典型的 DGS 不需要对游戏资源进行写访问,因此您可以让每个 DGS pod 装载包含只读资源的相同永久性磁盘。您可以使用 Kubernetes 中的 persistentVolume 和 persistentVolumeClaim 资源来实现此目的。
1、应用
它包含将绑定到您之前创建的资源磁盘的
资源的定义:
2、应用
它包含
资源的定义,该资源允许 pod 装载资源磁盘:
通过运行以下命令确认卷处于
状态:
预期输出:
同样,确认声明处于绑定状态:
预期输出:
配置 DGS pod
由于由 Kubernetes 处理调度和网络任务,并且 DGS 容器的启动和关闭时间可以忽略不计,因此,在本教程中,DGS 实例将按需启动。
每个 DGS 实例的持续时间取决于单个游戏对局的时长,并且游戏对局受制于 OpenArena DGS 服务器配置文件中所指定的时间限制。对局结束后,容器将成功退出。如果玩家想要再玩一次,可以请求加入另一对局。这种设计简化了 pod 生命周期的诸多方面,并为自动扩缩政策(稍后将在扩缩管理器部分中进行介绍)奠定了基础。
虽然此流程与 OpenArena 并不是结合得天衣无缝,但这只是因为本教程既没有更改游戏客户端代码,也没有为其创建分支。在商业化发布的游戏中,加入另一对局的请求会在之前对局结果屏幕和加载时间后进行,对用户不可见。如果代码要求客户端在对局之间连接到新的服务器实例,这并不会导致开发时间增加,因为无论如何,您都必须执行该代码来处理客户端重新连接,以应对不可预见的情况(如网络问题或服务器崩溃)。
为简单起见,本教程假定 GKE 节点采用以下默认网络配置:为每个节点分配一个公共 IP 地址并允许客户端连接。
管理专用游戏服务器进程
在商业化生产的游戏服务器中,对于有助于 DGS 在容器中运行良好的其他所有非 DGS 功能,建议尽量直接集成到 DGS 二进制文件中。
作为一种最佳做法,DGS 应避免直接与 matchmaker 或扩缩管理器通信,而应将其状态向 Kubernetes API 公开。外部进程应从适当的 Kubernetes 端点读取 DGS 状态,而不是直接查询服务器。如需详细了解如何直接访问 Kubernetes API,请参阅 Kubernetes 文档。
补充:为 DGS pod 选择正确的 Kubernetes 资源
乍一看,在具有有限生命周期和定义的成功标准的容器中运行的单个进程似乎是 Kubernetes 作业的一个用例,但事实上并没有必要使用作业。DGS 进程并不需要作业的并行执行功能。它们也不需要通过自动重启(通常是在分局式 DGS 由于某种原因而结束、状态丢失和玩家加入了另一 DGS 时)来保证成功。由于这些因素,对于此用例,安排单独的 Kubernetes pod 更合适。
在生产中,DGS pod 应由 matchmaker 使用 Kubernetes API 直接启动。在本教程中,说明 DGS pod 资源的直观易懂的 YAML 文件包含在
处的教程代码库中。在为专用游戏服务器 pod 创建配置时,请仔细留意卷属性,确保可以在多个 pod 中以只读方式装载资源磁盘。
设置扩缩管理器
扩缩管理器是一种简单进程,可根据当前 DGS 负载扩缩用作 GKE 节点的虚拟机数。扩缩通过一组永久运行的脚本完成,这些脚本会检查运行和请求的 DGS pod 总数,并根据需要调整节点池的大小。脚本打包在包含相应库和 Cloud SDK 的 Docker 容器映像中。您可以使用以下过程创建 Docker 映像并将其推送到 gcr.io。
1、如有必要,将 gcr.io
值和
置于构建和推送脚本的环境变量中。如果您在之前上传容器映像时已经执行过此步骤,则可以跳过此步骤。
2、更改为脚本目录:
3、运行构建脚本以构建所有容器映像,并将这些映像推送到 gcr.io:
4、使用文本编辑器在
处打开 Kubernetes 部署文件。
扩缩管理器脚本用于在 Kubernetes 部署中运行,确保在发生故障时,这些进程会重启。
5、将环境变量更改为部署的值,如下表所列:

6、返回父级目录:
7、将部署添加到 Kubernetes 集群:
如何扩缩节点
扩缩管理器使用 Kubernetes API 查看当前节点的使用情况,以扩缩节点。管理器可根据需要调整 Kubernetes Engine 集群的运行基础虚拟机的托管实例组大小。
DGS pod 的扩缩问题
DGS 扩缩的常见问题包括:
标准 CPU 和内存使用情况指标通常无法捕获足够的信息来驱动游戏服务器实例扩缩。
保留可用的未充分利用的节点的缓冲区至关重要,因为在现有节点上调度优化的 DGS 容器只需几秒钟时间。但是,添加节点可能需要几分钟时间,而等待中的玩家无法接受此延迟时间。
许多自动调节程序无法正常处理 pod 关闭。从正在移除的节点中排空 pod 至关重要。关闭节点通常也是无法接受的,即使该节点仅运行一个对局,也是如此。
虽然本教程提供的是基础脚本,但由于它们设计简单,您可以轻松添加其他逻辑。典型的 DGS 具有易于理解的性能特征,通过将这些特征纳入指标,您可以确定何时添加或移除虚拟机实例。常见扩缩指标包括每个 CPU 的 DGS 实例数(本教程中使用)或可用玩家空档数。
扩充
在本教程中,扩充无需特殊处理。为简单起见,本教程在 pod 的 YAML 文件 (
) 中设置了
和
pod 属性,以便为每个 DGS 预留大约 1 个 vCPU(这对于 OpenArena 来说已足够)。
由于集群是使用
实例系列创建的,该系列具有 600 MB 内存对应于 1 个 vCPU 的固定比率,因此如果每个 vCPU 调度 1 个 DGS pod,则内存应足够。这样一来,您就可以根据集群中的 Pod 数与集群内所有节点中的 CPU 数的比率进行扩充。此比率确定可用的剩余资源,使您可以在值低于阈值时添加更多节点。如果当前向 pod 分配超过 70% 的 vCPU,则本教程将添加节点。
在生产在线游戏后端,建议您准确分析 DGS CPU、内存和网络使用情况,然后适当设置
和
pod 属性。对于许多游戏来说,应该为具有不同使用模式配置(如游戏类型、特定地图或玩家空档数)的不同 DGS 场景创建多个 pod 类型。这些考虑因素超出了本教程的探讨范围。
缩减
与扩充不同,缩减是一个多步过程,也是运行自定义 Kubernetes 感知型 DGS 扩缩管理器的主要原因之一。在本教程中,
自动处理以下步骤:
1、选择要移除的适当节点。由于完全自定义的游戏感知型 Kubernetes 调度程序超出了本教程的探讨范围,因此按照 API 返回节点的顺序来选择这些节点。
2、在 Kubernetes 中将所选节点标记为不可用。这可以防止在节点上启动其他 pod。
3、使用
命令从托管实例组中移除所选节点。这可以防止托管实例组尝试重新创建实例。
脚本单独监控已放弃的未调度的节点,确定是否缺少 DGS pod。节点上的所有对局结束并且 pod 退出后,脚本将关闭虚拟机实例。
扩缩 DGS pod 数
在典型的生产游戏后端中,matchmaker 可控制添加新 DGS 实例的时间。由于 DGS pod 配置为在对局结束时退出(请参阅前面的设计限制),因此无需执行任何显式操作即可缩减 DGS pod 数。如果没有足够的玩家请求进入 matchmaker 系统以生成新的对局,则在对局结束时,DGS pod 会慢慢从 Kubernetes 集群中移除。
测试设置
到目前为止,您已创建了 OpenArena 容器映像并将其推送到 Container Registry,然后启动了 DGS Kubernetes 集群。此外,您还生成了游戏资源磁盘并将其配置为在 Kubernetes 中使用,然后启动了扩缩管理器部署。现在,您可以启动 DGS pod 进行测试。
请求新的 DGS 实例
在典型的生产系统中,当 matchmaker 进程中的对局具有适当玩家时,它将直接使用 Kubernetes API 请求实例。为测试本教程的设置,您可以直接发出实例请求。
1、在文本编辑器中打开
找到指定要运行的容器映像的行。
2、运行
命令,将值更改为匹配
容器映像标记,如本教程前文所述。
3、运行
命令,指定
文件:
4、等待一小段时间,然后确认 pod 的状态:
输出应类似于以下内容:
连接到 DGS
启动 pod 后,您可以通过启动 OpenArena 客户端来验证是否可以连接到 DGS。
从 macOS 或 Linux 桌面:
注意:本教程采用的配置设置将游戏服务器时长限制为几分钟,因此在测试客户端连接之前不要等待太久,否则您可能需要启动另一 pod。
测试扩缩管理器
扩缩管理器可根据 DGS pod 数扩缩 Kubernetes 集群中的虚拟机实例数。因此,测试扩缩管理器需要在一段时间内请求许多个 pod,并检查节点数是否适当扩缩。为查看节点扩缩,服务器配置文件中的对局长度必须具有时间限制。
处的教程服务器配置文件对对局设置了 5 分钟的限制,并且默认情况下,教程 DGS pod 使用该配置文件。要进行测试,请运行以下脚本,该脚本会在 5 分钟内定期添加 DGS pod:
您应该能够通过定期运行
来查看节点数的扩充和缩减。
清理
为避免因本教程中使用的资源而导致您的 Google Cloud Platform 帐号产生费用,请执行以下操作:
删除项目
要避免产生费用,最简单的方法是删除您为本教程创建的项目。
要删除项目,请执行以下操作:
警告:删除项目会产生以下影响:
项目中的所有内容都会被删除。如果您将现有项目用于本教程,则在删除该项目后,还将删除您已在该项目中完成的其他任何工作。
自定义项目 ID 丢失。 创建此项目时,您可能创建了要在将来使用的自定义项目 ID。要保留使用该项目 ID 的网址(例如 appspot.com 网址),请删除项目内的所选资源,而不是删除整个项目。
如果您打算浏览多个教程和快速入门,则重复使用项目可以帮助您避免超出项目配额上限。
1、在 Cloud Console 中,转到管理资源页面。
转到“管理资源”页面
2、在项目列表中,选择要删除的项目,然后点击删除 delete。
3、在对话框中输入项目 ID,然后点击关停以删除项目。
删除 GKE 集群
如果您不想删除整个项目,可运行以下命令来删除 GKE 集群:
删除永久性磁盘
要删除永久性磁盘,请执行以下操作:
1、在 GCP Console 中,转到 Compute Engine 的“磁盘”页面。
转到 Compute Engine 的“磁盘”页面
2、选择要删除的磁盘。
3、点击页面顶部的删除按钮。
后续步骤
本教程介绍了一种极简单的架构,该架构用于在容器中运行专用游戏服务器,以及根据游戏负载自动扩缩 Kubernetes 集群。您可以通过编程提供一些基本客户端支持,以添加多种功能,例如让玩家无缝从一局游戏转换另一局。要添加其他功能(例如允许玩家组建小组并支持服务器之间的小组移动),您可以创建与 matchmaking 服务共存的独立平台服务。然后,您可以使用该服务来组建小组,发送、接受或拒绝小组邀请,以及将几组玩家一起发送到专用游戏服务器实例。
另一常见功能是自定义 Kubernetes 调度程序,它能够以更加智能且以游戏为中心的方式为您的 DGS pod 选择节点。大多数游戏都非常需要将 pod 打包在一起的自定义调度程序,该调度程序使您可以轻松确定在峰值后进行缩减时移除节点应遵循的优先顺序。
有关在 GCP 上运行 DGS 的更多指南:
云游戏基础架构概览
专用游戏服务器迁移指南
在 Google Compute Engine 上设置 Minecraft 服务器
亲自试用其他 Google Cloud Platform 功能。查阅我们的教程。
文章信息
相关推荐
