diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/current/introduction.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/current/introduction.md index 9585c96b85..0b2e307bf0 100644 --- a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/current/introduction.md +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/current/introduction.md @@ -51,8 +51,8 @@ OpenKruiseGame(OKG)具有如下核心能力: - + diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/current/user-manuals/crd-field-description.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/current/user-manuals/crd-field-description.md index 54400305c8..a0d37137b4 100644 --- a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/current/user-manuals/crd-field-description.md +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/current/user-manuals/crd-field-description.md @@ -31,6 +31,9 @@ type GameServerSetSpec struct { // 游戏服接入层网络设置 Network *Network `json:"network,omitempty"` + + // 用户自定义设置生命周期钩子 + Lifecycle *appspub.Lifecycle `json:"lifecycle,omitempty"` } ``` @@ -149,6 +152,10 @@ type ServiceQualityAction struct { // 动作为更改GameServerSpec中的字段 GameServerSpec `json:",inline"` + + // 动作为更改GameServer的Annotations和Labels + Annotations map[string]string `json:"annotations,omitempty"` + Labels map[string]string `json:"labels,omitempty"` } ``` @@ -174,6 +181,24 @@ type KVParams struct { } ``` +#### Lifecycle + +``` +// Lifecycle contains the hooks for Pod lifecycle. +type Lifecycle struct { + // 设置在pod删除前卡点 + PreDelete *LifecycleHook `json:"preDelete,omitempty"` + // 设置在pod原地升级前卡点 + InPlaceUpdate *LifecycleHook `json:"inPlaceUpdate,omitempty"` +} + +type LifecycleHook struct { + LabelsHandler map[string]string `json:"labelsHandler,omitempty"` + FinalizersHandler []string `json:"finalizersHandler,omitempty"` + MarkPodNotReady bool `json:"markPodNotReady,omitempty"` +} +``` + ### GameServerSetStatus ``` diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/current/user-manuals/deploy-gameservers.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/current/user-manuals/deploy-gameservers.md index 380c206514..f989b7d05d 100644 --- a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/current/user-manuals/deploy-gameservers.md +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/current/user-manuals/deploy-gameservers.md @@ -172,11 +172,11 @@ PING minecraft-2.minecraft.default.svc.cluster.local (172.16.0.12): 56 data byte 可以发现,accessor访问minecraft-2成功,DNS成功解析到对应的内网IP地址。在这里的DNS访问规则如下:{pod-name}.{gss-name}.{namespace-name}.svc.cluster.local -## GameServer 与 Pod 注释同步 +## GameServer 与 Pod 标签/注释同步 -如上所述,通过 DownwardAPI 可以将 pod annotation的信息下沉至容器中。我们有时希望将 GameServer 的 annotation 可以同步到 Pod 上,以完成GameServer元数据信息的下沉动作。 +如上所述,通过 DownwardAPI 可以将 pod label/annotation的信息下沉至容器中。我们有时希望将 GameServer 的 label/annotation 可以同步到 Pod 上,以完成GameServer元数据信息的下沉动作。 -OKG 支持以 "gs-sync/" 开头的 annotation 从 GameServer 同步到 Pod 之上,如下所示: +OKG 支持以 "gs-sync/" 开头的 label/annotation 从 GameServer 同步到 Pod 之上,如下所示: ```bash kubectl patch gs minecraft-0 --type='merge' -p '{"metadata":{"annotations":{"gs-sync/test-key":"some-value"}}}' diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/current/user-manuals/lifecycle.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/current/user-manuals/lifecycle.md new file mode 100644 index 0000000000..865b7259ed --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/current/user-manuals/lifecycle.md @@ -0,0 +1,116 @@ +# 自定义生命周期管理 + +游戏服务器强状态的关键特性使它们对于优雅的下线操作有很高的需求。 +一个游戏服务器通常需要等待数据被完全持久化到磁盘上并确保安全后,才能进行彻底的移除。 +虽然Kubernetes原生提供了preStop钩子,允许容器在即将关闭前执行特定操作,但存在一个局限性:一旦超出了预设的时间限制,容器将不得不被强制终止,不管数据处理是否完成。在某些情况下,这种方法缺乏真正的优雅性。 +我们需要一个更灵活的机制来确保游戏服务器能够在保护了所有关键状态的前提下平滑地退出。 + +OpenKruise 引入了 Lifecycle Hook 功能,为游戏服务器提供了在关键生命周期节点上的精确控制和等待机制。 +这使得服务器能失在满足特定条件后,方才执行真正的删除或更新操作。 +通过提供可配置的 Lifecycle 字段,并结合自定义服务质量的能力,OKG 能够确保游戏服务器的下线过程既优雅又可靠。 +借助这一进阶特性,维护者可以确保所有必要的数据持久化和内部状态同步在安全无误地完成后,服务器才会被平稳地移除或更新。 + +## 使用示例 + +```yaml +apiVersion: game.kruise.io/v1alpha1 +kind: GameServerSet +metadata: + name: minecraft + namespace: default +spec: + replicas: 3 + lifecycle: + preDelete: + labelsHandler: + gs-sync/delete-block: "true" + gameServerTemplate: + metadata: + labels: + gs-sync/delete-block: "true" + spec: + containers: + - image: registry.cn-beijing.aliyuncs.com/chrisliu95/minecraft-demo:probe-v0 + name: minecraft + volumeMounts: + - name: gsState + mountPath: /etc/gsinfo + volumes: + - name: gsinfo + downwardAPI: + items: + - path: "state" + fieldRef: + fieldPath: metadata.labels['game.kruise.io/gs-state'] + serviceQualities: + - name: healthy + containerName: minecraft + permanent: false + exec: + command: ["bash", "./probe.sh"] + serviceQualityAction: + - state: true + result: done + labels: + gs-sync/delete-block: "false" + - state: true + result: WaitToBeDeleted + opsState: WaitToBeDeleted + - state: false + opsState: None +``` + + +对应的脚本如下。该脚本做了以下动作: + +- 从 /etc/gsinfo/state 中拿到当前gs的状态,并判断其是否为“PreDelete” + - 若是PreDelete,则说明当前gs应处于下线阶段。判断数据落盘是否完成(这个示例中通过判断文件以文件存在表示数据落盘完成) + - 若数据落盘未完成,则执行落盘动作(这个示例是创建一个文件) + - 若数据落盘完成,则输出“done”,并以1退出。 + - 若不是PreDelete,则说明该gs没有未进入下线阶段。以游戏服人数判断当前是否应该下线。 + - 若游戏服人数等于0,则输出“WaitToBeDeleted”,以1退出。 + - 若游戏服人数不为0,则以0退出。 + +``` +#!/bin/bash + +file_path="/etc/gsinfo/state" +data_flushed_file="/etc/gsinfo/data_flushed" + +if [[ ! -f "$file_path" ]]; then + exit 0 +fi + +state_content=$(cat "$file_path") + +if [[ "$state_content" == "PreDelete" ]]; then + if [[ -f "$data_flushed_file" ]]; then + echo "done" + exit 1 + else + touch "$data_flushed_file" + echo "WaitToBeDeleted" + exit 1 + fi +else + people_count_file="/etc/gsinfo/people_count" + + people_count=$(cat "$people_count_file") + + if [[ "$people_count" -eq 0 ]]; then + echo "WaitToBeDeleted" + exit 1 + else + exit 0 + fi +fi +``` + +![grace-deletion.png](/img/kruisegame/user-manuals/gs-lifecycle-delete.png) + +优雅下线的过程如下: +1. 游戏服正常运行,玩家数量不为0 +2. 当玩家数量为0,通过自定义服务质量设置opsState为WaitToBeDeleted +3. 通过自动缩容策略,OKG将该GameServer删除。由于配置了lifecycle hook,delete-block 标签为 true,gs不会真正被删除,而进入PreDelete状态,并通过自定义服务质量触发数据落盘过程。 +4. 当数据完成落盘,通过自定义服质量将delete-block标签设为false,卡点解除。 +5. 卡点解除后,PreDelete阶段将进入Delete阶段。gs真正被删除。 \ No newline at end of file diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/current/user-manuals/network.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/current/user-manuals/network.md index 80f569f1c0..2cdcde6499 100644 --- a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/current/user-manuals/network.md +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/current/user-manuals/network.md @@ -19,6 +19,7 @@ OKG 会集成不同云提供商的不同网络插件,用户可通过GameServer - AlibabaCloud-SLB-SharedPort - AlibabaCloud-NLB-SharedPort - Volcengine-CLB +- AmazonWebServices-NLB --- ### Kubernetes-HostPort @@ -535,6 +536,72 @@ AllowNotReadyContainers - 格式:{containerName_0},{containerName_1},... 例如:sidecar - 是否支持变更:在原地升级过程中不可变更。 +LBHealthCheckSwitch + +- 含义:是否开启健康检查 +- 格式:“on”代表开启,“off”代表关闭。默认为on +- 是否支持变更:支持 + +LBHealthCheckFlag + +- 含义:是否开启http类型健康检查 +- 格式:“on”代表开启,“off”代表关闭。默认为off +- 是否支持变更:支持 + +LBHealthCheckType + +- 含义:健康检查协议 +- 格式:填写 “tcp” 或者 “http”,默认为tcp +- 是否支持变更:支持 + +LBHealthCheckConnectTimeout + +- 含义:健康检查响应的最大超时时间。 +- 格式:单位:秒。取值范围[1, 300]。默认值为“5” +- 是否支持变更:支持 + +LBHealthyThreshold + +- 含义:健康检查连续成功多少次后,将服务器的健康检查状态由失败判定为成功。 +- 格式:取值范围[2, 10]。默认值为“2” +- 是否支持变更:支持 + +LBUnhealthyThreshold + +- 含义:健康检查连续失败多少次后,将服务器的健康检查状态由成功判定为失败。 +- 格式:取值范围[2, 10]。默认值为“2” +- 是否支持变更:支持 + +LBHealthCheckInterval + +- 含义:健康检查的时间间隔。 +- 格式:单位:秒。取值范围[1, 50]。默认值为“10” +- 是否支持变更:支持 + +LBHealthCheckProtocolPort + +- 含义:http类型健康检查的协议及端口。 +- 格式:多个值之间用英文半角逗号(,)分隔。如https:443,http:80 +- 是否支持变更:支持 + +LBHealthCheckUri + +- 含义:健康检查类型为HTTP时对应的检查路径。 +- 格式:长度为1~80个字符,只能使用字母、数字、字符。 必须以正斜线(/)开头。 +- 是否支持变更:支持 + +LBHealthCheckDomain + +- 含义:健康检查类型为HTTP时对应的域名。 +- 格式:特定域名长度限制1~80个字符,只能使用小写字母、数字、短划线(-)、半角句号(.)。 +- 是否支持变更:支持 + +LBHealthCheckMethod + +- 含义:健康检查类型为HTTP时对应的方法。 +- 格式:“GET” 或者 “HEAD” +- 是否支持变更:支持 + #### 插件配置 ``` [alibabacloud] @@ -588,6 +655,66 @@ AllowNotReadyContainers - 格式:{containerName_0},{containerName_1},... 例如:sidecar - 是否支持变更:在原地升级过程中不可变更。 +LBHealthCheckFlag + +- 含义:是否开启健康检查 +- 格式:“on”代表开启,“off”代表关闭。默认为on +- 是否支持变更:支持 + +LBHealthCheckType + +- 含义:健康检查协议 +- 格式:填写 “tcp” 或者 “http”,默认为tcp +- 是否支持变更:支持 + +LBHealthCheckConnectPort + +- 含义:健康检查的服务器端口。 +- 格式:取值范围[0, 65535]。默认值为“0” +- 是否支持变更:支持 + +LBHealthCheckConnectTimeout + +- 含义:健康检查响应的最大超时时间。 +- 格式:单位:秒。取值范围[1, 300]。默认值为“5” +- 是否支持变更:支持 + +LBHealthyThreshold + +- 含义:健康检查连续成功多少次后,将服务器的健康检查状态由失败判定为成功。 +- 格式:取值范围[2, 10]。默认值为“2” +- 是否支持变更:支持 + +LBUnhealthyThreshold + +- 含义:健康检查连续失败多少次后,将服务器的健康检查状态由成功判定为失败。 +- 格式:取值范围[2, 10]。默认值为“2” +- 是否支持变更:支持 + +LBHealthCheckInterval + +- 含义:健康检查的时间间隔。 +- 格式:单位:秒。取值范围[1, 50]。默认值为“10” +- 是否支持变更:支持 + +LBHealthCheckUri + +- 含义:健康检查类型为HTTP时对应的检查路径。 +- 格式:长度为1~80个字符,只能使用字母、数字、字符。 必须以正斜线(/)开头。 +- 是否支持变更:支持 + +LBHealthCheckDomain + +- 含义:健康检查类型为HTTP时对应的域名。 +- 格式:特定域名长度限制1~80个字符,只能使用小写字母、数字、短划线(-)、半角句号(.)。 +- 是否支持变更:支持 + +LBHealthCheckMethod + +- 含义:健康检查类型为HTTP时对应的方法。 +- 格式:“GET” 或者 “HEAD” +- 是否支持变更:支持 + #### 插件配置 ``` [alibabacloud] @@ -799,7 +926,7 @@ status: 此外,生成的EIP资源在阿里云控制台中会以{pod namespace}/{pod name}命名,与每一个游戏服一一对应。 --- -#### 插件名称 + ### AlibabaCloud-SLB-SharedPort `AlibabaCloud-SLB-SharedPort` @@ -1085,6 +1212,219 @@ networkStatus: --- +--- + +### AmazonWebServices-NLB + +#### 插件名称 + +`AmazonWebServices-NLB` + +#### Cloud Provider + +AmazonWebServices + +#### 插件说明 + +- 对于在AWS EKS集群中使用OKG的游戏业务,通过网络负载均衡将流量直接路由到Pod端口是实现高性能实时服务发现的基础。利用NLB进行动态端口映射,简化了转发链路,规避了Kubernetes kube-proxy负载均衡带来的性能损耗。这些特性对于处理副本战斗类型的游戏服务器尤为关键。对于指定了网络类型为AmazonWebServices-NLB的GameServerSet,AmazonWebServices-NLB网络插件将会调度一个NLB,自动分配端口,创建侦听器和目标组,并通过TargetGroupBinding CRD将目标组与Kubernetes服务进行关联。如果集群配置了VPC-CNI,那么此时流量将自动转发到Pod的IP地址;否则将通过ClusterIP转发。观察到GameServer的网络处于Ready状态时,该过程即执行成功。 + +- 是否支持网络隔离:否 + +#### 前提准备 + +由于AWS的设计有所区别,要实现NLB端口与Pod端口映射,需要创建三类CRD资源:Listener/TargetGroup/TargetGroupBinding + +##### 部署elbv2-controller: + +Listener/TargetGroup的CRD定义及控制器:https://github.com/aws-controllers-k8s/elbv2-controller ,该项目联动了k8s资源与AWS云资源,chart下载:https://gallery.ecr.aws/aws-controllers-k8s/elbv2-chart ,value.yaml示例: + +```yaml +serviceAccount: + annotations: + eks.amazonaws.com/role-arn: "arn:aws:iam::xxxxxxxxx:role/test" +aws: + region: "us-east-1" + endpoint_url: "https://elasticloadbalancing.us-east-1.amazonaws.com" +``` + +部署该项目最关键的在于授权k8s ServiceAccount访问NLB SDK,推荐通过IAM角色的方式: + +###### 步骤 1:为 EKS 集群启用 OIDC 提供者 + +1. 登录到 AWS 管理控制台。 +2. 导航到 EKS 控制台:https://console.aws.amazon.com/eks/ +3. 选择您的集群。 +4. 在集群详细信息页面上,确保 OIDC 提供者已启用。获取 EKS 集群的 OIDC 提供者 URL。在集群详细信息页面的 “Configuration” 部分,找到 “OpenID Connect provider URL”。 + +###### 步骤 2:配置 IAM 角色信任策略 + +1. 在 IAM 控制台中,创建一个新的身份提供商,并选择 “OpenID Connect” + - 提供商URL填写EKS 集群的 OIDC 提供者 URL + - 受众填写:`sts.amazonaws.com` +2. 在 IAM 控制台中,创建一个新的 IAM 角色,并选择 “Custom trust policy”。 + - 使用以下信任策略,允许 EKS 使用这个角色: + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Federated": "arn:aws:iam:::oidc-provider/oidc.eks..amazonaws.com/id/" + }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringEquals": { + "oidc.eks..amazonaws.com/id/:sub": "system:serviceaccount::ack-elbv2-controller", + "oidc.eks..amazonaws.com/id/:aud": "sts.amazonaws.com" + } + } + } + ] + } + ``` + - 将 ``、``、``、`` 和 `` 替换为您的实际值。 + - 添加权限 `ElasticLoadBalancingFullAccess` + +##### 部署AWS Load Balancer Controller: + +TargetGroupBinding的CRD及控制器:https://github.com/kubernetes-sigs/aws-load-balancer-controller/ + +官方部署文档:https://docs.aws.amazon.com/eks/latest/userguide/lbc-helm.html 其本质也是通过授权k8s ServiceAccount一个IAM角色的方式。 + +#### 网络参数 + +NlbARNs + +- 含义:填写nlb的arn,可填写多个,需要现在【AWS】中创建好nlb。 +- 填写格式:各个nlbARN用,分割。例如:arn:aws:elasticloadbalancing:us-east-1:888888888888:loadbalancer/net/aaa/3b332e6841f23870,arn:aws:elasticloadbalancing:us-east-1:000000000000:loadbalancer/net/bbb/5fe74944d794d27e +- 是否支持变更:是 + +NlbVPCId + +- 含义:填写nlb所在的vpcid,创建AWS目标组需要。 +- 填写格式:字符串。例如:vpc-0bbc9f9f0ffexxxxx +- 是否支持变更:是 + +NlbHealthCheck + +- 含义:填写nlb目标组的健康检查参数,可不填使用默认值。 +- 填写格式:各个配置用,分割。例如:"healthCheckEnabled:true,healthCheckIntervalSeconds:30,healthCheckPath:/health,healthCheckPort:8081,healthCheckProtocol:HTTP,healthCheckTimeoutSeconds:10,healthyThresholdCount:5,unhealthyThresholdCount:2" +- 是否支持变更:是 +- 参数解释: + - **healthCheckEnabled**:指示是否启用了健康检查。如果目标类型是lambda,默认情况下健康检查是禁用的,但可以启用。如果目标类型是instance、ip或alb,健康检查总是启用的,且不能禁用。 + - **healthCheckIntervalSeconds**:每个目标之间健康检查的时间间隔(以秒为单位)。 取值范围为5-300秒。如果目标组协议是TCP、TLS、UDP、TCP_UDP、HTTP或HTTPS,默认值为30秒。 如果目标组协议是GENEVE,默认值为10秒。如果目标类型是lambda,默认值为35秒。 + - **healthCheckPath**:[HTTP/HTTPS健康检查] 目标健康检查的路径。 [HTTP1或HTTP2协议版本] ping路径。默认值为/。 [GRPC协议版本] 自定义健康检查方法的路径,格式为/package.service/method。默认值为/Amazon Web Services.ALB/healthcheck。 + - **healthCheckPort**:负载均衡器在对目标执行健康检查时使用的端口。 如果协议是HTTP、HTTPS、TCP、TLS、UDP或TCP_UDP,默认值为traffic-port,这是每个目标接收负载均衡器流量的端口。 如果协议是GENEVE,默认值为端口80。 + - **healthCheckProtocol**:负载均衡器在对目标执行健康检查时使用的协议。 对于应用负载均衡器,默认协议是HTTP。对于网络负载均衡器和网关负载均衡器,默认协议是TCP。 如果目标组协议是HTTP或HTTPS,则不支持TCP协议进行健康检查。GENEVE、TLS、UDP和TCP_UDP协议不支持健康检查。 + - **healthCheckTimeoutSeconds**:在目标没有响应的情况下,认为健康检查失败的时间(以秒为单位)。取值范围为2-120秒。对于HTTP协议的目标组,默认值为6秒。对于TCP、TLS或HTTPS协议的目标组,默认值为10秒。对于GENEVE协议的目标组,默认值为5秒。如果目标类型是lambda,默认值为30秒。 + - **healthyThresholdCount**:在将目标标记为健康之前所需的连续健康检查成功次数。取值范围为2-10。如果目标组协议是TCP、TCP_UDP、UDP、TLS、HTTP或HTTPS,默认值为5。 对于GENEVE协议的目标组,默认值为5。如果目标类型是lambda,默认值为5。 + - **unhealthyThresholdCount**:指定在将目标标记为不健康之前所需的连续健康检查失败次数。取值范围为2-10。如果目标组的协议是TCP、TCP_UDP、UDP、TLS、HTTP或HTTPS,默认值为2。如果目标组的协议是GENEVE,默认值为2。如果目标类型是lambda,默认值为5。 + +PortProtocols +- 含义:pod暴露的端口及协议,支持填写多个端口/协议 +- 填写格式:port1/protocol1,port2/protocol2,...(协议需大写) +- 是否支持变更:是 + +Fixed +- 含义:是否固定访问端口。若是,即使pod删除重建,网络内外映射关系不会改变 +- 填写格式:false / true +- 是否支持变更:是 + +AllowNotReadyContainers +- 含义:在容器原地升级时允许不断流的对应容器名称,可填写多个 +- 填写格式:{containerName_0},{containerName_1},... 例如:sidecar +- 是否支持变更:在原地升级过程中不可变更 + +Annotations +- 含义:添加在service上的anno,可填写多个 +- 填写格式:key1:value1,key2:value2... +- 是否支持变更:是 + +#### 插件配置 +```toml +[aws] +enable = true +[aws.nlb] +# 填写nlb可使用的空闲端口段,用于为pod分配外部接入端口,范围最大为50(闭区间) +# 50限制来自AWS对侦听器数量的限制,参考:https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-limits.html +max_port = 32050 +min_port = 32001 +``` + +#### 示例说明 + +```shell +cat < + Connect Repo +2. 在弹出的面板中配置以下信息,然后单击CONNECT添加连接 + +| 区域 | 参数 | 参数值 | +|------------------------------------|--------------------------------|------------------------------------------------------------------| +| Choose your connection method | | VIA HTTPS | +| CONNECT REPO USING HTTPS | | git | +| | Project | default | +| | Repository URL | https://github.com/AliyunContainerService/gitops-demo.git | +| | Skip server verification | 勾选 | + + + + +### PvE类型游戏发 +PvE 类型游戏通常存在区服概念,大多情况下由运维工程师手动控制各地域的开服数量。关于 PvE 游戏云原生化最佳实践可参考 OKG PvE 游戏最佳实践文档(https://openkruise.io/zh/kruisegame/best-practices/pve-game) +在初次尝试ArgoCD时,我们可以使用白屏控制台为每个地域的集群分别创建Application: +1. 在ArgoCD UI左侧导航栏选择Applications,然后单击+ NEW APP +2. 在弹出的面板配置以下信息,然后单击CREATE进行创建。(以opengame-demo-shanghai-dev为例) + +| 区域 | 参数 | 参数值 | +|------------------------------|--------------------------|---------------------------------------------------------------------------------| +| GENERAL | Application Name | opengame-demo-shanghai-dev | +| | Project Name | default | +| | SYNC POLICY | 在下拉列表中选择Automatic。参数取值如下: | +| Manual:手动同步Git仓库变化 | | | +| Automatic:自动同步Git仓库变化,间隔3min | | | +| | SYNC OPTIONS | 勾选AUTO-CREATE NAMESPACE | +| SOURCE | Repository URL | 在下拉列表选择已有Git Repo,此处选择https://github.com/AliyunContainerService/gitops-demo.git | +| | Revision | HEAD | +| | Path | manifests/helm/open-game | +| DESTINATION | Cluster URL/Cluster Name | 在下拉列表中选择目标集群 | +| | Namespace | opengame | +| HELM | VALUES FILES | values.yaml | +| PARAMETERS | replicas | 3 #发布三个游戏服 | +| | scaled.enabled | false # 不开启自动弹性伸缩 | +3. 创建完成后,在Application页面,即可看到opengame-demo-shanghai-dev的应用状态。如果SYNC POLICY选择的是Manual方式,需要手动点击SYNC,将应用同步部署至目标集群。应用的Status为Healthy和Synced,表示已经成功同步。 + + + +4. 单击opengame-demo-shanghai-dev应用名称,即可查看应用详情,展示应用相关的Kubernetes资源的拓扑结构及相应状态。 + +对ArgoCD有所熟悉了之后,我们也可以通过ApplicationSet对象来一键发布游戏服。各个集群的差异性通过elements抽象出来,例如下面Yaml中,以集群维度抽象出三个字段:cluster集群名称用于区分Application名称;url用于区分目标集群地址;replicas用于区别不同集群发布的游戏服数量。 +编写完成该ApplicationSet Yaml 后,将其部署到ACK One舰队集群即可自动创建出四个Application。 +```bash +kubectl apply -f pve.yaml -n argocd + +# pve.yaml 内容如下: +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: minecraft +spec: + generators: + - list: + elements: + - cluster: shanghai-dev + url: + replicas: '1' + - cluster: shanghai-prod + url: + replicas: '3' + - cluster: frankfurt-prod + url: + replicas: '2' + - cluster: japan-prod + url: + replicas: '2' + template: + metadata: + name: '{{cluster}}-minecraft' + spec: + project: default + source: + repoURL: '' + targetRevision: HEAD + path: manifests/helm/open-game + helm: + valueFiles: + - values.yaml + parameters: #对应helm chart中提取的value参数 + - name: replicas + value: '{{replicas}}' + - name: scaled.enabled + value: 'false' + destination: + server: '{{url}}' + namespace: game-server #部署到对应集群的game-server命名空间下 + syncPolicy: + syncOptions: + - CreateNamespace=true #若集群中命名空间不存在则自动创建 +``` +在该Yaml中,所有的镜像版本都一致,若希望各集群镜像版本出现差异,可以仿照replicas的方式,添加新的parameters参数。 + +### PvP类型游戏发布 + +对于 PvP 类型的游戏,房间服的数量由自身伸缩器调配,而非运维工程师手动指定。有关 PvP 类型游戏的云原生化最佳实践可参考 OKG PvP 游戏最佳实践文档(https://openkruise.io/zh/kruisegame/best-practices/session-based-game) + +在 OKG 中我们通过为 GameServerSet 配置 ScaledObject 对象来实现房间服的弹性伸缩。因此,Helm Chart Value中的scaled.enabled 在此场景下需要开启。此外,房间服的副本数有 ArgoCD 和 OKG 2 个控制者而冲突,可以通过让 ArgoCD 忽略 GameServerSet 资源的副本数变化来解决,具体在 spec.ignoreDifferences 设置相应字段即可。考虑以上情况,该 pvp.yaml 如下所示: + +```bash +kubectl apply -f pvp.yaml -n argocd + +# pvp.yaml 内容如下: + +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: pvp +spec: + generators: + - list: + elements: + - cluster: shanghai-dev + url: + - cluster: shanghai-prod + url: + - cluster: frankfurt-prod + url: + - cluster: japan-prod + url: + template: + metadata: + name: '{{cluster}}-pvp' + spec: + project: defaultminecraft + ignoreDifferences: # 设置 GameServerSet minecraft副本数目由集群自控制 + - group: game.kruise.io + kind: GameServerSet + name: minecraft + namespace: game + jsonPointers: + - /spec/replicas + source: + repoURL: '' + targetRevision: HEAD + path: manifests/helm/open-game + helm: + valueFiles: + - values.yaml + destination: + server: '{{url}}' + namespace: pvp-server + syncPolicy: + syncOptions: + - CreateNamespace=true +``` + +在该Yaml中,所有的镜像版本都一致,若希望各集群镜像版本出现差异,可以仿照replicas的方式,添加新的parameters参数。 + +### 经验总结 +通过上面的示例,我们会发现做好应用的抽象是游戏服敏捷交付的关键之处。我们需要尽量保持GameServerSet大多字段一致,将有差异性的字段提取出来,这样只需要针对不同环境更改维护特定的相应字段即可,真正做到敏捷交付。 + +## 游戏服运维管理 +即使是同个工作负载(GameServerSet),游戏服之间的状态也是存在差异性的。在这种情况下,交付后的游戏服也需要持续地进行定向运维管理,这是与无状态业务最大的不同。 + +### OKG Dashboard 白屏化主动运维 +通常,我们需要主动运维游戏服 —— 统计和查询游戏服状态;定向更改游戏服版本、资源规格、运维状态等。通过OKG Dashboard可以实现游戏服的主动运维: +* 有关OKG Dashboard使用说明可参考:https://openkruise.io/zh/kruisegame/user-manuals/game-dashboard +* 对OKG Dashboard的更多需求可在issue下评论:https://github.com/openkruise/kruise-game/issues/139 + +### 建设监控告警机制,加强游戏服稳定性 +除了主动运维以外,我们需要建立稳定性问题订阅机制。当游戏服非预期运行时,运维工程师能够及时响应并处理。 +OKG提供了自定义服务质量的功能,灵活运用此功能可以实现定向游戏服异常状态透出并告警。可阅读文档: +* https://openkruise.io/zh/kruisegame/user-manuals/service-qualities#%E6%B8%B8%E6%88%8F%E6%9C%8D%E7%8A%B6%E6%80%81%E5%BC%82%E5%B8%B8%E8%AE%BE%E7%BD%AE%E7%BB%B4%E6%8A%A4%E4%B8%AD +* https://openkruise.io/zh/kruisegame/best-practices/pve-game#%E6%B8%B8%E6%88%8F%E6%9C%8D%E8%87%AA%E5%AE%9A%E4%B9%89%E6%9C%8D%E5%8A%A1%E8%B4%A8%E9%87%8F + +此外,若希望游戏服通过监控指标来实现定向告警,也可以通过在自定义服务质量脚本中调用prometheus API(pod name可利用DownwardAPI获取),对比指标阈值来决定GameServer OpsState的值。 \ No newline at end of file diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/best-practices/pve-game.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/best-practices/pve-game.md new file mode 100644 index 0000000000..76b5b2c2c9 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/best-practices/pve-game.md @@ -0,0 +1,440 @@ +# 传统区服类游戏(PvE)最佳实践 + +## PvE区服类游戏落地Kubernetes的挑战 + +首先,PvE区服类游戏有以下特点: + +1. 单个区服运行时间较长,应尽量避免停服操作,利于玩家游戏体验 +2. 开服时(或)存在配置差异 +3. 单区服容器中(或)存在多进程,区服服务质量需由用户定义 +4. 随着时间推移,各区服状态存在差异,需定向管理,如更改资源规格、镜像版本、定向合服等 + +该类游戏在落地Kubernetes通常遇到**左右为难**的困境: + +- 若使用Kubernetes原生workload,则无法进行游戏服精细化管理,具体地: + - 若使用Deployment管理: + - 生成的pod没有类似序号的状态标识,导致:1)无法基于序号进行有状态的服务发现了;2)无法区别游戏服之间状态差异性;3)异常重启时状态丢失,配置/存储等无法自动重定向。 + - 若使用StatefulSet管理: + - 生成的pod虽然有序号作为状态标识,但是:1)只能从序号大到小进行更新或删除,无法定向管理游戏服;2)无法感知游戏服之间的状态差异特性。 +- 若不使用Kubernetes原生workload,则无法利用上K8s的编排能力: + - 若使用脚本程序批量开服: + - 属于面向过程的方式,参数无法落盘,出错率高。 + - 若使用gitops管理: + - 区服数量较多时需要维护大量有着相同字段的yaml文件,有时甚至超过文件长度限制;批量发布时也十分复杂。 + - 若通过自建PaaS平台管理: + - 需要引入大量开发工作,且与业务属性耦合较重,导致后续迭代复杂 + +本篇最佳实践将介绍如何利用OKG管理区服类游戏服务。 + +## 游戏服热更新 + +OKG提供的原地升级热更新是一种更加云原生的游戏服热更落地方式,通过该方式可以实现游戏服热更文件的版本化管理、灰度更新、更新状态感知、以及故障恢复后热更版本一致性。 +具体实现方式可参考相应文档 https://openkruise.io/zh/kruisegame/user-manuals/hot-update + +## 游戏服配置管理 + +GameServerSet管理的GameServer具有序号属性,其名称是固定不变的,这一点与StatefulSet相同。因此,「GameServerSet名称+序号」可作为游戏服的唯一标识,联动分布式存储或配置管理系统进行配置的差异化管理。 + +### 挂载对象存储 + +通过对象存储,将不同游戏服的不同配置分别放在以游戏服名称命名的路径下,保证bucket的路径与游戏服一一对应,以PVC在GameServerSet上声明。该方式示意图如下: + + + +GameServerSet Yaml示例如下: + +```yaml +apiVersion: game.kruise.io/v1alpha1 +kind: GameServerSet +metadata: + name: gameserver + namespace: default +spec: + replicas: 2 + updateStrategy: + rollingUpdate: + podUpdatePolicy: InPlaceIfPossible + gameServerTemplate: + spec: + containers: + - image: registry.cn-hangzhou.aliyuncs.com/acs/minecraft-demo:1.12.2 + name: minecraft + env: + - name: POD_NAME #把pod name作为环境变量传入 + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + volumeMounts: + - name: pvc-oss #挂载oss对应pvc + mountPath: "/app/sgame.config" #容器中的目录及文件 + subPathExpr: $(POD_NAME)/sgame.config #对应oss目录及文件 + volumes: + - name: pvc-oss + persistentVolumeClaim: + claimName: pvc-oss +``` + +这样一来,开服前只需准备好游戏服对应配置并上传到bucket对应路径中,再部署GameServerSet或调整Replicas即可。 +如图所示,gameserver-0 与 gameserver-1两个目录下的文件内容不同,将分别挂载到对应的游戏服gameserver-0 与 gameserver-1上: + + + +### 动态拉取 + +如果业务存在配置中心服务(如Nacos),可以通过游戏服名称是固定且唯一的特性,在游戏服容器启动时,将自身的名称作为请求参数向配置中心发生请求拉取对应配置。 +容器自身名称的获取方式与挂载对象存储中类似,通过 DownwardAPI将其作为环境变量传入。 + +## 游戏服自定义服务质量 + +### 背景与概念 + +传统区服类游戏容器化落地时往往以“富容器”的形态存在,也就是一个容器中存在着多种进程,每个进程负责单个区服的不同功能。此时单个游戏服的状态错综复杂。而原生 Kubernetes 对业务状态管理停留在容器层面,无法精细化感知容器中特定进程状态,造成故障或异常难以定位处理。 + +OKG 认为游戏服的服务质量应由用户定义,用户可根据业务针对性地设置游戏服所处的状态,并精细化地进行相应处理。通过 OKG 的”自定义服务质量“探测到具体进程异常状态,并将其透出至 Kubernetes 侧,再利用 kube-event 等事件通知组件将异常告警至运维群中,帮助运维工程师快速发现问题,实现秒级故障定位,分钟级的故障处理。 + +下图是自定义服务质量功能示意图,通过 probe.sh 脚本的返回结果,对应更改GameServer的运维状态,实现故障/异常的快速定位: + + + +### 示例 + +我们来通过一个示例看下如何通过一个探测脚本实现游戏服多种状态感知。 + +在制作容器镜像时,编写探测容器状态的脚本。该示例脚本 probe.sh 将探测gate进程、data进程是否存在。 +当gate进程不存在则输出“gate”,并正常退出;当data进程不存在则输出“data”,并正常退出;当不存在异常,以退出码1退出。 + +probe.sh 是业务容器中探测脚本,将被OKG周期性调用,原理类似于Kubernetes原生的liveness/readiness探针。在上述场景下,其代码如下: + +```shell +#!/bin/bash + +gate=$(ps -ef | grep gate | grep -v grep | wc -l) +data=$(ps -ef | grep data | grep -v grep | wc -l) + +if [ $gate != 1 ] +then + echo "gate" + exit 0 +fi + +if [ $data != 1 ] +then + echo "data" + exit 0 +fi + +exit 1 +``` + +而对应的GameServerSet的yaml如下所示: + +```yaml +apiVersion: game.kruise.io/v1alpha1 +kind: GameServerSet +metadata: + name: minecraft + namespace: default +spec: + replicas: 3 + updateStrategy: + rollingUpdate: + podUpdatePolicy: InPlaceIfPossible + maxUnavailable: 100% + gameServerTemplate: + spec: + containers: + - image: registry.cn-beijing.aliyuncs.com/chrisliu95/minecraft-demo:probe-v0 + name: minecraft + serviceQualities: + - name: healthy + containerName: minecraft + permanent: false + exec: + command: ["bash", "./probe.sh"] + serviceQualityAction: + - state: true + result: gate + opsState: GateMaintaining + - state: true + result: data + opsState: DataMaintaining + - state: false + opsState: None +``` +部署完成后,生成3个Pod与GameServer + +```bash +kubectl get gs +NAME STATE OPSSTATE DP UP AGE +minecraft-0 Ready None 0 0 14s +minecraft-1 Ready None 0 0 14s +minecraft-2 Ready None 0 0 14s + +kubectl get po +NAME READY STATUS RESTARTS AGE +minecraft-0 1/1 Running 0 15s +minecraft-1 1/1 Running 0 15s +minecraft-2 1/1 Running 0 15s +``` + +进入minecraft-0容器中,模拟gate进程故障,将其对应的进程号kil + +```bash +kubectl exec -it minecraft-0 /bin/bash + +/data# ps -ef +UID PID PPID C STIME TTY TIME CMD +root 1 0 0 03:00 ? 00:00:00 /bin/bash ./start.sh +root 7 1 0 03:00 ? 00:00:00 /bin/bash ./gate.sh +root 8 1 0 03:00 ? 00:00:00 /bin/bash ./data.sh +root 9 1 99 03:00 ? 00:00:24 java -jar /minecraft_server. +... + +/data# kill -9 7 + +/data# exit +``` + +获取当前gs的opsState,已经变为GateMaintaining + +```bash + +kubectl get gs +NAME STATE OPSSTATE DP UP AGE +minecraft-0 Ready GateMaintaining 0 0 2m14s +minecraft-1 Ready None 0 0 2m14s +minecraft-2 Ready None 0 0 2m14s +``` + +进入minecraft-1容器中,模拟data进程故障,将其对应的进程号kil + +```bash +kubectl exec -it minecraft-1 /bin/bash + +/data# ps -ef +UID PID PPID C STIME TTY TIME CMD +root 1 0 0 03:00 ? 00:00:00 /bin/bash ./start.sh +root 7 1 0 03:00 ? 00:00:00 /bin/bash ./gate.sh +root 8 1 0 03:00 ? 00:00:00 /bin/bash ./data.sh +root 9 1 99 03:00 ? 00:00:24 java -jar /minecraft_server. +... + +/data# kill -9 8 + +/data# exit +``` + +获取当前gs的opsState,已经变为DataMaintaining + +```bash +kubectl get gs +NAME STATE OPSSTATE DP UP AGE +minecraft-0 Ready GateMaintaining 0 0 3m10s +minecraft-1 Ready DataMaintaining 0 0 3m10s +minecraft-2 Ready None 0 0 3m10s +``` + +分别进入minecraft-0,minecraft-1,手动拉起挂掉的进程: + +```bash +kubectl exec -it minecraft-0 /bin/bash + +/data# bash ./gate.sh & + +/data# exit + +kubectl exec -it minecraft-1 /bin/bash + +/data# bash ./data.sh & + +/data# exit +``` + +此时,gs的运维状态已经都恢复为None + +```bash +kubectl get gs +NAME STATE OPSSTATE DP UP AGE +minecraft-0 Ready None 0 0 5m6s +minecraft-1 Ready None 0 0 5m6s +minecraft-2 Ready None 0 0 5m6s +``` + +## 游戏服定向管理 + +### 设置GameServer回收策略 + +GameServer存在两种生命周期回收策略 —— Cascade 与 Delete,在GameServerSet.Spec.GameServerTemplate.ReclaimPolicy设置。 + +- Cascade:GameServer在pod删除时被回收,与pod生命周期保持一致。Cascade为ReclaimPolicy的默认值。 +- Delete:GameServer在GameServerSet副本数缩小时被回收。当对应的pod被手动删除、更新重建、被驱逐时,GameServer都不会被删除。 + +`Cascade`策略适合短生命周期游戏服,存在频繁启动删除而状态需要及时清空的情景,如大部分的PvP会话类游戏。 +而`Delete`策略更适合传统区服类PvE游戏,游戏服的状态需要长期被记录在GameServer上,避免状态丢失。只有用户执行合服/删服操作时才会将其回收。 +在创建GameServerSet时显式声明GameServer回收策略为Delete,能够更好地实现区服类游戏的定向管理功能。 + +### 定向更新游戏服镜像与资源规格 + +存在对特定游戏服存在定向更新镜像的场景,例如: + +- 在灰度或测试环境中,不同区对应着不同的镜像版本; +- SLG类型游戏存在玩法副本的概念,不同区的玩法可能不尽相同,对应着不同的镜像。 + +针对这种一个GameServerSet下可能存在多个版本的镜像游戏服,可以通过设置GameServer.Spec.Containers中image字段来指定更新特定游戏服镜像版本。 + +存在对特定游戏服存在定向更新镜像的场景,例如: + +- 随着时间增长,出现玩家增加过多、或者流失的情况,某些区服的计算资源无法满足当前需求。 + +针对这种一个GameServerSet下可能存在多种资源规格游戏服,可以通过设置GameServer.Spec.Containers中resources字段来指定游戏服特定资源规格。 + +定向更新游戏服镜像与资源规格的示意图如下: + + + +#### 示例说明 + +接下来通过一个示例来展示如何进行定向更新游戏服的镜像与资源规格。 + +首先部署一个3副本的GameServerSet + +```yaml +apiVersion: game.kruise.io/v1alpha1 +kind: GameServerSet +metadata: + name: minecraft +spec: + replicas: 3 + gameServerTemplate: + reclaimPolicy: Delete + spec: + containers: + - image: registry.cn-hangzhou.aliyuncs.com/acs/minecraft-demo:1.12.2 + name: minecraft + updateStrategy: + rollingUpdate: + podUpdatePolicy: InPlaceIfPossible + maxUnavailable: 100% +``` +定向更新minecraft-0的镜像,将其改为 registry.cn-hangzhou.aliyuncs.com/acs/minecraft-demo:1.12.2-new + +```yaml +kubectl edit gs minecraft-0 + +... +spec: + deletionPriority: 0 + opsState: None + updatePriority: 0 + # 新增containers + containers: + - name: minecraft + image: registry.cn-hangzhou.aliyuncs.com/acs/minecraft-demo:1.12.2-new +... +``` + +保存退出后,过一段时间过后,pod完成更新(由于指定原地升级策略,故容器重启次数+1): + +```bash +kubectl get po +NAME READY STATUS RESTARTS AGE +minecraft-0 1/1 Running 1 (13s ago) 3m28s +``` + +此时,假如pod故障/或被手动删除,生成的pod镜像会以GameServer声明的spec为准,例如: + +```bash +# delete pod +kubectl delete po minecraft-0 + +# pod state is Terminating, gs state is Deleting +kubectl get gs minecraft-0 +NAME STATE OPSSTATE DP UP AGE +minecraft-0 Deleting None 0 0 8m19s + +kubectl get po minecraft-0 +NAME READY STATUS RESTARTS AGE +minecraft-0 1/1 Terminating 1 (5m12s ago) 8m19s + +# after a while +# pod running again,age of GameServer is different from age of pod +kubectl get po minecraft-0 +NAME READY STATUS RESTARTS AGE +minecraft-0 1/1 Running 0 28s + +kubectl get gs minecraft-0 +NAME STATE OPSSTATE DP UP AGE +minecraft-0 Ready None 0 0 9m18s +``` + +由于设置了gs回收策略为Delete,所以游戏服的设置的状态不会因为pod的消失而消失。 +当前游戏服的镜像依然是更新后的registry.cn-hangzhou.aliyuncs.com/acs/minecraft-demo:1.12.2-new + +接下来定向更新游戏服minecraft-1的资源规格,将requests调整为cpu: 500m: + +```yaml +kubectl edit gs minecraft-1 + +... +spec: + deletionPriority: 0 + opsState: None + updatePriority: 0 + # 新增containers + containers: + - name: minecraft + resources: + requests: + cpu: 500m +... +``` + +资源配置不会立即原地更新。等待停服维护时,运维工程师手动使pod重建,新的资源规格即可生效,例如: + +```bash +kubectl delete po minecraft-1 + +# after a while + +# gs won't be deleted +kubectl get gs minecraft-1 +NAME STATE OPSSTATE DP UP AGE +minecraft-1 Ready None 0 0 15m + +# pod recreated +kubectl get po minecraft-1 +NAME READY STATUS RESTARTS AGE +minecraft-1 1/1 Running 0 11s +``` + +此时,pod的资源规格requests调整为cpu: 500m + +### 游戏服合服 + +当某一区服游戏玩家流失到一定程度,需要进行合服操作,此时可以定向将游戏服进行删除。 + +- 通过ReserveGameServerIds与replicas设置可以实现批量合服动作,例如,存在5个游戏服,id分别为0、1、2、3、4,希望删除游戏服2、3,则设置ReserveGameServerIds为2和3,同时replicas调整为3即可。详情可参考文档:[游戏服伸缩文档/游戏服 id reserve](https://openkruise.io/zh/kruisegame/user-manuals/gameservers-scale#%E6%B8%B8%E6%88%8F%E6%9C%8D-id-reserve) + +- 而通过设置gs的opsState为Kill也可以实现快速对一个游戏服进行删除操作。详情可参考文档:[游戏服伸缩文档/游戏服Kill](https://openkruise.io/zh/kruisegame/user-manuals/gameservers-scale#%E6%B8%B8%E6%88%8F%E6%9C%8D-kill) + + +## OKG管理PvE游戏服常见问题 + +### Q:如何决定GameServerSet的纳管范围? + +首先需要明确的是GameServerSet(简称gss)是集群维度的资源,不可跨集群声明。其次,PvE类型游戏通常会涉及到zone、group等概念,维度较多。这样一来,同个zone使用一个gss?还是同个group使用一个gss呢?实际上,判断条件主要取决于这些游戏服初始的差异性如何。 + +差异性的考量一般可以从两个方面来看: + +1. 配置差异性。如上文配置管理所述,可以通过游戏服GameServer集群命名空间内唯一名称的特性屏蔽掉内容的差异。故可以使用同一个GameServerSet管理。 +2. 资源规格差异性与镜像版本差异性。这类差异性存在两种情况:1)初始时一致,随着时间拉长开始出现差异性。2)初始就不一致。对于情况1,使用使用同一个GameServerSet管理游戏服,再利用上文提到的OKG定向更新功能即可。对于情况2,实际上也可以使用同一个GameServerSet管理游戏服,只不过在开新服时,做的操作就更加复杂一些,不仅需要调整副本数目,还需要调整对应的GameServer的Spec,使其拥有独立的镜像或资源配置。这种方式比较适合测试环境或区服数目较少的生产环境。在区服数目规模较大时,建议进行规格限定,使同种镜像同种资源规格的游戏服用同一个GameServerSet管理。 + +### Q:如何进行开新服? + +新开服的步骤如下: + +1. 确保集群中部署了相应的GameServerSet(初始副本数目或为0) +2. 若存在差异配置,则提前将配置文件准备好并上传至oss或用户自定义配置中心(若不存在差异性配置,则使用创建GameServerSet时配置configmap即可) +3. 找到对应的gss,调整副本数目使增加相应开服的数量 +4. 若开新服的镜像版本/资源规格与GameServerSet中声明的不一致,则可以更改对应的GameServerSpec的containers字段的镜像或资源。需要注意的是,如果要调整资源规格,需要手动删除pod,使其完成一次重建。 \ No newline at end of file diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/best-practices/session-based-game.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/best-practices/session-based-game.md new file mode 100644 index 0000000000..073b693e71 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/best-practices/session-based-game.md @@ -0,0 +1,319 @@ +# 会话类游戏(PvP开房间)最佳实践 + +会话类(session based)游戏,是指在有限的时间内,将玩家汇聚到特定游戏场景下的游戏类型。在通常意义下,会话等同对局,一局结束后,玩家间的游戏关系也在此结束,该会话也同时结束。因此,在业界也会将会话类游戏通俗的理解为“开房间游戏”,一个房间承载了对应的游戏会话。这类游戏往往存在着以下特点: + +- 游戏时间非连续,存在明显的起停时间节点 +- 常见于MOBA、FPS类游戏,对时延要求较高 +- 会话中至少存在2个及以上的玩家,相互战斗、交互 +- 业务波峰与波谷时,对局数量差距明显 + +根据以上特点,一个理想的会话类游戏云原生架构图如下所示 + + + + +它应该具备以下能力: + +- 提供网络直连功能,为每个房间提供独立的公网访问地址,玩家客户端可直接访问。 +- 提供游戏匹配功能,为玩家找到合适的队友与对手组成会话对局,并为其分配合适的游戏房间。 +- 提供状态管理功能,自动化管理游戏房间的业务状态与生命周期。 +- 提供弹性伸缩功能,根据业务波峰与波谷自动申请和释放基础设施资源,控制成本。 +- 可高效地进行游戏交付及运维管理,自动化水平高。 + +## 网络直连 + +会话类游戏需要网络直连,通常有以下考虑: + +- 降低游戏时延,增加玩家游戏体验 +- 去掉不必要的网关或代理,节约资源成本的同时简化技术架构 + +### 选择OpenKruiseGame网络模型 + +在传统游戏运维时代,游戏服业务与基础设施较为耦合,往往开发服务端程序时需要设计额外的端口分配管理器来避免同个机器上不同房间的端口冲突问题。理想状态,在云原生化后,游戏业务无需再关注游戏房间的端口分配问题,房间服可水平扩展,因此需要每个房间服拥有独自的公网访问地址,然而Kubernetes原生的service负载均衡模型却无法满足该需求。 + +OpenKruiseGame(OKG)提供了多种网络模型,自动化管理(创建&回收)房间服的公网地址(EIP+端口),自此以后游戏开发者无需关注基础设施的网络配置,而游戏运维者只需填写简单的参数就可以高效部署并自动化管理房间服网络。在OKG的模式下,**每个房间服对应一个pod**,针对会话类游戏,当前可使用的网络模型包括:Kubernetes-HostPort、AlibabaCloud-NATGW、 AlibabaCloud-SLB、AlibabaCloud-EIP。每种网络模型特点不同,适用于不同的场景。 + +**Kubernetes-HostPort** + +利用宿主机EIP + 端口作为公网地址,这种模式适合节点pod高密部署的情况。当房间服较小且数量很多时,调度到每个node上的pod数就会很多,此时充分利用该node上EIP的带宽,能够最大程度地节约EIP资源成本。 + +**AlibabaCloud-SLB** + +将同个SLB的不同端口映射到对应不同的房间服上,以实现房间服具有独立公网地址的效果。该模式配置最为简单,同时使用EIP的数量非常少。但注意SLB受限于后端最大实例数限制,每个SLB最多关联200个房间服,当GameServerSet下房间服的数量即将超过200时,需要新增SLB实例以满足扩容需求。 + +**AlibabaCloud-NATGW** + +将自动化管理房间服相关联的Dnat映射规则,用户需要安装ack-extend-network-controller组件,配置NAT网关相关参数。NATGW模型相对SLB模型可扩展性更强、更灵活,但同时配置起来也更加复杂。 + +**AlibabaCloud-EIP** + +为每个房间服pod分配独立的EIP。这种模式下消耗的EIP将比较多,适合于刚做容器化迁移时,游戏服存在端口管理器的情况。容器暴露的端口段即为被访问的端口段,不存在映射行为。 + +详细说明与示例可参考[OKG网络模型文档](https://openkruise.io/zh/kruisegame/user-manuals/network) + +### 获取房间服网络信息 + +游戏房间拥有了独立的公网访问地址,剩下的问题就是如何将该地址提供给玩家客户端。一般来说,会话类游戏会存在匹配服务这样的角色,而匹配服务又通常存在两种方式感知房间服的网络地址:1)主动获取;2)房间服自注册上报。 + +**主动获取** + +匹配服务会主动获取到当前集群中可用的房间服,并获取到对应公网地址,选择合适的房间服返回给客户端。这时匹配服务需要调用Kubernetes API,来获取GameServer对象中NetworkStatus。对于GameServer的CURD操作可以参考Kruise-Game官方仓库的[e2e用例](https://github.com/openkruise/kruise-game/tree/master/test/e2e) + +**注册上报** + +当然,也存在着房间服业务上报网络信息的情况。此时,利用[Kubernetes的DownwardAPI机制](https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/downward-api/)可将网络信息下沉至容器内被业务感知到,业务程序解析到对应地址后再上报即可。具体示例可参考[文档](https://openkruise.io/zh/kruisegame/user-manuals/network#downwardapi) + +## 游戏匹配 + +会话类游戏匹配的过程大致分为两个阶段 —— 1)玩家寻找队友/对手,形成对局;2)为对局分配合适的房间服,并将网络地址返回给玩家。 + +### 基于OpenMatch实现游戏匹配服务 + +在开源社区中有像Open Match这样的游戏匹配框架,用户只需按照框架标准实现匹配逻辑即可。OKG基于Open Match提供了 [kruise-game-open-match-director](https://github.com/CloudNativeGame/kruise-game-open-match-director) 组件,主要帮助实现上述匹配过程的第二阶段——为对局找到游戏服并返回地址。这样一来,用户只需关注第一阶段的匹配逻辑即可。有关基于Open Match 与 OKG 的匹配服务开发指南可以观看[云原生游戏系列课程](https://edu.aliyun.com/course/316831/lesson/19791?spm=a2cwt.28120015.316831.13.577431dbdxbMkD),也可以加入云原生游戏社区群(钉钉群ID:44862615)参与讨论或提问。 + +### 自研游戏匹配服务 + +当然,如果存在自研的匹配框架/系统,也可以通过简单的二次开发接入OKG。如上文中获取网络信息一节中所提到的,匹配服务联动房间服有两种方式,一种是主动获取房间服状态及网络信息;另一种是房间服自注册上报。但对于第二种方式,值得注意的是真正执行分配地址给玩家客户端前,需要确认一次对应房间服的状态,因为网络地址的获取和分配是异步的,中间过程中存在房间服不可用的情况。而对于第一种方式,推荐的做法是基于Kubernetes Informer机制监听GameServer对象,在存在为对局分配房间服需求时,获取当前可用的GameServer,并将对应的网络信息返回,此时的网络信息的获取和分配是同步的,具体实现方式可以参考[kruise-game-open-match-director的allocator代码](https://github.com/CloudNativeGame/kruise-game-open-match-director/blob/main/pkg/allocator.go)。 + +## 状态管理 + +### 会话类游戏状态设置 + +在上一节游戏匹配中,我们提到匹配服务在为形成对局的各个玩家客户端分配房间服地址时需要获取房间服的状态来保证给玩家分配的房间服是可用的。那么房间服状态可用应如何界定呢?在OKG的设计思想里,一个可用的游戏服应该 = 基础设施运行时状态可用(State Ready) + 基础设施网络可用(Network Ready) + 业务状态可用。 + +何为业务状态可用?这涉及到游戏业务对于房间服状态的定义。我们推荐房间服业务至少包含以下几种状态: + +- None(不存在任何异常或特殊的状态,表明可用,也是房间服初始化启动后的默认状态) +- Allocated(已被分配,表明正在或即将有玩家进行游戏) +- WaitToBeDeleted(即将被删除,等待OKG回收pod) + +以上三种状态可用GameServer Spec中的OpsState记录。OKG提供两种方式进行状态标记: + +- 调用Kubernetes API 直接更改GameServer.Spec.OpsState (通常为匹配系统分配完房间服后将其标记为Allocated) +- 通过[自定义服务质量](https://openkruise.io/zh/kruisegame/user-manuals/service-qualities)将容器中的业务状态暴露并转化为对应的GameServer.Spec.OpsState + +最简单的状态转化模式如图所示: + +1. 房间服被拉起后的默认状态可用,此时OpsState为**None** +2. 当匹配需求产生时,匹配服务查找可用的(基础设施Ready & OpsState为None)房间服,分配后将其OpsState置为**Allocated** (通过Kubernetes API进行设置,可参考[kruise-game-open-match-director的allocator代码](https://github.com/CloudNativeGame/kruise-game-open-match-director/blob/main/pkg/allocator.go)。若使用OKG + Open Match则无需设置,Director已经做了上述工作) +3. 当对局结束,游戏业务通过[自定义服务质量](https://openkruise.io/zh/kruisegame/user-manuals/service-qualities)将OpsState置为**WaitToBeDeleted**。这样对应的pod将被OKG自动进行回收删除,后续弹性伸缩部分将展开介绍。 + + + + +当然,如果希望不频繁的起停pod,在对局结束后也可以更改OpsState为None。整体状态转化模式如图所示: + +1. 同上,房间服被拉起后的默认状态可用,此时OpsState为**None** +2. 同上,分配房间服后,匹配系统将其设置为**Allocated** +3. 当对局结束,通过[自定义服务质量](https://openkruise.io/zh/kruisegame/user-manuals/service-qualities)将OpsState置为**None** +4. 通过协程判断房间服状态长期处于None状态时,将通过[自定义服务质量](https://openkruise.io/zh/kruisegame/user-manuals/service-qualities)将OpsState置为**WaitToBeDeleted。** + + + +### 通过服务质量透出房间服状态 + +在完成房间服状态流转设计后,我们会发现有些状态是有房间服业务决定的,而这些状态同时也需要透出到Kubernetes层面,这样才能联动自动伸缩器、匹配系统等。因此,需要一种机制将业务状态标志到Kubernetes对象上,也就是GameServer上,而这就是**自定义服务质量**功能。 + +自定义服务质量通过**执行探测脚本**的结果,以及用户设置的**探测结果对应状态**来自动化地将房间服状态标记到GameServer上。 + +下面是一个状态探测脚本名为waitToBeDeleted.sh,探测容器中GS_STATE环境变量的值是否为WaitToBeDeleted + +```bash +#!/bin/bash +if [ -z "$GS_STATE" ]; then + exit 1 +elif [ "$GS_STATE" = "WaitToBeDeleted" ]; then + echo "$GS_STATE" +else + exit 1 +fi +``` + +对应的GameServerSet yaml应该如下 + +```bash +... +spec: + ... + serviceQualities: + - name: waitToBeDeleted + containerName: battle #探测容器名为battle的容器 + permanent: false + exec: + #OKG将周期性执行battle容器中./waitToBeDeleted.sh脚本(需要注意将脚本放置到对应的路径下) + command: ["bash", "./waitToBeDeleted.sh"] + serviceQualityAction: + #当探测结果为true,也就是脚本执行结果为正常退出(退出码为0)时,标记GameServer的opsState为WaitToBeDeleted + - state: true + opsState: WaitToBeDeleted +``` + +当然,自定义服务质量可以有多个,比如当房间服需要将自身None状态透出时,名为none.sh的脚本如下: + +```bash +#!/bin/bash +if [ -z "$GS_STATE" ]; then + exit 1 +elif [ "$GS_STATE" = "None" ]; then + echo "$GS_STATE" +else + exit 1 +fi +``` + +对应的GameServerSet yaml应该如下: + +```bash +... +spec: + ... + serviceQualities: + - name: waitToBeDeleted + containerName: battle #探测容器名为battle的容器 + permanent: false + exec: + #OKG将周期性执行battle容器中./waitToBeDeleted.sh脚本(需要注意将脚本放置到对应的路径下) + command: ["bash", "./waitToBeDeleted.sh"] + serviceQualityAction: + #当探测结果为true,也就是脚本执行结果为正常退出(退出码为0)时,标记GameServer的opsState为WaitToBeDeleted + - state: true + opsState: WaitToBeDeleted + - name: none + containerName: battle #探测容器名为battle的容器 + permanent: false + exec: + #OKG将周期性执行battle容器中./none.sh脚本(需要注意将脚本放置到对应的路径下) + command: ["bash", "./none.sh"] + serviceQualityAction: + #当探测结果为true,也就是脚本执行结果为正常退出(退出码为0)时,标记GameServer的opsState为None + - state: true + opsState: None +``` + +至此,我们发现房间服业务程序只需要在合适的时刻节点设置对应的GS_STATE的环境变量值即可。比如: + +- 当房间服刚刚拉起,设置GS_STATE=None; +- 当房间服有玩家进入,设置GS_STATE=Allocated(尽管不需要透出到opsState,但依然可以做状态变迁,避免自身状态与在Kubernetes显示的不一致); +- 当房间服对局结束,再设置GS_STATE=None; +- 当房间服长时间空闲,设置GS_STATE=WaitToBeDeleted。 + +## 弹性伸缩 + +在上一节中,我们设计了三种房间服状态:None / Allocated / WaitToBeDeleted。在本节,我们将根据以上房间服状态进行相应的弹性伸缩配置。 + +对于会话类游戏弹性伸缩的理想状态就是,在业务高峰期房间服数量足够多,可以让玩家秒级接入;而业务低峰期的时候减少房间服的数量,节约资源成本。OKG提供了自动伸缩器,可以感知房间服状态,来自动调节GameServerSet的replicas值,从而实现根据游戏业务状态伸缩的理想效果。 + +### 房间服自动减少 + +在状态管理一节中,我们也有所提到,opsState为WaitToBeDeleted的GameServer将会自动被OKG回收。这样一来,只要业务决定了自身不再提供服务了,通过自定义服务质量设置WaitToBeDeleted即可。关于缩容策略的具体的配置可以参考 [https://openkruise.io/zh/kruisegame/user-manuals/gameservers-scale#使用示例](https://openkruise.io/zh/kruisegame/user-manuals/gameservers-scale#%E4%BD%BF%E7%94%A8%E7%A4%BA%E4%BE%8B) + +### 房间服自动增加 + +OKG提供自动扩容的核心策略就是保证房间服存在可用且充足的数量。这个数量等同于buffer,是由用户决定的,在OKG中这个参数叫做 **minAvailable**。 + +在当前所有opsState为None的游戏服数量少于设置**minAvailable**值时,OKG将自动扩容出新的游戏服,使opsState为None的游戏服数量满足设置的最小个数。 + +### 资源自动伸缩 + +Kubernetes的弹性伸缩涵盖两个层面,应用层弹性 与 资源层弹性。其中OKG提供了房间服应用层弹性的能力,自动地调节房间服对应pod的数量。而仅调节pod的数量是无法实现资源成本的节约,需要自动地调节节点数量,这正是Kubernetes cluster-autoscaler实现资源层弹性的方式。cluster-autoscaler核心原理是: + +- 当pod由于资源不足而处于pending状态时,自动弹出节点。 +- 当节点利用率过低/节点空闲时,自动回收节点。 + +对于游戏场景,自动伸缩最佳实践建议如下: + +- 根据节点规格设置OKG **minAvailable** 大小。节点的启动是需要时间的,所以需要提前准备好空闲可用的房间服供玩家连接。空闲的房间服本质上将节点水平扩容的触发时间前置了,弥补了启动机器的时间差。这样一来,节点规格和minAvailable就关联密切了,举个例子:集群中使用的节点规格为8核16G,而运行在其上的房间服pod需要1核2G的资源,这样理论上该节点可以运行7个房间服(节点会有保留资源,所以不是8个)。在这种情况下,minAvailable 设置为7以上,则集群中会一直存在着一个“空闲”节点(这里的空闲指的不是没有pod,而是没有玩家);设置14以上,则集群中会一直存在着两个“空闲”节点,这样也就将**房间服备用数量**转化为了**节点备用数量**,用户可以按照业务场景、成本控制的角度具体设置。 + +- 设置节点完全空闲时才使cluster autoscaler自动回收节点,保障游戏运行正常。根据资源阈值的方式缩容对游戏并不友好,由于游戏是有状态的服务,存在极大的可能性遇到节点上资源负载较小但玩家依然正在游戏的情况,不能轻易删除。 + +## 房间服热更新 + +为了平滑地进行房间服升级,同时简化运维操作,往往存在着需求:希望一键更新房间服版本,同时不影响玩家的游戏体验,不进行停服维护。这一过程也叫做房间服的热更新。 +房间服的热更新与传统PvE类型游戏需要的原地升级不同,由于单局时间短,往往在开启游戏对局之后不会更改该房间的服务逻辑,而是一键更新发布后,新开启的房间使用最新的版本,存量的房间服不做任何改动;同时配合匹配系统,将新进入的玩家导入新版本的房间;再利用自动伸缩机制使旧版本的房间数目随着人数流失而不断减少,新版本的房间数随着人数增加不断增加,最终完成版本切换。 +整个过程只需更改房间服容器镜像即可,自动化不停服更新,大幅度减少运维复杂度。 + +### 不更新存量房间服 + +使用GameServerSet工作负载时可以选择“OnDelete”更新类型,实现不更新存量房间服,而新的房间服使用新版本的效果。 + +例如,起始部署一个3副本的游戏服集合,镜像tag全部为1.12.2 +```yaml +apiVersion: game.kruise.io/v1alpha1 +kind: GameServerSet +metadata: + name: minecraft + namespace: default +spec: + replicas: 3 + updateStrategy: + type: OnDelete + gameServerTemplate: + spec: + containers: + - image: registry.cn-hangzhou.aliyuncs.com/acs/minecraft-demo:1.12.2 + name: minecraft +``` + +此时,进行镜像更新,镜像tag改为1.12.2-new + +```bash +kubectl edit gss minecraft + +... +spec: + gameServerTemplate: + spec: + containers: + - image: registry.cn-hangzhou.aliyuncs.com/acs/minecraft-demo:1.12.2-new + name: minecraft +... +``` + +可以看到,存量的游戏服并没有进行更新,镜像版本依然为1.12.2 + +```bash +kubectl get po -oyaml | grep minecraft-demo + - image: registry.cn-hangzhou.aliyuncs.com/acs/minecraft-demo:1.12.2 + image: registry.cn-hangzhou.aliyuncs.com/acs/minecraft-demo:1.12.2 + imageID: registry.cn-hangzhou.aliyuncs.com/acs/minecraft-demo@sha256:8aa4177a19b15d7336162c6ca4d833a74c3cb23d85eab2ef2a63f7a2a682b8fb + - image: registry.cn-hangzhou.aliyuncs.com/acs/minecraft-demo:1.12.2 + image: registry.cn-hangzhou.aliyuncs.com/acs/minecraft-demo:1.12.2 + imageID: registry.cn-hangzhou.aliyuncs.com/acs/minecraft-demo@sha256:8aa4177a19b15d7336162c6ca4d833a74c3cb23d85eab2ef2a63f7a2a682b8fb + - image: registry.cn-hangzhou.aliyuncs.com/acs/minecraft-demo:1.12.2 + image: registry.cn-hangzhou.aliyuncs.com/acs/minecraft-demo:1.12.2 + imageID: registry.cn-hangzhou.aliyuncs.com/acs/minecraft-demo@sha256:8aa4177a19b15d7336162c6ca4d833a74c3cb23d85eab2ef2a63f7a2a682b8fb +``` + +此时,进行扩容,新创建一个游戏服minecraft-3,发现其镜像版本为1.12.2-new +```bash +kubectl scale gss minecraft --replicas=4 +gameserverset.game.kruise.io/minecraft scaled + + +kubectl get po minecraft-3 -oyaml | grep minecraft-demo + - image: registry.cn-hangzhou.aliyuncs.com/acs/minecraft-demo:1.12.2-new + image: registry.cn-hangzhou.aliyuncs.com/acs/minecraft-demo:1.12.2-new + imageID: registry.cn-hangzhou.aliyuncs.com/acs/minecraft-demo@sha256:f68fd7d5e6133c511b374a38f7dbc35acedce1d177dd78fba1d62d6264d5cba0 +``` + +### 与匹配系统的联动 + +当存GameServerSet下存在新版本的镜像时,匹配系统需要决定玩家进入哪一个版本的房间服。 + +匹配系统有两种方式感知到当前工作负载下的版本: + +1. 房间服启动时将自身版本主动注册上报,删除时反注册析构 +2. 匹配系统匹配时调用Kubernetes API查询当前合适该玩家的版本 + +这两种方式与前文提到的如何获取房间服地址的方式是类似的,与其实现方式保持一致即可。 +如按方式1,房间服启动时将访问地址信息上报的同时一并将版本信息上报;如按方式2,则在查找合适房间服时额外增加筛选房间服版本的逻辑。 + +需要注意的是,即使匹配系统感知到最新版本,但此时并不能使玩家只进入到最新版本中。由于刚刚完成版本更新,新版本的房间数量并不充足,需要存在一定时间的过渡阶段,让找不到最新版本房间服的玩家依然可以进入旧版本游戏。 + +### 通过自动伸缩完成平滑升级 + +当GameServerSet镜像版本已经更新,且匹配系统可感知到最新版本时,我们希望新版本的房间服越来越多,而旧版本的房间服越来越少,而这恰好通过自动伸缩来实现。 + +首先,在版本切换之初,GameServerSet下只存在旧版本的房间服,且该版本的房间服存在以下几种状态,Allocated、WaitToBeDeleted、None。 +若Allocated的房间服在对局结束后直接变为WaitToBeDeleted,则旧版本的None在玩家不激增的情况下会先变为Allocated、再变为WaitToBeDeleted,进而整体会随着时间的推移逐渐减少,而新的房间服数量会因为设置了minAvailable参数而不断增加; +但如果Allocated的房间服在对局结束后被再次利用重新变为None,在玩家不激增的情况下,会存在很长一段时间新版本的房间服无法扩容出来,所以建议在更新GameServerSet镜像后手动调大其副本数量,直接扩容出最新版本的房间服。旧版本的处于None状态的房间服会由于长时间等待不到玩家进入而进入WaitToBeDeleted状态,最终被删除。 \ No newline at end of file diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/best-practices/shared-mem.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/best-practices/shared-mem.md new file mode 100644 index 0000000000..b74a12fee2 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/best-practices/shared-mem.md @@ -0,0 +1,227 @@ +# 游戏服共享内存最佳实践 + +## 背景说明 + +内存敏感型游戏服务,特指对于内存资源需求较大的游戏服,在启动时往往需要加载众多资源到内存之中以提升玩家游戏交互体验。但也正因于此,带来了1)游戏服启动速度慢、版本更新时效率低;2)游戏服之间存在相同内存数据但无法被服用,节点内存资源被过度浪费的问题。 + +游戏开发者往往使用共享内存技术来解决上述问题,以提升游戏服启动效率和内存资源效率。通常,存在一个init进程,执行游戏服初始化加载的功能,将数据写入共享内存;在此之后,在该机器上新创建的所有游戏服都无需重复执行该过程,只需读取同一块地址对应的内存数据即可,启动速度提升了,且内存资源被复用,不会造成资源浪费。 + +本文将关注容器化游戏服共享内存使用方案,提供最佳实践。 + +## 方案介绍 + +方案涉及到两种类型的进程,如上文中提到的init进程,进行写内存;gs进程在启动时进行读内存。 + + + + +架构图若上所示,init进程使用DaemonSet进行管理,每个游戏服节点部署一个init;而gs进程使用GameServerSet进行管理,每个节点可以有多个gs。当DaemonSet部署完成并且对应的init pod成功执行后,进行GameServerSet部署,开启游戏服。此时,gs将快速启动,并且同一个节点上的gs会初始时复用同一块内存空间。 + +## 实践示例 + +### 1. init进程程序示例(写内存) + +使用下面代码创建一块共享内存,然后每隔一秒向内存写入id数据,id每秒递增加一 + +```cpp +/* study.cpp 写端代码 */ +#include +#include +#include +#include +#include +#include + +/* 用于传递消息的结构体 */ +typedef struct _msg +{ + int id; + char str[64]; +}MSG; + +int main() +{ + MSG* msg; + + /* 获取键值 */ + key_t key = ftok("./",2015); + if(key == -1) + { + perror("ftok"); + exit(-1); + } + + /* 创建或打开一块共享内存,并返回内存标识符 */ + int shd = shmget(key,sizeof(MSG),IPC_CREAT | 0666); + if(shd == -1) + { + perror("shmget"); + exit(-1); + } + + /* 映射内存地址到当前进程,并返回内存块的地址 */ + msg = (MSG*)shmat(shd,NULL,0); + if(msg == (MSG*)-1) + { + perror("shmat"); + exit(-1); + } + + /* 改变内存地址的数据 */ + memset((void*)msg,0,sizeof(MSG)); + for(int i = 0;i < 100000;i++) + { + msg->id = i; + printf("msg->id = %d\n",msg->id); + sleep(1); + } + + /* 查看系统中的共享内存 */ + system("ipcs -m"); + + return 0; +} +``` + +### 2. gs进程程序示例(读内存) + +以下为读取共享内存的代码, 代码获取shm_id为0的共享内存数据,循环打印id数据。 + +```cpp +#include +#include +#include +#include + +int main() { + int shm_id; + void *shared_memory; + + // 获取共享内存标识符 + shm_id = 0; + printf("shm_id: %d\n", shm_id); +// if (shm_id == -1) { +// perror("shmget failed"); +// exit(1); +// } + + // 连接共享内存 + shared_memory = shmat(shm_id, NULL, 0); + if (shared_memory == (void *) -1) { + perror("shmat failed"); + exit(1); + } + + // 读取共享内存数据 + while(1) { + printf("Value from shared memory: %d\n", *((int *)shared_memory)); + } + + // 断开与共享内存的连接 + if (shmdt(shared_memory) == -1) { + perror("shmdt failed"); + exit(1); + } + + return 0; +} +``` + +### 3. 制作镜像 + +gs的Dockerfile如下(init 与之类似): + +```docker +FROM gcc:latest + +WORKDIR /usr/src/myapp +COPY . . + +RUN gcc -o read read.c + +USER root + +RUN chmod 777 /usr/src/myapp/read + +EntryPoint ["/usr/src/myapp/read"] + +CMD ["sleep 300000"] +``` + +### 4. 部署init进程 + +```yaml +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: shm-daemonset + namespace: default +spec: + selector: + matchLabels: + name: init + template: + metadata: + labels: + name: init + spec: + hostIPC: true #设置 hostIPC: true 时,Pod将使用宿主机的IPC命名空间,使用宿主IPC的Pod可以访问宿主机上的共享内存段 + nodeSelector: + app: shared-mem #通过标签控制共享内存作用的节点范围 + containers: + - name: init + image: registry.cn-hangzhou.aliyuncs.com/skkk/testc:write27_v2 + volumeMounts: + - name: shm + mountPath: /dev/shm + volumes: + - name: shm + hostPath: + path: /dev/shm + type: Directory +``` + +创建ds后,可以在宿主机上看到创建的共享内存 + + + +可以在容器日志中看到每隔一秒修改id的值 + + + +### 5. 部署gs进程 + +创建gs从共享内存中读取id的值 + +```yaml +apiVersion: game.kruise.io/v1alpha1 +kind: GameServerSet +metadata: + name: gameserver + namespace: default +spec: + replicas: 2 + updateStrategy: + rollingUpdate: + podUpdatePolicy: InPlaceIfPossible + gameServerTemplate: + spec: + hostIPC: true + nodeSelector: + app: shared-mem + containers: + - image: registry.cn-hangzhou.aliyuncs.com/skkk/testc:readtest + imagePullPolicy: Always + name: gs + volumeMounts: + - name: shm + mountPath: /dev/shm + volumes: + - hostPath: + path: /dev/shm + type: Directory + name: shm +``` + +部署成功后在容器日志中可以看到已经获取到共享内存中的id值 + diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/best-practices/workflow.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/best-practices/workflow.md new file mode 100644 index 0000000000..d2005dc29a --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/best-practices/workflow.md @@ -0,0 +1,215 @@ +# 游戏运维工作流最佳实践 + +## 背景 + +由于游戏服务器有状态的特性,对于游戏运维而言,通常需要根据当前业务状态进行相应处理,以避免玩家体验受损。OKG提供了游戏服务器状态感知和定向管理的能力,具备根据不同状态进行不同处理的前提条件。在实际生产过程中,一个运维流程是多个运维动作的组合,运维工程师往往需要进行“游戏服状态确认” → “游戏服操作”的往复动作,这也导致游戏服云原生化后依然存在一定的操作复杂度。 + +本文将结合“房间服无损发布”这一实际场景,向读者展示如何通过Argo Workflow将对GameServerSet和GameServer的运维动作有效组合起来,从而构筑一套既流畅又高效的运维工作流。 + +## 示例 + +### 场景说明 + +“房间服无损发布” 需要满足以下特点: + +- 正在游戏的旧版本房间不受影响,而空闲的旧版本房间服需要被回收清理 +- 存在一定数量的新版本房间,可让新连接的玩家随时进入 + +为在新版本发布保证“房间无损”,房间服在交付时会选择 OnDelete 更新策略,以实现存量房间不被删除,新创建的房间使用新的镜像的效果。此外,对应GameServerSet可以通过配置OKG自定义服务质量与自动伸缩策略实现自动化生命周期管理。有关房间服最佳实践文档可参考 https://openkruise.io/zh/kruisegame/best-practices/session-based-game + +### 新版发布流程 + +基于以上交付内容,运维工程师在更新新版本时将进行以下动作: + +1. 更新GameServerSet镜像,此时正在运行的房间服不会删除或重建 +2. 对GameServerSet进行扩容,扩容出足够多的新版本房间服 +3. 确认新版本房间服的状态是否正常提供服务 +4. 清理旧版本的空闲房间 + +接下来我们将通过一个示例展示如何一键式完成以上流程。 + +### 模拟存量旧版本房间服状态 + +*注:集群中需安装OKG* + +集群中有3个旧版本房间服,版本号为`1.12.2` + +``` +cat < 作者:刘秋阳、蔡靖 +> +> 时间:2024-04-26 +> +> **[原文链接](https://mp.weixin.qq.com/s/TOPcOsE5WCIIXkgo9jujlA)** \ No newline at end of file diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/blog-video/cloud-forward-okg.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/blog-video/cloud-forward-okg.md new file mode 100644 index 0000000000..ab0b3ca0d4 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/blog-video/cloud-forward-okg.md @@ -0,0 +1,19 @@ +# Cloud Forward | 云原生游戏系列视频 + +## Cost Efficiency and DevOps Boost Optimization in the Gaming Industry + + +## OpenKruiseGame for Game Workloads + + +## Implementing PvP Session based Games with OpenKruiseGame + + +## Deploying PvE Zone server Games with OpenKruiseGame + + +## Migrating H5 and Comprehensive Games to the Cloud with OpenKruiseGame + + +## Global Delivery and O&M Management via ACK One + OpenKruiseGame + \ No newline at end of file diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/blog-video/guanying-20231129.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/blog-video/guanying-20231129.md new file mode 100644 index 0000000000..6551ca309c --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/blog-video/guanying-20231129.md @@ -0,0 +1,65 @@ +# 冠赢互娱基于OpenKrusieGame实现游戏云原生架构升级 + +> 摘要:传统区服PvE类型游戏平滑落地Kubernetes的实践 +> +> 作者:刘秋阳 +> +> 时间:2023-11-29 + +## 关于冠赢互娱 +冠赢互娱是一家集手游、网游、VR游戏等研发、发行于一体的游戏公司,旗下官方正版授权的传奇类手游——《仙境传奇》系列深受广大玩家们的喜爱。基于多年MMORPG类型游戏的自研与运营经验,冠赢互娱正式推出了2D MMO游戏开发引擎Thousand,并成功应用至近期上线的《仙境传奇-梦回零三》 手游。其背后采用的云原生架构大幅度提升了游戏开服、更新等运维效率,同时降低了服务器的资源成本,并为后续开发更优秀的产品、加快游戏生态成型提供扎实基奠。 + + + +## 启用云原生架构的初衷 +在Thousand引擎立项之初,研发团队基于传统区服类游戏的特点,决定采用云原生架构。主要的考虑如下: +1. 区服之间具有强隔离属性,应尽量避免资源抢占。过往运营游戏时会出现同台宿主机上不同区之间相互资源干扰的情况,加大了受影响的玩家数量。而利用容器技术可以实现精细化的资源控制,避免区服之间相互干扰,能够有效降低故障影响面。 + +2. 通过声明式的方式进行游戏服管理带来了效率优势。从过去运维机器、执行一系列脚本演化为以服务为对象、批量且自动化管理的方式,不仅可大幅度提升了开服效率,同时也能降低游戏维护时的出错概率。 + +3. 需要更加精细化的故障定位、及业务快速恢复的能力。区服共享计算节点,当故障发生时,无法及时定位故障根因源自区服A、区服B、还是宿主机,且当机器故障时,业务迁移效率十分低下。通过云原生架构,基础设施资源与业务一定程度的解耦带来了业务故障快速定位的能力,并且容器轻量且环境一致性的特点带来了高效的业务恢复能力,问题定位及恢复的效率大幅度提升。 + +4. 云原生生态日益茁壮,通过云原生技术不仅可高度集成计算、网络、存储等基础设施资源、而且可以非常轻便地利用上可观测、调度、应用交付等能力。 + +## 游戏服落地Kubernetes的挑战 +然而,云原生化绕不开的容器编排标准Kubernetes对游戏的支持力度十分有限,冠赢传奇类游戏在落地Kubernetes的过程中也遇到了众多挑战: + + + +1. 每个区服需要单独暴露公网地址,玩家选择服务器后可直连对应区服。额外进行接入层网络管理无疑增加了区服批量管理时的运维成本;同时如果选择单个区服pod绑定EIP的模式又会消耗大量的EIP资源,造成经济成本浪费。 + +2. 单个区服是由多个服务共同组成,容器化后以一种“富容器”的形态存在。原生Kubernetes对业务状态管理停留在容器层面,无法精细化感知容器中特定进程状态,造成故障或异常难以定位处理;而将服务拆分,单独部署增加了架构复杂性、改造难度将急剧上升。 + +3. 一个完整的游戏服由引擎侧与脚本侧组成,游戏服引擎支持热更脚本,避免频繁停服造成玩家流失。研发团队设计了多种游戏服落地Kubernetes后的热更方案,包括从公共服务器拉取最新热更文件、或通过云储存动态挂载热更文件。但无论哪种方式,都会遇到各式问题,包括:1)不支持版本化管理热更文件,更新频繁后实际存在的众多版本无法与文件形成对应关系,造成更新失败后回滚复杂;2)更新状态难以定位。即使对容器中的文件进行了更新替换,但执行重载命令时难以确定当前热更文件是否已经挂载完毕,这种更新成功与否的状态维护需要交给运维者额外管理,也一定程度上提高了运维复杂度;3)在容器异常时,pod重建拉起旧版本的镜像,热更文件并未能持续化保留;4)更新速度始终不尽人意。 + +## OpenKruiseGame助力游戏服云原生化落地 +冠赢利用OpenKruiseGame解决了上述问题,实现了2D MMO游戏开发引擎Thousand在Kubernetes的平滑落地。OpenKruiseGame(简称OKG),是CNCF孵化项目OpenKruise在游戏领域的子项目,其专门为游戏打造,协助游戏开发者实现更敏捷的游戏弹性架构、统一标准的运维动作、多云一致性交付、建立游戏自运维平台等能力。 + +面对以上挑战,冠赢使用了OKG以下能力: +1. OKG提供了接入层网络自动化管理的能力,用户无需手动为每个区服构建/析构网络,并且针对不同场景支持了不同的网络模型。冠赢根据自身业务特点使用了NATGW模型,区服开服时自动生成dnat entry,区服被合并(删除)时自动删除dnat entry,多个区服将共享EIP,可充分利用EIP的带宽资源。 + +2. OKG认为游戏服的服务质量应由用户定义,用户可根据业务针对性地设置游戏服所处的状态,并精细化地进行相应处理。冠赢面对“富容器”的游戏场景,通过OKG的”自定义服务质量“探测到具体进程异常状态,并将其透出至Kubernetes侧,再利用kube-event等事件通知组件将异常告警至运维群中,帮助运维工程师快速发现问题,实现秒级故障定位,分钟级的故障处理。 + +3. OKG提供了基于容器镜像的原地热更方案,热更脚本作为sidecar容器与main容器一同部署在同一个游戏服,二者通过emptyDir共享热更文件,更新时只需更新sidecar容器即可。这样一来游戏服的热更将以云原生的方式进行:1)sidecar容器镜像具有版本属性,解决了版本管理问题;2)Kubernetes容器更新成功后处于Ready状态,能够感知sidecar脚本更新是否成功;3)即使容器异常发生重启,热更文件随着镜像的固化而持续化保留了下来;4)通过镜像预热机制能够快速完成热更过程。 + +## 云原生化成果 +Thousand引擎整体云原生架构图如下所示。冠赢实现了基于OKG的平台化工程: +1. 游戏工程师上传新脚本,触发CI流程,自动打包镜像后自动部署新的GameServer到Kubernetes集群;通过编辑旧脚本,同样可以触发CICD,并基于OKG的原地升级更新对应GameServer的sidecar镜像,实现游戏服热更。整个过程无需游戏运维工程师参与,通过云原生技术将游戏服部署更新的能力交予游戏开发者,提高了游戏生产效率。 + +2. 自动生成的GameServer基于OKG的网络功能具备独立的公网访问地址(EIP:端口 唯一),Thousand引擎平台提供服务发现机制使玩家直连对应区服进行游戏。 + +3. 当游戏服偶发异常时,通过OKG提供的自定义服务质量功能,Thousand引擎平台将感知到具体异常信息并将其通知于运维工程师,运维工程师可快速定位并响应问题,最大程度保证玩家的游戏质量。 + + + +Thousand引擎的诞生标志着冠赢互娱实现了游戏云原生架构升级。经过生产验证,云原生架构带来了以下优势: +1. 在开服效率方面:传统开服时需要进行手动进行各个全服之间的IP端口配置关联,由于是手动配置,故障率也比较高,导致新区开服时间比较长。而在容器化之后,一切参数都标准化、可视化,面对流量高峰可以快速开服保证容器开服的速度和配置的完整性。开新区的时间效率从30分钟优化为15秒;开新服的时间效率从2分钟优化为10秒,极大提高了开服效率。 + +2. 在更新效率方面:传统更新流程会将各个目录文件的可执行文件进行覆盖更新,更新速度慢且出错率高;而在容器化后,引擎与脚本拆分为两个容器,二者可分别定向被更新,更新的粒度更加细致可控,降低了更新错误率,同时通过镜像预热的方式带来了秒级的更新体验,更新效率提高了5倍。 + +3. 在成本节约方面:传统开服时会通过预估人数采购好对应的游戏服服务器配置做资源预留,同时服务需要的资源无法准确隔离,导致服务资源存在较大冗余且资源配置无法及时调整,造成了大量资源浪费;而在容器化后,游戏服相互之间资源隔离,结合精细化调度可以充分利用宿主机资源,资源成本至少节约10%。 + +4. 在问题定位方面:传统手动部署的环境中,经常出现区服崩溃无法及时发现的问题;而容器化后可以直接透出区服具体报错进程,快速定位服务问题并解决,问题响应效率提升5倍。 + +## 云原生游戏展望 +尽管游戏云原生化后硕果累累,但冠赢的云原生化进程仍未结束。冠赢云平台技术负责人盛浩表示,“云原生技术蓬勃发展,未来冠赢将更加全面地拥抱云原生,与OKG社区携手并进,计划引入混沌工程并建立故障自愈体系,进一步加强平台自动化运维能力;通过垂直伸缩动态调配,在保证区服玩家可玩性的同时进一步节约资源成本”。其实未来并不遥远,OKG已经开放了自定义故障定义功能、并支持自动向特定状态容器执行运维脚本的功能;而k8s 1.27版本也引入了原地自动垂直伸缩的能力,对区服类游戏的资源调配意义重大。也许,属于游戏的云原生时代就在我们眼前。 diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/blog-video/higress.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/blog-video/higress.md new file mode 100644 index 0000000000..accffc3113 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/blog-video/higress.md @@ -0,0 +1,118 @@ +# Higress × OpenKruiseGame 游戏网关最佳实践 + +> 作者:赵伟基/刘秋阳/张添翼 +> +> 时间:2024-01-24 + +OpenKruiseGame(OKG)是一个面向多云的开源游戏服 Kubernetes 工作负载,是 CNCF 工作负载开源项目 OpenKruise 在游戏领域的子项目,其提供了热更新、原地升级、定向管理等常用的游戏服管理功能。而游戏作为典型的流量密集型场景,在吞吐量、延迟性能、弹性与安全性等方面对入口网关提出了很高的要求。 + +Higress 是基于阿里内部两年多的 Envoy 网关实践沉淀,以开源 Istio 与 Envoy 为核心构建的下一代云原生网关。Higress 实现了安全防护网关、流量网关、微服务网关三层网关合一,可以显著降低网关的部署和运维成本。Higress 可以作为 K8s 集群的 Ingress 入口网关,并且兼容了大量 K8s Nginx Ingress 的注解,可以从 K8s Nginx Ingress 快速平滑迁移到 Higress。同时也支持 K8s Gateway API 标准,支持用户从 Ingress API 平滑迁移到 Gateway API。 + +本文将演示 Higress 如何无缝对接 OKG 游戏服,并为其带来的优秀特性 + +## Higress 无缝接入 OKG + +前置步骤: +1. [安装 OpenKruiseGame](../installation.md)。 +2. [安装 Higress](https://higress.io/zh-cn/docs/user/quickstart/)。 + +OKG 提供诸多游戏服热更新和游戏服伸缩的优秀特性,便于游戏运维人员管理游戏服的全生命周期。游戏不同于无状态类型的服务,玩家战斗的网络流量是不允许被负载均衡的,因此每一个游戏服需要独立的访问地址。 + +使用原生工作负载(如 Deployment 或 StatefulSet)时,运维工程师需要为众多游戏服一一配置接入层网络,这无疑阻碍了开服效率,同时手动配置也无形中增加了故障的概率。OKG 提供的 GameServerSet 工作负载可以自动化地管理游戏服的接入网络,大幅度降低运维工程师的负担。 + +对于 TCP/UDP 网络游戏,OKG 提供了诸如 HostPort、SLB、NATGW 等网络模型;而对于 H5/WebSocket 类型的网络游戏,OKG 也相应提供了 Ingress 网络模型,如 Higress、Nginx、ALB 等。 + + + +本文采用了一款开源游戏 Posio 来构建 demo 游戏服。下述配置中,IngressClassName="higress" 指定了 Higress 作为游戏服的网络层,Higress 通过下面配置可以无缝接入 Posio 游戏服,并且可以基于 Annotation 实现 Higress 定义的高阶流量治理等功能。示例 Yaml 如下所示,GameServerSet 生成的游戏服对应的访问域名与游戏服 ID 相关。 + +在此例中,游戏服 0 的访问域名为 game0.postio.example.com,游戏服 1 的访问域名为 game1.postio.example.com. 客户端以此来访问不同的游戏服。 + +```yaml +piVersion: game.kruise.io/v1alpha1 +kind: GameServerSet +metadata: + name: postio + namespace: default +spec: + replicas: 1 + updateStrategy: + rollingUpdate: + podUpdatePolicy: InPlaceIfPossible + network: + networkType: Kubernetes-Ingress + networkConf: + - name: IngressClassName + value: "higress" + - name: Port + value: "5000" + - name: Path + value: / + - name: PathType + value: Prefix + - name: Host + value: game.postio.example.com + gameServerTemplate: + spec: + containers: + - image: registry.cn-beijing.aliyuncs.com/chrisliu95/posio:8-24 + name: postio +``` + +OKG [水平伸缩](../user-manuals/gameservers-scale.md)提供自动扩容、根据游戏服的 OpsState 缩容、根据 DeletionPriority 缩容、根据游戏服序号缩容等功能来支持游戏运维的业务需求。水平伸缩的特性在给游戏开发者带来便利的同时,也对入口网关提出了更高的要求:入口网关必须具备配置热更新的能力,完成路由配置的平滑下发。 +原因在于:在进行游戏服的扩容时,OKG 会同步创建 Ingress 等相关网络相关资源,以此来保障游戏服的自动上线。如果入口网关不具备配置热更新的能力,在扩容时就,线上玩家就会遇到连接断开等问题,影响游玩体验。 + + +## Nginx reload 无法优雅热更新 + +在游戏服出现扩容,或者定义的路由策略发生变更时,Nginx 的配置变更会触发 reload,导致上下游的连接都断开并触发重连。 + +我们以 Posio 游戏服为例,模拟 Nginx+OKG 在游戏服扩容时出现的问题。Posio 服务端依赖于 Socket 连接与客户端通信。游戏服扩容时,触发对应的 Ingress 资源创建,此时 Nginx-ingress-controller 监听到 Ingress 资源变更,触发自身 reload 机制,此时原来与游戏服建立的连接(如本例中的 Socket 连接会被断开)。在正在游戏的玩家侧的体感便是出现了异常卡顿。 + +为了直观的展示 Nginx Ingress reload 带来的影响,我们对 Nginx 默认配置参数进行一些更改: + +```shell +kubectl edit configmap nginx-configuration -n kube-system + +data: + ... + worker-shutdown-timeout: 30s # 一个很难做权衡的配置 + +``` + +Nginx 配置参数中 worker-shutdown-timeout 是 Nginx 的 worker 进程优雅下线的超时配置,worker 进程会先停止接收新的连接,并等待老的连接逐渐关闭,达到超时时间后,才会去强制关闭当前的所有连接,完成进程退出。 + +此参数配置过小,会导致大量活跃连接瞬间断开;而此参数配置过大时,又会导致 websocket 长连接始终维持住 Nginx 进程,当发生频繁 reload 时会产生大量 shutting down 状态的 worker 进程,老 worker 占有的内存迟迟得不到释放,可能会导致 OOM 引发线上故障: + + + +实际游玩的测试过程如下:客户端访问游戏服,进行正常游玩。在此过程中通过 OKG 能力触发游戏服扩容,查看此时客户端的响应。通过网页开发者工具可以看到,出现了两条 Socket 连接,一条是原先浏览器访问游戏服建立,另一条是由于 Nginx 断连后重连产生的 Socket 连接。 + + + +原有连接收到的最后一个包时间戳是 15:10:26。 + + + +而新建连接到获取第一个正常游戏包的时间是 15:10:37,网页与游戏服的断连大概持续 5s 左右。 + + + +除了玩家的游玩体验受影响,这个机制也会给业务整体稳定性埋雷。在高并发场景下,因为连接瞬断,导致大批量客户端的并发重连,会导致 Nginx 的 CPU 瞬间飙升;而后端游戏服务器需要处理更多业务逻辑,一般比网关的资源需求更高,因此 Nginx 透传过来的大量并发重连,也更容易打垮后端,造成业务雪崩。 + +## Higress 如何实现优雅热更新 + +Higress 支持采用 K8s Ingress 暴露游戏服外部 IP 端口,供玩家连接访问。当游戏服伸缩或者定义的路由配置发生变化时,Higress 支持路由配置的热更新,以此保障玩家连接的稳定性。 + + + +Higress 基于 Envoy 的精确配置变更管理,做到了真正的配置动态热更新。在 Envoy 中 downstream 对应 listener 配置,交由 LDS 实现配置发现;upstream 对应 cluster 配置,交由 CDS 实现配置发现。listener 配置更新重建,只会导致 downstream 连接断开,不会影响 upstream 的连接;downstream 和 upstream 的配置可以独立变更,互不影响。再进一步,listener 下的证书(cert),过滤器插件(filter),路由(router)均可以实现配置独立变更,这样不论是证书/插件/路由配置变更都不再会引起 downstream 连接断开。 + +精确的配置变更机制,除了让 Envoy 可以实现真正的热更新,也让 Envoy 的架构变的更可靠,Envoy 配置管理从设计之初就是为数据面(DP)和控制面(CP)分离而设计的,因此使用 gRPC 实现远程配置动态拉取,并借助 proto 来规范配置字段,并保持版本兼容。这个设计实现了数据面和控制面的安全域隔离,增强了架构的安全性。 + +使用 OKG 接入 Higress 后,下面依然模拟客户端访问游戏服,进行正常游玩。在此过程中通过 OKG 能力触发游戏服扩容,查看此时客户端的响应。通过网页开发者工具可以看到,在此过程中客户端与游戏服建立的连接稳定不受影响。 + + + +此外,在大规模游戏服场景下,每个游戏服对应一个独立的 Ingress,会产生大量的 Ingress 资源,我们测试在达到 1k 级别规模时,Nginx Ingress 要新扩一个游戏服需要分钟级生效,而 Higress 可以在秒级生效。Nginx Ingress 的这一问题也被 Sealos 踩坑,并最终通过切换到 Higress 解决,有兴趣可以阅读这篇文章了解:[《云原生网关哪家强:Sealos 网关血泪史》](https://mp.weixin.qq.com/s?__biz=MzUzNzYxNjAzMg==&mid=2247561453&idx=1&sn=de22e31a1ab59311072b468de907e282&scene=21#wechat_redirect) + diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/blog-video/kubecon-shangyou-20230926.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/blog-video/kubecon-shangyou-20230926.md new file mode 100644 index 0000000000..d27546bfd8 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/blog-video/kubecon-shangyou-20230926.md @@ -0,0 +1,9 @@ +# 尚游网络基于OpenKruiseGame游戏云原生化实践 + +> KubeCon & CloudNativeCon 2023 +> +> 演讲者:胡炼壮 / 刘秋阳 +> +> 时间:2023-9-26 + + \ No newline at end of file diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/blog-video/meetup-guangzhou-20231125.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/blog-video/meetup-guangzhou-20231125.md new file mode 100644 index 0000000000..39700fb500 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/blog-video/meetup-guangzhou-20231125.md @@ -0,0 +1,9 @@ +# OpenKruiseGame 助力游戏运维管理提效 + +> KubeSphere社区 云原生Meetup 广州站 +> +> 演讲者:刘秋阳 +> +> 时间:2023-11-25 + + \ No newline at end of file diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/blog-video/okg-911.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/blog-video/okg-911.md new file mode 100644 index 0000000000..14d89fe5e3 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/blog-video/okg-911.md @@ -0,0 +1,8 @@ +# 久幺幺科技《百变大侦探》游戏云原生架构落地实践 + +## 架构大图 + + +## 原文链接 + +https://www.aliyun.com/customer-stories/games-2024-911tech \ No newline at end of file diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/blog-video/okg-gsshosting.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/blog-video/okg-gsshosting.md new file mode 100644 index 0000000000..228fd8830c --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/blog-video/okg-gsshosting.md @@ -0,0 +1,8 @@ +# GssHosting 游戏服托管平台实现多地域高效管理 + +## 架构大图 + + +## 原文链接 + +https://www.aliyun.com/customer-stories/games-2024-gss \ No newline at end of file diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/design-concept.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/design-concept.md new file mode 100644 index 0000000000..447e8a508b --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/design-concept.md @@ -0,0 +1,68 @@ +# 设计理念 +## 开源OpenKruiseGame(OKG)的初衷 + +>我是从2015年开始做云原生产品的,从最开始的Swarm到后来的Kubernetes,在容器集群之上运行的负载类型从早期的网站、API服务到后来的转码、AI训练再到元宇宙、Web3、图形化应用。我们见证了云原生技术在改变一个又一个行业。但是,游戏是一个非常特殊的行业,一个大型的游戏,包含网关、平台服、游戏服、匹配服等不同种角色。很多游戏公司早已将平台服、网关等业务进行了云原生化改造,但是,游戏服的容器化进展都比较缓慢。通过和大量的游戏开发者/运维人员进行交流,大致可以归纳为如下三个重要的原因。 +> +>1. 运行中的游戏服更换部署架构的风险收益比过高。 +>2. 游戏服云原生化过程中存在缺失的核心功能,例如:游戏热更新,定向合服/停服等。 +>3. 缺少游戏服云原生化的最佳实践与成功案例。 +> +>为了解决上述问题,我们联合了灵犀互娱等多家游戏公司,将游戏服云原生化场景下的通用能力进行了抽象,开源了OpenKruiseGame项目。希望能够通过一个云厂商无关的开源项目,将游戏服的云原生化最佳实践交付给更多的游戏开发者。同时,我们也希望越来越多的游戏公司/工作室/开发者可以参与到社区,将遇到的难题、场景和大家一起讨论,分享游戏服云原生化的经验。 + +

来自 刘中巍,阿里云容器服务,OpenKruiseGame项目发起人

+ +>灵犀互娱已全面拥抱云原生架构,在云原生化过程中我们清楚地认识到,游戏服不同于其他Web类型应用,在k8s集群之中对其的管理是非常复杂的。原生k8s workload 提供的管理功能很难满足游戏服日常运维需求,Deployment 无法固定ID不适配有状态的特性、而StatefulSet又缺乏定向管理的灵活性,为此我们自研了Paas平台,提供对游戏服的编排管理的能力,以实现高效开服/更新等游戏服运维操作。 + +

来自 冯谋杰 阿里灵犀互娱容器云负责人

+ +>作为一个大型的游戏分发平台,B站有着海量且异构架构的内外部游戏项目需要管理维护,在当前降本增效的大环境下,游戏项目从传统虚拟机迁移至k8s势在必行。但是原生的k8s面对游戏热更、多环境管理、滚服游戏的区服抽象、业务接流等场景是比较疲软的。需要一个成本低廉、高效的跨云解决方案为上述问题提供支持,基于OpenKruise衍生的OpenKruiseGame所提供的固定id、原地升级等功能对游戏场景有着很大的吸引力,给游戏的容器化增加了一种选择。 + +

来自 李宁 bilibili游戏运维负责人

+ + +>在尝试对游戏服进行云原生化改造的过程中,网络是首要考虑的问题。由于游戏服从虚拟机迁移至容器,基于机器IP的运维方式在k8s中难以保障,衍生出固定IP的需求;对外服务的方式也不像直接在虚拟机暴露端口那么简单,增加了许多复杂性。除了网络问题之外,一个游戏服的各个进程在pod中的状态难以感知,原生k8s重建的策略太过“粗暴”,不利于游戏稳定运行,亟需一种针对性的感知策略,针对不同的探测结果执行不同的动作。 + +

来自 盛浩 冠赢互娱游戏云平台负责人

+ +## 为什么OpenKruiseGame(OKG)是一个工作负载 + + + +游戏服云原生化核心要解决两个问题,游戏服的生命周期管理与游戏服的运维管理。Kubernetes内置了一些通用的工作负载模型,例如:无状态(Deployment)、有状态(StatefulSet)、任务(Job)等。但是,游戏服的状态管理不论从粒度还是确定性上面都有更高的要求。例如:游戏服需要热更新的机制来确保更短的游戏中断;游戏服需要原地更新确保元数据信息(网络为主)不变;游戏服需要确保在自动伸缩过程中只有0玩家的游戏服可以下线;需要具备手动运维/诊断/隔离任意一个游戏服的能力等。这些都是Kubernetes内置负载不能够解决的问题。 + +此外,Kubernetes中的工作负载还承担了与基础设施无缝整合的重要枢纽角色。例如:通过Annotations中的字段,自动实现监控系统、日志系统与应用的对接;通过nodeSelector字段,实现应用与底层资源的调度绑定关系;通过labels中的字段,记录分组等元数据信息,替代传统的CMDB系统。这些都让自定义工作负载成为了Kubernetes中适配不同类型应用的最佳方式,OpenKruiseGame(OKG)是一个完全面向游戏场景的Kubernetes工作负载,通过OpenKruiseGame(OKG),开发者不止可以获得更好的游戏服的生命周期管理和游戏服的运维管理,还可以以OpenKruiseGame(OKG)为纽带,无需开发额外的代码,充分发挥云产品带来的强大能力。 + +## OpenKruiseGame(OKG)的设计理念 + +OpenKruiseGame(OKG)只包含两个CRD对象:GameServerSet与GameServer。OpenKruiseGame(OKG)的设计理念是基于状态控制的,将不同的职责划分在不同的工作负载维度来控制。 + +* GameServerSet(生命周期管理) + 对一组GameServer的生命周期管理的抽象,主要用于副本数目管理、游戏服发布等生命周期控制。 + +* GameServer(定向管理运维动作) + 对一个GameServer的运维/管理动作的抽象,主要用于更新顺序控制、游戏服状态控制、游戏服网络变更等定向运维管理动作。 + +当我们理解了OpenKruiseGame(OKG)的设计理念后,一些非常有趣的推论就可以快速的得出,例如: + +* 当不小心删除GameServer的时候会触发游戏服的删除吗? + +不会,GameServer只是游戏服的差异性运维动作的状态记录,如果删除GameServer之后,会重新创建一个使用默认配置的GameServer对象。此时,你的GameServer也会重置为默认定义在GameServerSet中的游戏服模板配置。 + +* 如何让匹配服务与自动伸缩更好的配合防止出现玩家被强制下线? + +可以通过服务质量能力,将游戏的玩家任务转换为GameServer的状态,匹配框架感知GameServer的状态并控制伸缩的副本数目,GameServerSet也会根据GameServer的状态来判断删除的顺序,从而实现优雅下线。 + +## OpenKruiseGame(OKG)的部署架构 + + + +OpenKruiseGame(OKG)的部署模型分为三个部分: + +1. OpenKruiseGame(OKG)控制器 + 负责管理GameServerSet与GameServer的生命周期管理,在OpenKruiseGame控制器中,内置一个Cloud Provider模块,用来适配不同云服务厂商在网络插件等场景下的差异,让OpenKruiseGame可以真正做到一套代码无差异部署。 + +2. OpenKruise控制器 + 负责管理Pod的生命周期管理,是OpenKruiseGame(OKG)的依赖组件,对OpenKruiseGame(OKG)使用者/开发者是无感的。 + +3. OpenKruiseGame(OKG)运维后台【待完成】 + 针对希望白屏化使用OpenKruiseGame(OKG)的开发者提供的运维后台与API,主要提供游戏服的生命周期管理和编排能力。 diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/developer-manuals/contribution.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/developer-manuals/contribution.md new file mode 100644 index 0000000000..6ce30896c4 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/developer-manuals/contribution.md @@ -0,0 +1,29 @@ +# 项目贡献 +欢迎来到 OpenKruiseGame 社区。随时提供帮助、报告问题、提高文档质量、修复错误或引入新功能。有关如何向 OpenKruiseGame 提交内容的详细信息,请参见下文。 + +## 提交问题并参与基于场景的讨论 +OpenKruiseGame 是一个非常开放的社区,随时提交各种类型的问题,以下列表显示了问题类型: +* 错误报告 +* 功能要求 +* 性能问题 +* 功能提案 +* 特征设计 +* 征求帮助 +* 文档不完整 +* 测试改进 +* 关于项目的任何问题 + + +当您提交问题时,请确保您已经进行了数据屏蔽,以确保您的信息的机密性,例如 AccessKey。 +## 贡献代码和文档 +能够为 OpenKruiseGame 提供帮助的行动值得鼓励,您可以提交您希望在拉取请求中修复的内容。 +*如果您发现拼写错误,请更正它。 +* 如果您发现代码错误,请修复它。 +* 如果您发现缺少的单元测试,请解决问题。 +* 如果您发现文档不完整或有错误,请更新它。 + +## 需要额外帮助 +如果您在游戏服务器云原生改造过程中遇到其他类型的问题需要帮助,请发邮件给我们寻求进一步的帮助,邮箱:zhongwei.lzw@alibaba-inc.com + +## 成为 OpenKruiseGame 的核心贡献者 +也非常欢迎大家参与OpenKruiseGame会议,共同决定OpenKruiseGame的未来发展方向.作为OpenKruise的一个子项目,OpenKruiseGame在我们双周会上讨论OpenKruise的时候也会讨论。有关详细信息,请参阅 时间表. \ No newline at end of file diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/developer-manuals/faq.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/developer-manuals/faq.md new file mode 100644 index 0000000000..0914661fc2 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/developer-manuals/faq.md @@ -0,0 +1,25 @@ +# FAQ + +## 如何调试你的代码 + + +0) 编辑Makefile,将IMG字段的值修改为Makefile的仓库地址。 + +1) 编译打包kruise-game-manager镜像。 + +```bash +make docker-build +``` + +2) 将打包后的镜像上传到镜像仓库。 + +```bash +make docker-push +``` + +3) 在 Kubernetes 集群 (~/.kube/conf) 中部署 kruise-game-manager 组件。 + +```bash +make deploy +``` + diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/developer-manuals/go-client.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/developer-manuals/go-client.md new file mode 100644 index 0000000000..861e274279 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/developer-manuals/go-client.md @@ -0,0 +1,157 @@ +# Golang Client + +如果要在一个 Golang 项目中对 OKG 的资源做 create/get/update/delete 这些操作、或者通过 informer 做 list-watch,你需要一个支持 OKG 的 client。 + + +你需要在你的项目中引入 [kruise-game](https://github.com/openkruise/kruise-game) 仓库 + +## 使用方式 + +首先,在你的 `go.mod` 中引入 `kruise-game` 依赖 (版本号最好和你安装的 kruise-game 版本相同): + +``` +require github.com/openkruise/kruise-game v0.7.0 +``` + +使用kruise-game要求Kubernetes版本>= 1.16。 + +### 使用 OKG api + +这里我们使用GameServerSet作为示例,GameServer的使用方法与GameServerSet相同 + +1. 使用您的rest config新建 kruise-game 客户端: + +```go +kruisegameclientset "github.com/openkruise/kruise-game/pkg/client/clientset/versioned" + +// cfg 是在client-go中定义的rest config,你可以使用 kubeconfig 或 serviceaccount 获取 +kruisegameClient := kruisegameclientset.NewForConfigOrDie(cfg) +``` + +2. 查询/列出 kruise-game 资源: + +```go +gamekruiseiov1alpha1 "github.com/openkruise/kruise-game/apis/v1alpha1" + +gameServerSet, err := kruisegameClient.GameV1alpha1().GameServerSets(namespace).Get(context.TODO(), "GameServerSetName", metav1.GetOptions{}) + +// gss 是 GameServerSet 对象 +gssName := gss.GetName() + +// labelSelector用于过滤 GameServer,示例中我们使用GameServerSet名称筛选出归属GameServerSet管理的GameServer,你也可以使用自定义的labelSelector。 +labelSelector := labels.SelectorFromSet(map[string]string{ +gamekruiseiov1alpha1.GameServerOwnerGssKey: gssName, +}).String() + +gameServerList, err := kruisegameclientset.GameV1alpha1().GameServerSets(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: labelSelector}) +``` + +3. 创建/更新 kruise-game resources: + +```go +import gameKruiseV1alpha1 "github.com/openkruise/kruise-game/apis/v1alpha1" + +cloneSet := &gameKruiseV1alpha1.GameServerSet{ + // ... +} +gameServerSet, err = kruisegameclientset.GameV1alpha1().GameServerSet(namespace).Create(context.TODO(), cloneSet, metav1.CreateOptions{}) +``` + +```go + +gameServerSet, err := kruisegameclientset.GameV1alpha1().GameServerSets(namespace).Get(context.TODO(), "GameServerSetName", metav1.GetOptions{}) +if err != nil { + return err +} + +// 修改对象, 例如副本数 +gameServerSet.Spec.Replicas = pointer.Int32Ptr(3) + +newGameServerSet, err := kruisegameclientset.GameV1alpha1().GameServerSets(namespace).Update(context.TODO(), gameServerSet, metav1.UpdateOptions{}) +if err != nil{ +return err +} +``` + +4. 删除现有的GameServerSet: + +```go +// 删除 gss +err := kruisegameclientset.GameV1alpha1().GameServerSets(namespace).Delete(context.TODO(), "GameServerSetName", metav1.DeleteOptions{}) +if err != nil { +return err +} +``` + +5. 监测Kruise-Game资源: + +```go +import gameinformer "github.com/openkruise/kruise-api/client/informers/externalversions" + +gameInformerFactory := gameinformer.NewSharedInformerFactory(kruisegameclientset, 0) +gameInformerFactory.Game().V1alpha1().GameServerSets().Informer().AddEventHandler(...) +gameInformerFactory.Start(...) +``` + +### RABC + +当您的组件部署在k8s集群内部时,您需要赋予组件操作 OKG 资源的权限 + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: okg-role +rules: +- apiGroups: + - game.kruise.io + resources: + - gameserversets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - game.kruise.io + resources: + - gameservers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: okg-sa # 为你的pod设置serviceAccount名字 + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: okg-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: okg-role +subjects: +- kind: ServiceAccount + name: okg-sa + namespace: kube-system + +``` + +## 代码示例 +以下项目都使用了OKG API,开发者可作为示例阅读源码参考: +- https://github.com/CloudNativeGame/aigc-gateway +- https://github.com/CloudNativeGame/kruise-game-open-match-director +- https://github.com/CloudNativeGame/palworld-okg-playground \ No newline at end of file diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/installation.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/installation.md new file mode 100644 index 0000000000..fc1bd8ad8f --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/installation.md @@ -0,0 +1,98 @@ +# 安装 + +## 安装OpenKruiseGame(OKG) + +### 安装说明 + +安装OpenKruiseGame需安装Kruise与Kruise-Game,且要求 Kubernetes版本 >= 1.18。 + +### 安装Kruise + +建议采用 helm v3.5+ 来安装 Kruise。 + +```shell +# Firstly add openkruise charts repository if you haven't do this. +$ helm repo add openkruise https://openkruise.github.io/charts/ +# [Optional] +$ helm repo update +# Install the latest version. +$ helm install kruise openkruise/kruise --version 1.6.3 +``` + +### 安装Kruise-Game + +```shell +$ helm install kruise-game openkruise/kruise-game --version 0.8.0 +``` + +### 升级 Kruise-Game + +```shell +$ helm upgrade kruise-game openkruise/kruise-game --version 0.8.0 [--force] +``` + +### 可选项 + +#### 可选:使用自定义配置安装/升级 + +下表列出了 kruise-game 的可配置参数及其默认值。 + +| Parameter | Description | Default | +|----------------------------------|---------------------------------------------------------|----------------------------------| +| `installation.namespace` | kruise-game 安装到的 namespace,一般不建议修改 | `kruise-game-system` | +| `installation.createNamespace` | 是否需要创建上述 namespace,一般不建议修改,除非指定安装到已有的 ns 中 | `true` | +| `kruiseGame.fullname` | kruise-game 部署和其他配置的名称 | `kruise-game-controller-manager` | +| `kruiseGame.healthBindPort` | 用于检查 kruise-game 容器健康检查的端口 | `8082` | +| `kruiseGame.webhook.port` | kruise-game 容器服务的 webhook 端口 | `443` | +| `kruiseGame.webhook.targetPort` | 用于 MutatingWebhookConfigurations 中工作负载的 ObjectSelector | `9876` | +| `kruiseGame.apiServerQps` | kruise-game-controller-manager 每秒发送到 API server的最大持续查询数 | `5` | +| `kruiseGame.apiServerQpsBurst` | kruise-game-controller-manager 每秒发送到 API server的最大突发查询数 | `10` | +| `replicaCount` | kruise-game 的期望副本数 | `1` | +| `image.repository` | kruise-game 的镜像仓库 | `openkruise/kruise-game-manager` | +| `image.tag` | kruise-game 的镜像版本 | `v0.8.0` | +| `image.pullPolicy` | kruise-game 的镜像拉取策略 | `Always` | +| `serviceAccount.annotations` | kruise-game的serviceAccount注解 | ` ` | +| `resources.limits.cpu` | kruise-game容器的CPU资源限制 | `500m` | +| `resources.limits.memory` | kruise-game容器的内存资源限制 | `1Gi` | +| `resources.requests.cpu` | kruise-game容器的CPU资源请求 | `10m` | +| `resources.requests.memory` | kruise-game容器的内存资源请求 | `64Mi` | +| `prometheus.enabled` | 是否创建指标监控服务 | `true` | +| `prometheus.monitorService.port` | monitorService的监听端口 | `8080` | +| `scale.service.port` | 伸缩服务监听端口 | `6000` | +| `scale.service.targetPort` | 伸缩服务目标端口 | `6000` | +| `network.totalWaitTime` | 等待网络Ready的最长时间,单位是秒 | `60` | +| `network.probeIntervalTime` | 探测网络状态的时间间隔,单位是秒 | `5` | +| `cloudProvider.installCRD` | 是否安装 CloudProvider 相关CRD资源 | `true` | + +使用 `--set key=value[,key=value]` 参数指定每个参数到 `helm install`,例如, + +#### 可选:中国地区的镜像 + +如果你在中国并且无法从官方 DockerHub 拉取镜像,你可以使用托管在阿里云上的镜像: + +```bash +$ helm install kruise-game https://... --set image.repository=registry-cn-hangzhou.ack.aliyuncs.com/acs/kruise-game-manager +``` + +## 卸载OpenKruiseGame(OKG) + +请注意,这将导致删除 kruise-game 创建的所有资源,包括 webhook 配置、服务、命名空间、CRD 和 CR 实例 kruise-game 控制器! +请仅在您完全了解后果后才这样做。 +如果安装了 helm charts,则卸载 kruise-game: + +```bash +$ helm uninstall kruise-game +release "kruise-game" uninstalled +``` + +## 常见问题 + +Q: 出现错误 `no matches for kind "ServiceMonitor" in version "monitoring.coreos.com/v1"` +A: 这是因为集群并没有安装prometheus operator。启用游戏服监控功能需要安装prometheus operator于Kubernetes集群。若您不使用该功能,可以在安装时将 prometheus.enabled 设置为false(默认为true) + +Q: 出现错误 `CustomResourceDefinition "poddnats.alibabacloud.com" in namespace "" exists and cannot be imported into the cureent release` +A: 这是因为在集群中已经安装了该CRD,您可以在安装时将cloudProvider.installCRD设置为false(默认为true) + +## What's Next +接下来,我们推荐你: +- 了解 kruise-game 的 [部署游戏服](user-manuals/deploy-gameservers.md). diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/introduction.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/introduction.md new file mode 100644 index 0000000000..9585c96b85 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/introduction.md @@ -0,0 +1,96 @@ +# OpenKruiseGame简介 +⭐ ***If you like OpenKruiseGame, give it a star on GitHub!*** +## 概览 + +OpenKruiseGame(OKG)是一个面向多云的开源游戏服Kubernetes工作负载,是CNCF工作负载开源项目OpenKruise在游戏领域的子项目,让游戏服的云原生化变得更加简单、快速、稳定。 + + + +## 什么是OpenKruiseGame(OKG) +OpenKruiseGame(OKG)是简化游戏服云原生化的自定义Kubernetes工作负载,相比Kubernetes内置的无状态(Deployment)、有状态(StatefulSet)等工作负载而言,OpenKruiseGame(OKG)提供了热更新、原地升级、定向管理等常用的游戏服管理功能,是完全面向游戏服场景而设计的Kubernetes工作负载。 + +除此之外,OpenKruiseGame(OKG)还承担了游戏服与云服务、匹配服务、运维平台对接的角色,通过低代码或者0代码的方式实现游戏服云原生化时日志、监控、网络、存储、弹性、匹配等功能的自动化集成,通过Kubernetes的一致性交付标准,实现多云/混合云/多集群的统一管理。 + +OpenKruiseGame(OKG)是一个完全开源的项目,开发者可以通过二次开发的方式定制属于自己的游戏服工作负载,构建游戏服的发布运维后台等。除了通过Kubernetes的模板/API的方式进行调用和扩展,OpenKruiseGame(OKG)还支持与KubeVela等交付系统进行对接,通过白屏化的方式实现游戏服的编排与全生命周期管理。 + +## 为什么需要OpenKruiseGame(OKG) + +Kubernetes作为云原生时代的应用交付/运维标准,其具备的声明式资源管理、自动弹性伸缩、多云环境一致性交付等能力与游戏服的场景是非常匹配的,能够在开服效率、成本控制、版本管理、全球同服等场景提供支持。但是,游戏服的一些特性导致了它与Kubernetes进行适配的时候存在一些障碍,例如: + +* 热更新/热重载 + +为了让玩家能够得到更好的游戏体验,很多游戏服都是通过热更新或者配置热重载的方式进行更新,而在Kubernetes的各种不同负载中,Pod的生命周期和镜像的生命周期是一致的,当业务的镜像需要发布的时候,Pod会进行重建,而重建Pod的代价往往意味着玩家对局的中断,玩家服网络元数据的变更等。 + +* 定向运维管理 + +玩家服在大部分的场景下是有状态的,例如PVP游戏在更新或者下线的时候,应该优先且只能变更没有活跃玩家在线的游戏服;PVE游戏在停服或者合服的时候,应该能够定向管理特定ID的玩家服。 + +* 适合游戏的网络模型 + +Kubernetes中的网络模型是通过Service进行抽象的,更多的是面向无状态场景的适配。对于网络敏感的游戏服而言,高性能网关或者IP端口固定的无损直连的方案更符合真实的业务场景。 + +* 游戏服编排 + +当下的游戏服架构越来越复杂,很多MMORPG的玩家服已经抽象成了多种不同功能和用途的游戏服的组合,例如:负责网络接入的网关服、负责游戏引擎的中心服,负责游戏脚本和玩法的策略服等。每个游戏服的容量和管理策略有所不同,通过单一的负载类型很难描述和快速交付。 + +这些能力的缺失,让游戏服进行云原生化变得非常困难。OpenKruiseGame(OKG)设计的初衷就是将这些游戏行业通用的需求进行抽象,通过语义化的方式,将不同类型游戏服的云原生化过程变得简单、高效、安全。 + +## 核心功能列表 + +OpenKruiseGame(OKG)具有如下核心能力: + +* 镜像热更新/配置热重载 +* 定向更新/删除/隔离 +* 内置多种网络模型(IP端口不变/无损直连/全球加速) +* 自动弹性伸缩 +* 自动化运维管理(服务质量) +* 云服务厂商无关 +* 复杂的游戏服务编排 + +## 谁在使用OpenKruiseGame(OKG) + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +## What's Next +接下来,我们推荐你: +* 安装 OpenKruiseGame。有关详细信息,请参阅 [安装](./installation.md)。 +* 为 OpenKruiseGame 提交代码。更多信息,请参见 [开发者指南](./developer-manuals/contribution.md)。 +* 加入钉钉群(ID:44862615)与OpenKruiseGame核心贡献者一起讨论。 +* 通过电子邮件 zhongwei.lzw@alibaba-inc.com 联系我们。 + + diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/container-startup-sequence-control.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/container-startup-sequence-control.md new file mode 100644 index 0000000000..ffe2216d58 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/container-startup-sequence-control.md @@ -0,0 +1,35 @@ +# 容器启动顺序控制 +## 功能概述 + +单个游戏服Pod存在多个容器的情况下,有时候会需要对容器的启动顺序有所要求。OKG提供了自定义顺序启动的功能 + +## 使用示例 + +在GameServerSet.Spec.GameServerTemplate.spec.containers 中添加 KRUISE_CONTAINER_PRIORITY 环境变量: + +``` +apiVersion: game.kruise.io/v1alpha1 +kind: GameServerSet + +# ... + +spec: + gameServerTemplate: + spec: + containers: + - name: main + # ... + - name: sidecar + env: + - name: KRUISE_CONTAINER_PRIORITY + value: "1" + +# ... + +``` + +- 值的范围在 [-2147483647, 2147483647],不写默认是 0。 +- 权重高的容器,会保证在权重低的容器之前启动。 +- 相同权重的容器不保证启动顺序。 + +上述例子中游戏服启动时由于sidecar权重更高,所以先启动sidecar容器,再启动main容器 \ No newline at end of file diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/crd-field-description.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/crd-field-description.md new file mode 100644 index 0000000000..54400305c8 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/crd-field-description.md @@ -0,0 +1,288 @@ +# CRD字段说明 +## GameServerSet + +### GameServerSetSpec + +``` +type GameServerSetSpec struct { + // 游戏服数目,必须指定,最小值为0 + Replicas *int32 `json:"replicas"` + + // 游戏服模版,新生成的游戏服将以模版定义的参数创建 + GameServerTemplate GameServerTemplate `json:"gameServerTemplate,omitempty"` + + // serviceName 是管理此 GameServerSet 的服务的名称。 + // 该服务必须在GameServerSet之前存在,并负责该集合的网络标识。 + // Pod 获取遵循以下模式的 DNS/主机名:pod-specific-string.serviceName.default.svc.cluster.local + // 其中“pod-specific-string”由 GameServerSet 控制器管理。 + ServiceName string `json:"serviceName,omitempty"` + + // 保留的游戏服序号,可选项。若指定了该序号,已经存在的游戏服将被删除;而未存在的游戏服,新建时将跳过、不创建该序号 + ReserveGameServerIds []int `json:"reserveGameServerIds,omitempty"` + + // 游戏服自定义服务质量。用户通过该字段实现游戏服自动化状态感知。 + ServiceQualities []ServiceQuality `json:"serviceQualities,omitempty"` + + // 游戏服批量更新策略 + UpdateStrategy UpdateStrategy `json:"updateStrategy,omitempty"` + + // 游戏服水平伸缩策略 + ScaleStrategy ScaleStrategy `json:"scaleStrategy,omitempty"` + + // 游戏服接入层网络设置 + Network *Network `json:"network,omitempty"` +} +``` + +#### GameServerTemplate + +``` +type GameServerTemplate struct { + // 继承至PodTemplateSpec的所有字段,字段详情参考 https://pkg.go.dev/k8s.io/api/core/v1#PodTemplateSpec + corev1.PodTemplateSpec `json:",inline"` + + // 对持久卷的请求和声明 + VolumeClaimTemplates []corev1.PersistentVolumeClaim `json:"volumeClaimTemplates,omitempty"` + + // ReclaimPolicy 表明GameServer的回收策略 + // 当前支持两种,Cascade与Delete。默认为Cascade + ReclaimPolicy GameServerReclaimPolicy `json:"reclaimPolicy,omitempty"` +} + +type GameServerReclaimPolicy string + +const ( + // Cascade 表明pod删除时GameServer一并删除。GameServer的生命周期与pod相同 + CascadeGameServerReclaimPolicy GameServerReclaimPolicy = "Cascade" + + // Delete 表明 GameServers 只会在GameServerSet副本数目减少时被删除。 + // 当对应的pod被手动删除、更新重建、被驱逐时,GameServer都不会被删除。 + DeleteGameServerReclaimPolicy GameServerReclaimPolicy = "Delete" +) +``` + +#### UpdateStrategy + +``` +type UpdateStrategy struct { + // 更新策略类型,可选择 OnDelete 或 RollingUpdate + Type apps.StatefulSetUpdateStrategyType `json:"type,omitempty"` + + // 当策略类型为RollingUpdate时可用,指定RollingUpdate具体策略 + RollingUpdate *RollingUpdateStatefulSetStrategy `json:"rollingUpdate,omitempty"` +} + + +type RollingUpdateStatefulSetStrategy struct { + // 保留旧版本游戏服的数量或百分比,默认为 0。 + Partition *int32 `json:"partition,omitempty"` + + + // 会保证发布过程中最多有多少个游戏服处于不可用状态,默认值为 1。 + // 支持设置百分比,比如:20%,意味着最多有20%个游戏服处于不可用状态。 + MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty"` + + // 表明游戏服更新的方式。可选择ReCreate / InPlaceIfPossible / InPlaceOnly。默认为ReCreate。 + PodUpdatePolicy kruiseV1beta1.PodUpdateStrategyType `json:"podUpdatePolicy,omitempty"` + + // 是否暂停发布,默认为false。 + Paused bool `json:"paused,omitempty"` + + // 原地升级的策略 + InPlaceUpdateStrategy *appspub.InPlaceUpdateStrategy `json:"inPlaceUpdateStrategy,omitempty"` + + // 游戏服在更新后多久被视为准备就绪,默认为0,最大值为300。 + MinReadySeconds *int32 `json:"minReadySeconds,omitempty"` +} + +type InPlaceUpdateStrategy struct { + // 将游戏服状态设置为NotReady和更新游戏服Spec中的镜像之间的时间跨度。 + GracePeriodSeconds int32 `json:"gracePeriodSeconds,omitempty"` +} +``` + +#### ScaleStrategy + +``` +type ScaleStrategy struct { + // 扩缩期间游戏服最大不可用的数量,可为绝对值或百分比 + MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty"` + + // 缩容策略类型,目前支持两种:General 与 ReserveIds。 + // 默认为General,缩容时优先考虑reserveGameServerIds字段, + // 当预留的GameServer数量不满足缩减数量时,继续从当前游戏服务器列表中选择并删除GameServer。 + // 当该字段设置为ReserveIds时,无论是保留的游戏服还是控制器按照优先级删除的游戏服, + // 被删除的游戏服的序号都会回填至ReserveGameServerIds字段。 + ScaleDownStrategyType ScaleDownStrategyType `json:"scaleDownStrategyType,omitempty"` +} + +``` + +#### ServiceQualities + +``` +type ServiceQuality struct { + // 继承至corev1.Probe所有字段,此处指定探测方式,字段详情参考 https://pkg.go.dev/k8s.io/api/core/v1#Probe + corev1.Probe `json:",inline"` + + // 自定义服务质量的名称,区别定义不同的服务质量 + Name string `json:"name"` + + // 探测的容器名称 + ContainerName string `json:"containerName,omitempty"` + + // 是否让GameServerSpec在ServiceQualityAction执行后不发生变化。 + // 当Permanent为true时,无论检测结果如何,ServiceQualityAction只会执行一次。 + // 当Permanent为false时,即使ServiceQualityAction已经执行过,也可以再次执行ServiceQualityAction。 + Permanent bool `json:"permanent"` + + // 服务质量对应执行动作 + ServiceQualityAction []ServiceQualityAction `json:"serviceQualityAction,omitempty"` +} + +type ServiceQualityAction struct { + // 用户设定当探测结果为true/false时执行动作 + State bool `json:"state"` + + // Result为对应探测脚本返回的信息。当Result被指定时,只有当探测的结果符合该声明的结果时action才会执行 + Result string `json:"result,omitempty"` + + // 动作为更改GameServerSpec中的字段 + GameServerSpec `json:",inline"` +} +``` + +#### Network + +``` +type Network struct { + // 网络类型 + NetworkType string `json:"networkType,omitempty"` + + // 网络参数,不同网络类型需要填写不同的网络参数 + NetworkConf []NetworkConfParams `json:"networkConf,omitempty"` +} + +type NetworkConfParams KVParams + +type KVParams struct { + // 参数名,名称由网络插件决定 + Name string `json:"name,omitempty"` + + // 参数值,格式由网络插件决定 + Value string `json:"value,omitempty"` +} +``` + +### GameServerSetStatus + +``` +type GameServerSetStatus struct { + // 控制器观察到GameServerSet的迭代版本 + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // 游戏服数目 + Replicas int32 `json:"replicas"` + + // 处于Ready的游戏服数目 + ReadyReplicas int32 `json:"readyReplicas"` + + // 可用的游戏服数目 + AvailableReplicas int32 `json:"availableReplicas"` + + // 当前的游戏服数目 + CurrentReplicas int32 `json:"currentReplicas"` + + // 已更新的游戏服数目 + UpdatedReplicas int32 `json:"updatedReplicas"` + + // 已更新并Ready的游戏服数目 + UpdatedReadyReplicas int32 `json:"updatedReadyReplicas,omitempty"` + + // 处于Maintaining状态的游戏服数目 + MaintainingReplicas *int32 `json:"maintainingReplicas,omitempty"` + + // 处于WaitToBeDeleted状态的游戏服数目 + WaitToBeDeletedReplicas *int32 `json:"waitToBeDeletedReplicas,omitempty"` + + // LabelSelector 是标签选择器,用于查询应与 HPA 使用的副本数相匹配的游戏服。 + LabelSelector string `json:"labelSelector,omitempty"` +} +``` + +## GameServer + +### GameServerSpec + +``` +type GameServerSpec struct { + // 游戏服运维状态,表示业务相关的游戏服状态,可以由用户定义为任何值。 + // OKG提供了一些保留值,有着特殊含义,包括: + // None —— 默认值,代表不存在任何异常和特殊状态 + // WaitToBeDeleted —— gs缩容优先级最高,且配置自动伸缩策略后会被自动回收 + // Maintaining —— gs缩容优先级最低 + // Allocated —— gs缩容优先级大于Maintaining,小于None。通常代表gs已经被分配,在游戏匹配场景下可以使用。 + // Kill —— 设置Kill的gs将被OKG控制器直接删除 + OpsState OpsState `json:"opsState,omitempty"` + + // 更新优先级,优先级高则优先被更新 + UpdatePriority *intstr.IntOrString `json:"updatePriority,omitempty"` + + // 删除优先级,优先级高则优先被删除 + DeletionPriority *intstr.IntOrString `json:"deletionPriority,omitempty"` + + // 是否进行网络隔离、切断接入层网络,默认为false + NetworkDisabled bool `json:"networkDisabled,omitempty"` + + // 使对应的GameServer Containers字段与GameServerSetSpec中GameServerTemplate定义的字段不同,意味着该GameServer可以拥有独立的参数配置。 + // 当前支持更改 Image 与 Resources + Containers []GameServerContainer `json:"containers,omitempty"` +} + +type GameServerContainer struct { + // Name 表示要更新的容器的名称。 + Name string `json:"name"` + + // Image 表示要更新的容器的镜像。 + // 当Image更新时,pod.spec.containers[*].image会立即更新。 + Image string `json:"image,omitempty"` + + // Resources 表示要更新的容器的资源。 + // 当Resources更新时,pod.spec.containers[*].Resources不会立即更新,它会在pod重建时更新。 + Resources corev1.ResourceRequirements `json:"resources,omitempty"` +} +``` + +### GameServerStatus + +``` +type GameServerStatus struct { + // 期望游戏服状态,Ready + DesiredState GameServerState `json:"desiredState,omitempty"` + + // 当前游戏服实际状态 + CurrentState GameServerState `json:"currentState,omitempty"` + + // 网络状态信息 + NetworkStatus NetworkStatus `json:"networkStatus,omitempty"` + + // 游戏服对应pod状态 + PodStatus corev1.PodStatus `json:"podStatus,omitempty"` + + // 游戏服服务质量状况 + ServiceQualitiesCondition []ServiceQualityCondition `json:"serviceQualitiesConditions,omitempty"` + + // 与该GameServer相关的Condition集合 + // 当前支持聚合 pod Conditions / node Conditions / pv Conditions + Conditions []GameServerCondition `json:"conditions,omitempty" ` + + // 当前更新优先级 + UpdatePriority *intstr.IntOrString `json:"updatePriority,omitempty"` + + // 当前删除优先级 + DeletionPriority *intstr.IntOrString `json:"deletionPriority,omitempty"` + + // 上次变更时间 + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` +} +``` \ No newline at end of file diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/deploy-gameservers.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/deploy-gameservers.md new file mode 100644 index 0000000000..380c206514 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/deploy-gameservers.md @@ -0,0 +1,191 @@ +# 部署游戏服 + +## "Hello World" of OKG +您可以使用GameServerSet进行游戏服的部署,一个简单的部署案例如下: + +```bash +cat < 2/2 +minecraft-1 1/1 Running 0 10s 172.16.0.6 xxx 2/2 +minecraft-2 1/1 Running 0 10s 172.16.0.12 xxx 2/2 +``` + +### DNS + +为了使游戏服的pod能够单独被访问,除了部署GameServerSet之外,还需部署与GameServerSet同名称的headless service,在本例中其Yaml如下: + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: minecraft +spec: + clusterIP: None # 设置为 None 使得服务成为 Headless + selector: + game.kruise.io/owner-gss: minecraft # 填写GameServerSet的名称 +``` + +部署一个简单的accessor Yaml,目的是在该容器内访问对应的minecraft pod + +```yaml +apiVersion: game.kruise.io/v1alpha1 +kind: GameServerSet +metadata: + name: accessor + namespace: default +spec: + replicas: 1 + gameServerTemplate: + spec: + containers: + - image: busybox + name: accessor + args: + - sleep + - "3600" + command: ["/bin/sh", "-c", "sleep 3600"] +``` + +进入accessor容器中ping对应的minecraft pod(这一步模拟真实环境中的访问逻辑,当然选择访问哪一个pod需要一定的筛选规则): + +```bash +kubectl exec -it accessor-0 /bin/sh +/ # +/ # ping minecraft-2.minecraft.default.svc.cluster.local +PING minecraft-2.minecraft.default.svc.cluster.local (172.16.0.12): 56 data bytes +64 bytes from 172.16.0.12: seq=0 ttl=63 time=0.082 ms +64 bytes from 172.16.0.12: seq=1 ttl=63 time=0.061 ms +64 bytes from 172.16.0.12: seq=2 ttl=63 time=0.072 ms +``` + +可以发现,accessor访问minecraft-2成功,DNS成功解析到对应的内网IP地址。在这里的DNS访问规则如下:{pod-name}.{gss-name}.{namespace-name}.svc.cluster.local + +## GameServer 与 Pod 注释同步 + +如上所述,通过 DownwardAPI 可以将 pod annotation的信息下沉至容器中。我们有时希望将 GameServer 的 annotation 可以同步到 Pod 上,以完成GameServer元数据信息的下沉动作。 + +OKG 支持以 "gs-sync/" 开头的 annotation 从 GameServer 同步到 Pod 之上,如下所示: + +```bash +kubectl patch gs minecraft-0 --type='merge' -p '{"metadata":{"annotations":{"gs-sync/test-key":"some-value"}}}' +gameserver.game.kruise.io/minecraft-0 patched +``` + +此时查看 pod 注释,发现可以找到对应key-value: + +```bash +kubectl get po minecraft-0 -oyaml | grep gs-sync + gs-sync/test-key: some-value +``` \ No newline at end of file diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/game-dashboard.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/game-dashboard.md new file mode 100644 index 0000000000..afaea31b10 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/game-dashboard.md @@ -0,0 +1,148 @@ +# 游戏服运维控制台 + +OpenKruiseGame基于KubeSphere 4.0 LuBan架构提供了游戏服白屏化管理控制台。本文介绍如何安装KubeSphere 与 OKG 游戏服运维控制台,以及对应的使用说明。 + +当前OKG Dashboard版本:0.1.0 + + + +## 安装KubeSphere 与 OKG Dashboard + +### 安装KubeSphere 4.0 + +通过helm安装: + +``` +helm upgrade --install -n kubesphere-system --create-namespace ks-core https://charts.kubesphere.io/main/ks-core-0.4.0.tgz +``` + +显示以下信息,则为安装成功: + +``` +Release "ks-core" does not exist. Installing it now. + +NAME: ks-core +LAST DEPLOYED: Wed Dec 20 19:59:19 2023 +NAMESPACE: kubesphere-system +STATUS: deployed +REVISION: 1 +TEST SUITE: None +NOTES: +Please wait for several seconds for KubeSphere deployment to complete. + +1. Make sure KubeSphere components are running: + + kubectl get pods -n kubesphere-system + +2. Then you should be able to visit the console NodePort: + + Console: http://xxx.xx.x.xx:30880 + +3. To login to your KubeSphere console: + + Account: admin + Password: "P@88w0rd" + NOTE: Please change the default password after login. + +For more details, please visit https://kubesphere.io. +``` + +默认情况下,ks-console暴露方式是nodeport,若希望更改暴露方式,则安装后编辑对应svc,如更改为LoadBalancer: +``` +kubectl edit svc ks-console -n kubesphere-system +... + type: LoadBalancer +... +``` + +有关KubeSphere更多安装说明,请参考:https://docs.kubesphere.com.cn/v4.0/03-install-and-uninstall/01-install-ks-core + +### 安装OKG Dashboard + +安装KubeSphere完成后,访问控制台,点击扩展市场: + +![](/img/kruisegame/user-manuals/dash-mainpage.png) + +选择OKG Dashboard,点击订阅。OKG Dashboard完全免费,提交订单即可。支付成功后返回扩展市场,此时订阅按钮已转变为管理按钮: + +![](/img/kruisegame/user-manuals/dash-okg.png) + +进入OKG Dashboard管理页面,点击安装: + +![](/img/kruisegame/user-manuals/dash-management.png) + +根据弹出的窗口,依次 1)选择版本点击下一步;2)开始安装: + +![](/img/kruisegame/user-manuals/dash-version-select.png) + +![](/img/kruisegame/user-manuals/dash-begin-install.png) + +安装成功后,可以看到界面显示已安装,并处于启用状态: + +![](/img/kruisegame/user-manuals/dash-installed.png) + +## 使用说明 + +OKG Dashboard 为集群级别组件。选择要操作的集群,进入后,看到左边导航栏,点击“游戏服运维管理”: + +![](/img/kruisegame/user-manuals/dash-ops.png) + +### 概览页 + +点击“游戏服运维管理”后,默认进入概览页。概览页统计了当前集群中游戏服处于不同状态的数量 + +![](/img/kruisegame/user-manuals/dash-overview.jpg) + +字段说明 + +- 总数:当前集群中,GameServer的总计数量 +- 正在创建:当前集群中,State 为 Creating 的 GameServer数量 +- 正在更新:当前集群中,State 为 Updating 的 GameServer数量 +- 正在删除:当前集群中,State 为 Deleting 的 GameServer数量 +- Ready:当前集群中,State 为 Ready 的 GameServer数量 +- NotReady:当前集群中,State 为 NotReady 的 GameServer数量 +- 使用OKG网络:当前集群中,使用了OKG网络模型 的 GameServer数量 +- 网络Ready:当前集群中,使用了OKG网络模型 且 NetworkState 为 Ready 的 GameServer数量 +- 默认运维状态:当前集群中,opsState 为 None 的 GameServer数量 +- 已被分配:当前集群中,opsState 为 Allocated 的 GameServer数量 +- 待删除:当前集群中,opsState 为 WaitToBeDeleted 的 GameServer数量 +- 正在维护:当前集群中,opsState 为 Maintaining 的 GameServer数量 + + +### 游戏服部署集列表页 + +点击“游戏服部署集”,查看当前集群所有GameServerSet + +![](/img/kruisegame/user-manuals/dash-gss.png) + +字段说明 +- 模版镜像:GameServerTemplate设置的Image。格式为: {容器名称} -> {镜像名称及版本} +- 模版资源配置:GameServerTemplate设置的Resources。格式为: {容器名称} -> { cpu request / mem request / cpu limit / mem limit },留白意味着未设置对应字段。 + +操作 +- 跳转资源页查看详情 + ![](/img/kruisegame/user-manuals/dash-gss-jump.png) + 在GameServerSet详情页,可以编辑对应Yaml,或者删除对应对象: + ![](/img/kruisegame/user-manuals/dash-gss-ops.png) + + +### 游戏服列表页 + +点击“游戏服”,查看当前集群所有GameServer + +![](/img/kruisegame/user-manuals/dash-gs.png) + +字段说明 + +- 运行镜像:当前游戏服运行的镜像及版本,可能与对应GameServerSet的GameServerTemplate设置的镜像不同。格式为: {容器名称} -> {镜像名称及版本} +- 游戏服异常情况:GameServerStatus Condition State 为 False 的情况将在此显示。 + +操作 +- 跳转资源页查看详情 + ![](/img/kruisegame/user-manuals/dash-gs-jump.png) + 在GameServer详情页,可以编辑对应Yaml,或者删除对应对象: + ![](/img/kruisegame/user-manuals/dash-gs-ops.png) +- 更新运维状态 + ![](/img/kruisegame/user-manuals/dash-gs-update-opsState.png) + 弹窗显示后,输入希望更改的opsState,点击OK即可更新: + ![](/img/kruisegame/user-manuals/dash-gs-opsState-updated.png) diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/game-matchmaking.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/game-matchmaking.md new file mode 100644 index 0000000000..3e19b6e1b8 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/game-matchmaking.md @@ -0,0 +1,16 @@ +# 游戏匹配 + +会话类游戏通常需要`匹配服务`让玩家找到合适的队友及对手组成对局,并为该对局分配合适的游戏服。组成对局的玩家拿到游戏服地址后方可进入游戏。 + +OKG支持云原生游戏匹配框架[Open Match](https://github.com/googleforgames/open-match), +并基于Open Match构建了[kruise-game-open-match-director](https://github.com/CloudNativeGame/kruise-game-open-match-director)组件, +为形成对局的玩家分配游戏服地址。 + +## 使用说明 + +- Kubernetes集群中需要安装 `OpenKruiseGame` 、 `Open Match` 以及 `kruise-game-open-match-director` +- 被GameServerSet管理且待匹配的游戏服需要配置Network字段,使游戏服具备直连网络。详细可参考[网络功能文档](./network.md) +- kruise-game-open-match-director 将选择网络可用且OpsState为None的游戏服,获取对应网络连接信息,分配予Match中的Tickets。 +- kruise-game-open-match-director 将已分配的GameServer对应的OpsState字段标记为Allocated,此时该GameServer不会再被分配,且水平缩容时优先级较低,避免被轻易删除。游戏服具体缩容顺序可参考[游戏服伸缩文档](./gameservers-scale.md#openkruisegame的水平伸缩特性) +- kruise-game-open-match-director 更多功能请参考[GitHub](https://github.com/CloudNativeGame/kruise-game-open-match-director) +- 关于OKG + Open Match更多示例请参考[GitHub](https://github.com/CloudNativeGame/kruise-game-open-match-example) \ No newline at end of file diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/gameserver-monitor.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/gameserver-monitor.md new file mode 100644 index 0000000000..81d2419f4f --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/gameserver-monitor.md @@ -0,0 +1,35 @@ +# 游戏服监控 +## 可用指标 + +OKG 默认透出游戏服相关 prometheus metrics,其中指标包括: + +| 名称 | 描述 | 类型 | +| --- |----------------------|---------| +| GameServersStateCount | 不同state状态下的游戏服数量 | gauge | +| GameServersOpsStateCount | 不同opsState状态下的游戏服数量 | gauge | +| GameServersTotal | 存在过的游戏服总数 | counter | +| GameServerSetsReplicasCount | 每个GameServerSet的副本数量 | gauge | +| GameServerDeletionPriority | 游戏服删除优先级 | gauge | +| GameServerUpdatePriority | 游戏服更新优先级 | gauge | + +## 监控仪表盘 + +### 仪表盘导入 + +1. 将 [grafana.json](https://github.com/openkruise/kruise-game/blob/master/config/prometheus/grafana.json) 导入至Grafana中 +2. 选择数据源 +3. 替换UID并完成导入 + +### 仪表盘说明 + +完成导入后的仪表盘如下所示: + + + +从上至下,依次包含 + +- 第一行:当前游戏服各个状态的数量、当前游戏服各个状态的比例饼图 +- 第二行:游戏服各个状态数量变化折线图 +- 第三行:游戏服删除优先级、更新优先级变化折线图(可根据左上角namespace与gsName筛选游戏服) +- 第四、五行:游戏服集合中不同状态的游戏服数量变化折线图(可根据左上角namespace与gssName筛选游戏服集合) + diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/gameservers-scale.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/gameservers-scale.md new file mode 100644 index 0000000000..8acd4b8855 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/gameservers-scale.md @@ -0,0 +1,513 @@ +# 游戏服伸缩 +## OpenKruiseGame的水平伸缩特性 + +### 缩容顺序 + +OKG提供游戏服状态设置的能力,您可以手动/自动(服务质量功能)地设置游戏服的运维状态或删除优先级。当缩容时,GameServerSet负载会根据游戏服的状态进行缩容选择,缩容规则如下: + +1)根据游戏服的opsState缩容。按顺序依次缩容opsState为`WaitToBeDeleted`、`None`、`Allocated`、`Maintaining`的游戏服 + +2)当opsState相同时,按照DeletionPriority(删除优先级)缩容,优先删除DeletionPriority大的游戏服 + +3)当opsState与DeletionPriority都相同时,优先删除名称尾部序号较大的游戏服 + +#### 示例 + +部署一个副本为5的游戏服: + +```bash +cat < + +在游戏场景下,它的主要问题在于: + +- 在pod层面,无法感知游戏服业务状态,进而无法通过业务状态设置删除优先级 +- 在workload层面,无法根据业务状态选择缩容对象 +- 在autoscaler层面,无法定向感知游戏服业务状态计算合适的副本数目 + +这样一来,基于原生Kubernetes的自动伸缩机制将在游戏场景下造成两大问题: + +- 缩容数目不精确。容易删除过多或过少的游戏服。 +- 缩容对象不精确。容易删除业务负载水平高的游戏服。 + +OKG 的自动伸缩机制如下所示 + + + +- 在游戏服层面,每个游戏服可以上报自身状态,通过自定义服务质量或外部组件来暴露自身是否为WaitToBeDeleted状态。 +- 在workload层面,GameServerSet可根据游戏服上报的业务状态来决定缩容的对象,如[游戏服水平伸缩](gameservers-scale.md)中所述,WaitToBeDeleted的游戏服是删除优先级最高的游戏服,缩容时最优先删除。 +- 在autoscaler层面,精准计算WaitToBeDeleted的游戏服个数,将其作为缩容数量,不会造成误删的情况。 + +如此一来,OKG的自动伸缩器在缩容窗口期内只会删除处于WaitToBeDeleted状态的游戏服,真正做到定向缩容、精准缩容。 + +**使用示例如下** + +_**前置条件:在集群中安装 [KEDA](https://keda.sh/docs/2.10/deploy/)**_ + +部署ScaledObject对象来设置自动伸缩策略,具体字段含义可参考 [ScaledObject API](https://github.com/kedacore/keda/blob/main/apis/keda/v1alpha1/scaledobject_types.go) + +```yaml +apiVersion: keda.sh/v1alpha1 +kind: ScaledObject +metadata: + name: minecraft #填写对应GameServerSet的名称 +spec: + scaleTargetRef: + name: minecraft #填写对应GameServerSet的名称 + apiVersion: game.kruise.io/v1alpha1 + kind: GameServerSet + pollingInterval: 30 + minReplicaCount: 0 + advanced: + horizontalPodAutoscalerConfig: + behavior: #继承HPA策略,可参考文档 https://kubernetes.io/zh-cn/docs/tasks/run-application/horizontal-pod-autoscale/#configurable-scaling-behavior + scaleDown: + stabilizationWindowSeconds: 45 #设置缩容稳定窗口时间为45秒 + policies: + - type: Percent + value: 100 + periodSeconds: 15 + triggers: + - type: external + metricType: AverageValue + metadata: + scalerAddress: kruise-game-external-scaler.kruise-game-system:6000 +``` + +部署完成后,更改gs minecraft-0 的 opsState 为 WaitToBeDeleted(可参考[自定义服务质量](service-qualities.md)实现自动化设置游戏服状态) + +```bash +kubectl edit gs minecraft-0 + +... +spec: + deletionPriority: 0 + opsState: WaitToBeDeleted #初始为None, 将其改为WaitToBeDeleted + updatePriority: 0 +... +``` + +经过缩容窗口期后,游戏服minecraft-0自动被删除 +```bash +kubectl get gs +NAME STATE OPSSTATE DP UP +minecraft-0 Deleting WaitToBeDeleted 0 0 +minecraft-1 Ready None 0 0 +minecraft-2 Ready None 0 0 + +# After a while + + +kubectl get gs +NAME STATE OPSSTATE DP UP +minecraft-1 Ready None 0 0 +minecraft-2 Ready None 0 0 +``` + +### 自动扩容 + +除了设置自动缩容策略,也可以设置自动扩容策略。 + +#### 利用资源指标或自定义指标进行扩容 + +例如,原生Kubernetes支持使用CPU利用率进行扩容,其完整的yaml如下 + +```yaml +apiVersion: keda.sh/v1alpha1 +kind: ScaledObject +metadata: + name: minecraft #填写对应GameServerSet的名称 +spec: + scaleTargetRef: + name: minecraft #填写对应GameServerSet的名称 + apiVersion: game.kruise.io/v1alpha1 + kind: GameServerSet + pollingInterval: 30 + minReplicaCount: 0 + advanced: + horizontalPodAutoscalerConfig: + behavior: #继承HPA策略,可参考文档 https://kubernetes.io/zh-cn/docs/tasks/run-application/horizontal-pod-autoscale/#configurable-scaling-behavior + scaleDown: + stabilizationWindowSeconds: 45 #设置缩容稳定窗口时间为45秒 + policies: + - type: Percent + value: 100 + periodSeconds: 15 + triggers: + - type: external + metricType: AverageValue + metadata: + scalerAddress: kruise-game-external-scaler.kruise-game-system:6000 + - type: cpu + metricType: Utilization # 允许的类型是 "利用率 "或 "平均值" + metadata: + value: "50" +``` + +对游戏服进行压测,可以看到游戏服开始扩容 + +```bash +kubectl get gss +NAME DESIRED CURRENT UPDATED READY MAINTAINING WAITTOBEDELETED AGE +minecraft 5 5 5 0 0 0 7s + +# After a while + +kubectl get gss +NAME DESIRED CURRENT UPDATED READY MAINTAINING WAITTOBEDELETED AGE +minecraft 20 20 20 20 0 0 137s +``` + +#### 设置opsState为None的游戏服的最小个数 + +OKG支持设置游戏服最小数目。在当前所有opsState为None的游戏服数量少于设置的值时,OKG将自动扩容出新的游戏服,使opsState为None的游戏服数量满足设置的最小个数。 + +配置方式如下,在此例中设置opsState为None的游戏服的最小个数为3: + +```yaml +apiVersion: keda.sh/v1alpha1 +kind: ScaledObject +metadata: + name: minecraft #填写对应GameServerSet的名称 +spec: + scaleTargetRef: + name: minecraft #填写对应GameServerSet的名称 + apiVersion: game.kruise.io/v1alpha1 + kind: GameServerSet + pollingInterval: 30 + minReplicaCount: 0 + advanced: + horizontalPodAutoscalerConfig: + behavior: #继承HPA策略,可参考文档 https://kubernetes.io/zh-cn/docs/tasks/run-application/horizontal-pod-autoscale/#configurable-scaling-behavior + scaleDown: + stabilizationWindowSeconds: 45 #设置缩容稳定窗口时间为45秒 + policies: + - type: Percent + value: 100 + periodSeconds: 15 + triggers: + - type: external + metricType: AverageValue + metadata: + minAvailable: "3" # 设置opsState为None的游戏服的最小个数 + scalerAddress: kruise-game-external-scaler.kruise-game-system:6000 +``` + +初始部署replicas为1的GameServerSet,经过KEDA探测周期后,马上扩容出两个新的游戏服。此时opsState为None的游戏服数量不小于设置的minAvailable值,完成了自动扩容。 + +```bash +kubectl get gs +NAME STATE OPSSTATE DP UP AGE +minecraft-0 Ready None 0 0 7s + +# After a while + +kubectl get gs +NAME STATE OPSSTATE DP UP AGE +minecraft-0 Ready None 0 0 20s +minecraft-1 Ready None 0 0 5s +minecraft-2 Ready None 0 0 5s +``` + +### 其他设置 + +Kubernetes对于自动伸缩行为具备一定容忍度,该值由kube-controller-manager 参数 --horizontal-pod-autoscaler-tolerance 决定,默认为0.1,这意味着理想副本数与当前副本数的差值在10%以内时不会触发扩容或缩容。 +如果做到更加精准地自动伸缩,可以调低该参数,例如设置0.0时,OKG将会缩容所有WaitToBeDeleted的游戏服。 \ No newline at end of file diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/hot-update.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/hot-update.md new file mode 100644 index 0000000000..732de4070a --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/hot-update.md @@ -0,0 +1,345 @@ +# 游戏服热更新 + +游戏服更新是游戏服应用交付中尤为重要的一环。作为有状态类型业务,游戏服的更新往往对云原生基础设施有着更高的要求。本文主要介绍如何利用OKG的原地升级能力实现游戏服热更新。 + +## 游戏服与容器 + +在介绍热更方法之前,或许我们需要先明确游戏服与容器的关系。在OKG的概念里,一个游戏服(GameServer)中可以包含多个容器,每个容器功能作用不尽相同,各自对应不同的容器镜像。当然,一个游戏服也可以只包含一个容器。游戏服包含一个容器、还是包含多个容器对应着两种不同的架构思想。 + +单容器的游戏服,更贴近虚拟机的运维管理方式。无论是状态的管理、或者小版本的热更都不借助Kubernetes的能力,沿用过去的运维方式进行。比如,游戏服的单容器中存在多个进程,多个脚本文件或配置文件,游戏服引擎常驻进程通常会通过构建新的容器进行实现新版发布,而新的脚本、资源、或配置的更新往往依赖对象存储的挂载、或是自研程序的动态拉取。并且更新的情况由业务自行判断,整个过程以非云原生的方式进行。在业内,我们称这种游戏服为富容器。富容器热更新的问题在于: + +- 无法对脚本/资源/配置文件进行云原生化的版本管理。由于容器镜像并没有发生变化,运维人员对当前容器中运行的脚本文件等版本不得而知。游戏上线后小版本的迭代十分频繁,当故障出现时,没有版本管理的系统将难以定位问题,这很大程度上提高了运维复杂度。 +- 更新状态难以定位。即使对容器中的文件进行了更新替换,但执行重载命令时难以确定当前热更文件是否已经挂载完毕,这种更新成功与否的状态维护需要交给运维者额外管理,也一定程度上提高了运维复杂度。 +- 无法灰度升级。在更新时,为了控制影响面,往往需要先更新低重要性的游戏服,确认无误后再灰度其余游戏服。但无论是对象存储挂载的方式还是程序拉取的方式很难做到灰度发布。一旦全量发布出现问题,故障影响面是非常大的。 +- 在容器异常时,pod重建拉起旧版本的镜像,热更文件并未能持续化保留。 + +针对游戏服热更场景,更理想的做法是使用多容器的游戏服架构,将热更的部分作为sidecar容器与main容器一同部署在同一个游戏服(GameServer)中,二者通过emptyDir共享热更文件。更新时只需更新sidecar容器即可。这样一来,游戏服的热更将以云原生的方式进行: + +- sidecar容器镜像具有版本属性,解决了版本管理问题。 +- Kubernetes容器更新成功后处于Ready状态,能够感知sidecar更新是否成功。 +- OKG提供多种更新策略,可按照发布需求自行控制发布对象,完成灰度发布。 +- 即使容器异常发生重启,热更文件随着镜像的固化而持续化保留了下来。 + +## 基于原地升级的游戏服热更新 + +### 原地升级 + +在标准的Kubernetes中,应用的更新是通过更改资源对象中Image字段实现的。但原生的workload,如Deployment或StatefulSet管理的pod在更新了Image之后会出现重建的情况,pod的生命周期与容器的生命周期耦合在一起,上文提到的多容器架构的游戏服热更新在原生Kubernetes的workload下变成了无稽之谈。 + +OKG的GameServerSet提供了一种原地升级的能力,在保证整个游戏服生命周期不变的情况下定向更新其中某一个容器,不会导致游戏服重新创建。sidecar容器更新过程游戏服正常运行,玩家不会收到任何影响。 + +如下图所示,蓝色部分为热更部分,橘色部分为非热更部分。我们将Game Script容器从版本V1更新至版本V2后,整个pod不会重建,橘色部分不受到任何影响,Game Engine正常平稳运行 + + +![hot-update.png](/img/kruisegame/user-manuals/hot-update.png) + +### 使用示例 + +本文使用2048网页版作为示例。在示例中,我们将看到如何在不影响游戏服生命周期的前提条件下更新游戏脚本。 + +部署带有sidecar容器的游戏服,使用GameServerSet作为游戏服负载,设置: +- pod更新策略选择原地升级 +- 使用AlibabaCloud-SLB网络模型暴露服务 +- 两个容器,其中app-2048为主容器,承载主要游戏逻辑;sidecar为伴生容器,存放热更文件。二者通过emptyDir共享文件目录 + - sidecar启动时将存放热更文件的目录下文件(/app/js)同步至共享目录下(/app/scripts),同步后sleep不退出 + - app-2048容器使用/var/www/html/js目录下的游戏脚本 + +```bash +cat < + +接下来,我们希望更新游戏服脚本,将游戏结束时的显示字样变为 `*_* Game over!` + +修改对应脚本文件html_actuator.js,并构建新的sidecar镜像,将镜像tag命名为v2.0。(在实际生产中,这一过程可通过CI流程完成) + +镜像更新后只需更新GameServerSet对应的容器镜像版本即可: + +```bash +kubectl edit gss gss-2048 +... + - image: registry.cn-beijing.aliyuncs.com/acs/2048-sidecar:v2.0 + name: sidecar +... +``` + +一段时间过后,发现gs已从Updating变为Ready,Pod已经更新完毕,restarts次数变为1,但Age并没有减少。 + +```bash +kubectl get pod +NAME READY STATUS RESTARTS AGE +gss-2048-0 2/2 Running 1 (33s ago) 8m55s +``` + +此时对app-2048容器执行重载命令 + +```bash +kubectl exec gss-2048-0 -c app-2048 -- /usr/sbin/nginx -s reload +``` + +打开无痕浏览器,进行游戏,游戏结束时提示字样已更新: + + + +### 文件热更后的重载方式 + +在上面的示例中,对单个pod使用exec执行命令的方式重载。 +而在批量管理时,重载操作太过繁琐复杂。下面提供了几种文件热更后的重载方式,以供参考。 + +#### 手动批量重载 + +当全部游戏服更新Ready后,可借助批量管理工具kubectl-pexec批量在容器中执行exec重载命令。完成游戏服热重载。 + +#### 通过inotify跟踪热更文件目录 + +inotify是Linux文件监控系统框架。通过inotify,主游戏服业务容器可以监听热更文件目录下文件的变化,进而触发更新。 + +使用inotify需要在容器中安装inotify-tools: + +```bash +apt-get install inotify-tools +``` + +以上述2048游戏为例,在原镜像基础之上,app-2048容器监听 /var/www/html/js/ 目录,当发现文件变化时自动执行重载命令。脚本如下所示,在容器启动时执行即可。值得注意的是重载命令应为幂等的。 + +```shell +inotifywait -mrq --timefmt '%d/%m/%y %H:%M' --format '%T %w%f%e' -e modify,delete,create,attrib /var/www/html/js/ | while read file +do + /usr/sbin/nginx -s reload + echo "reload successfully" +done +``` + +将上述程序固化至镜像中,构建出新的镜像`registry.cn-beijing.aliyuncs.com/acs/2048:v1.0-inotify`,再次实验(其他字段不变),将sidecar镜像从v1.0替换到v2.0后,会发现已经不需要手动输入重载命令已完成全部热更过程。 +完整的yaml如下 +```yaml +kind: GameServerSet +metadata: + name: gss-2048 + namespace: default +spec: + replicas: 1 + updateStrategy: + rollingUpdate: + podUpdatePolicy: InPlaceIfPossible + network: + networkType: AlibabaCloud-SLB + networkConf: + - name: SlbIds + value: lb-bp1oqahx3jnr7j3f6vyp8 + - name: PortProtocols + value: 80/TCP + gameServerTemplate: + spec: + containers: + - image: registry.cn-beijing.aliyuncs.com/acs/2048:v1.0-inotify + name: app-2048 + volumeMounts: + - name: shared-dir + mountPath: /var/www/html/js + - image: registry.cn-beijing.aliyuncs.com/acs/2048-sidecar:v1.0 #热更时替换成v2.0 + name: sidecar + args: + - bash + - -c + - rsync -aP /app/js/* /app/scripts/ && while true; do echo 11;sleep 2; done + volumeMounts: + - name: shared-dir + mountPath: /app/scripts + volumes: + - name: shared-dir + emptyDir: {} +``` + +#### sidecar触发http请求 + +主游戏服业务容器暴露一个http接口,sidecar在启动成功后向本地127.0.0.1发送重载请求,由于pod下容器共享网络命名空间,主容器接收到请求后进行文件重载。 + +以上述2048游戏为例,在原镜像基础之上: + +- app-2048容器新增reload接口,以下是js代码示例 + + ```js + var http = require('http'); + var exec = require('child_process').exec; + + var server = http.createServer(function(req, res) { + if (req.url === '/reload') { + exec('/usr/sbin/nginx -s reload', function(error, stdout, stderr) { + if (error) { + console.error('exec error: ' + error); + res.statusCode = 500; + res.end('Error: ' + error.message); + return; + } + console.log('stdout: ' + stdout); + console.error('stderr: ' + stderr); + res.statusCode = 200; + res.end(); + }); + } else { + res.statusCode = 404; + res.end('Not found'); + } + }); + + server.listen(3000, function() { + console.log('Server is running on port 3000'); + }); + ``` +- 同时,sidecar容器新增请求脚本request.sh,容器启动后利用postStart增加发送请求命令,如下所示 + + ```yaml + ... + name: sidecar + lifecycle: + postStart: + exec: + command: + - bash + - -c + - ./request.sh + ... + ``` + + 对应request.sh脚本如下所示,具有重试机制,确认重载成功再退出 + + ```shell + #!/bin/bash + + # 循环发送 HTTP 请求,直到服务器返回成功响应为止 + while true; do + response=$(curl -s -w "%{http_code}" http://localhost:3000/reload) + if [[ $response -eq 200 ]]; then + echo "Server reloaded successfully!" + break + else + echo "Server reload failed, response code: $response" + fi + sleep 1 + done + ``` + +这样一来,在文件更新后也可完成自动重载。 + +将上述程序固化至镜像中,构建出以下新的镜像: +- `registry.cn-beijing.aliyuncs.com/acs/2048:v1.0-http` +- `registry.cn-beijing.aliyuncs.com/acs/2048-sidecar:v1.0-http` +- `registry.cn-beijing.aliyuncs.com/acs/2048-sidecar:v2.0-http` + +替换新镜像再次实验(注意yaml中sidecar需要增加lifecycle字段)。将sidecar镜像从v1.0-http替换到v2.0-http后,会发现已经不需要手动输入重载命令已完成全部热更过程。 +完整的yaml如下: +```yaml +kind: GameServerSet +metadata: + name: gss-2048 + namespace: default +spec: + replicas: 1 + updateStrategy: + rollingUpdate: + podUpdatePolicy: InPlaceIfPossible + network: + networkType: AlibabaCloud-SLB + networkConf: + - name: SlbIds + value: lb-bp1oqahx3jnr7j3f6vyp8 + - name: PortProtocols + value: 80/TCP + gameServerTemplate: + spec: + containers: + - image: registry.cn-beijing.aliyuncs.com/acs/2048:v1.0-http + name: app-2048 + volumeMounts: + - name: shared-dir + mountPath: /var/www/html/js + - image: registry.cn-beijing.aliyuncs.com/acs/2048-sidecar:v1.0-http #热更时替换成v2.0-http + name: sidecar + lifecycle: + postStart: + exec: + command: + - bash + - -c + - ./request.sh + args: + - bash + - -c + - rsync -aP /app/js/* /app/scripts/ && while true; do echo 11;sleep 2; done + volumeMounts: + - name: shared-dir + mountPath: /app/scripts + volumes: + - name: shared-dir + emptyDir: {} +``` + +#### 全托管的热更重载 + +OKG具备触发容器中执行命令的能力,基于该功能OKG可提供全自动化的热更新能力,让用户不再过度关心热更重载问题。如若您有这方面需求,可以在GitHub提交issue,和社区开发者一起讨论OKG热更功能演进路线。 + +### 停服原地热更 + +游戏场景下狭义上的热更是指不影响玩家正常游戏的不停服更新。然而在有些场景下,游戏服停服更新也需要依赖原地升级能力。 + +#### 网络元数据不变 + +游戏服的有状态特性时常体现在网络信息上。由于每个游戏服都是独特的,无法使用k8s svc负载均衡的概念,往往游戏开发者会基于IP实现路由分发机制,这时我们需要在游戏更新时避免游戏服IP信息变化。OKG的原地升级能力能够满足上述需求。 + +#### 共享内存不丢失 + +游戏服创建后调度到某宿主机上,游戏业务利用共享内存降低数据落盘延迟,这样一来,相当于游戏服在本地增加了一层缓存。在游戏服更新时,即时出现短暂的服务暂停时间,但由于缓存的存在,游戏服的终止以及启动速度较快,停服时间也会大大减少。共享内存的实现也依赖于OKG的原地升级能力,保证对应缓存数据不会丢失。 \ No newline at end of file diff --git a/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/network.md b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/network.md new file mode 100644 index 0000000000..80f569f1c0 --- /dev/null +++ b/i18n/zh/docusaurus-plugin-content-docs-kruisegame/version-v0.8.0/user-manuals/network.md @@ -0,0 +1,1174 @@ +# 网络模型 +## 功能概述 + +如[OKG设计理念](../design-concept.md)中提到的,游戏服接入层网络是游戏开发者非常关注的问题。 +非网关架构下,游戏开发者需要考虑如何暴露游戏服的外部IP端口,供玩家连接访问。 +在不同场景下,往往需要不同的网络产品,而有时网络产品由云厂商提供。OKG 的 Cloud Provider & Network Plugin 源于此而诞生。 +OKG 会集成不同云提供商的不同网络插件,用户可通过GameServerSet设置游戏服的网络参数,并在生成的GameServer中查看网络状态信息,极大降低了游戏服接入网络的复杂度。 + +## 网络插件附录 + +当前支持的网络插件: +- Kubernetes-HostPort +- Kubernetes-NodePort +- Kubernetes-Ingress +- AlibabaCloud-NATGW +- AlibabaCloud-SLB +- AlibabaCloud-NLB +- AlibabaCloud-EIP +- AlibabaCloud-SLB-SharedPort +- AlibabaCloud-NLB-SharedPort +- Volcengine-CLB + +--- +### Kubernetes-HostPort +#### 插件名称 + +`Kubernetes-HostPort` + +#### Cloud Provider + +Kubernetes + +#### 插件说明 +- Kubernetes-HostPort利用宿主机网络,通过主机上的端口转发实现游戏服对外暴露服务。宿主机需要配置公网IP,有被公网访问的能力。 + +- 用户在配置文件中可自定义宿主机开放的端口段(默认为8000-9000),该网络插件可以帮助用户分配管理宿主机端口,尽量避免端口冲突。 + +- 该插件不支持网络隔离。 + +- Kubernetes-HostPort 依赖Kubernetes提供的hostPort模式。需要注意存在一些CNI插件不支持hostPort,如Terway等。 + + + +#### 网络参数 + +ContainerPorts + +- 含义:填写提供服务的容器名以及对应暴露的端口和协议 +- 填写格式:containerName:port1/protocol1,port2/protocol2,...(协议需大写) 比如:`game-server:25565/TCP` +- 是否支持变更:不支持,在创建时即永久生效,随pod生命周期结束而结束 + +#### 插件配置 + +``` +[kubernetes] +enable = true +[kubernetes.hostPort] +#填写宿主机可使用的空闲端口段,用于为pod分配宿主机转发端口 +max_port = 9000 +min_port = 8000 +``` + +#### 示例说明 + +OKG支持在原生Kubernetes集群使用HostPort游戏服网络,使用游戏服所在宿主机暴露外部IP及端口,转发至游戏服内部端口中。使用方式如下。 + +部署一个带有network的GameServerSet: + +``` +cat <添加到原始路径(与HTTPIngressPath中Path一致)的任意位置,该插件将会生成游戏服ID对应的路径。例如,当设置路径为 /game,游戏服0对应路径为/game0,游戏服1对应路径为/game1,以此类推。 +- 是否支持变更:支持 + +PathType + +- 含义:路径类型。与HTTPIngressPath的PathType字段一致。 +- 填写格式:与HTTPIngressPath的PathType字段一致。 +- 是否支持变更:支持 + +Port + +- 含义:游戏服暴露的端口值。 +- 填写格式:端口数字 +- 是否支持变更:支持 + +IngressClassName + +- 含义:指定IngressClass的名称。与IngressSpec的IngressClassName字段一致。 +- 填写格式:与IngressSpec的IngressClassName字段一致。 +- 是否支持变更:支持 + +Host + +- 含义:域名。每个游戏服依据ID拥有各自的访问域名。 +- 填写格式:将添加域名的任意位置,该插件将会生成游戏服ID对应的域名。例如,当设置域名为 test.game.cn-hangzhou.ali.com,游戏服0对应域名为test.game0.cn-hangzhou.ali.com,游戏服1对应域名为test.game1.cn-hangzhou.ali.com,以此类推。 +- 是否支持变更:支持 + +TlsHosts + +- 含义:包含TLS证书的host列表。含义与IngressTLS的Hosts字段类似。 +- 填写格式:host1,host2,... 例如,xxx.xx1.com,xxx.xx2.com +- 是否支持变更:支持 + +TlsSecretName + +- 含义:与IngressTLS的SecretName字段一致。 +- 填写格式:与IngressTLS的SecretName字段一致。 +- 是否支持变更:支持 + +Annotation + +- 含义:作为ingress对象的annotation +- 格式:key: value(注意:后有空格),例如:nginx.ingress.kubernetes.io/rewrite-target: /$2 +- 是否支持变更:支持 + +Fixed + +- 含义:是否需要保持ingress,让其不随pod的删除而删除 +- 取值:true / false +- 是否支持变更:支持 + +_补充说明_ + +- 支持填写多个annotation,在networkConf中填写多个Annotation以及对应值即可,不区分填写顺序。 +- 支持填写多个路径。路径、路径类型、端口按照填写顺序一一对应。当路径数目大于路径类型数目(或端口数目)时,无法找到对应关系的路径按照率先填写的路径类型(或端口)匹配。 + +#### 插件配置 + +无 + +#### 示例说明 + +GameServerSet中network字段声明如下: + +```yaml + network: + networkConf: + - name: IngressClassName + value: nginx + - name: Port + value: "80" + - name: Path + value: /game(/|$)(.*) + - name: Path + value: /test- + - name: Host + value: test.xxx.cn-hangzhou.ali.com + - name: PathType + value: ImplementationSpecific + - name: TlsHosts + value: xxx.xx1.com,xxx.xx2.com + - name: Annotation + value: 'nginx.ingress.kubernetes.io/rewrite-target: /$2' + - name: Annotation + value: 'nginx.ingress.kubernetes.io/random: xxx' + networkType: Kubernetes-Ingress +``` + +则会生成gss replicas对应数目的service与ingress对象。0号游戏服生成的ingress字段如下所示: + +```yaml +spec: + ingressClassName: nginx + rules: + - host: test.xxx.cn-hangzhou.ali.com + http: + paths: + - backend: + service: + name: ing-nginx-0 + port: + number: 80 + path: /game0(/|$)(.*) + pathType: ImplementationSpecific + - backend: + service: + name: ing-nginx-0 + port: + number: 80 + path: /test-0 + pathType: ImplementationSpecific + tls: + - hosts: + - xxx.xx1.com + - xxx.xx2.com +status: + loadBalancer: + ingress: + - ip: 47.xx.xxx.xxx +``` + +其他序号的游戏服只有path字段与service name不同,生成的其他参数都相同。 + +对应的0号GameServer的networkStatus如下: + +```yaml + networkStatus: + createTime: "2023-04-28T14:00:30Z" + currentNetworkState: Ready + desiredNetworkState: Ready + externalAddresses: + - ip: 47.xx.xxx.xxx + ports: + - name: /game0(/|$)(.*) + port: 80 + protocol: TCP + - name: /test-0 + port: 80 + protocol: TCP + internalAddresses: + - ip: 10.xxx.x.xxx + ports: + - name: /game0(/|$)(.*) + port: 80 + protocol: TCP + - name: /test-0 + port: 80 + protocol: TCP + lastTransitionTime: "2023-04-28T14:00:30Z" + networkType: Kubernetes-Ingress +``` + +--- +### AlibabaCloud-NATGW +#### 插件名称 + +`AlibabaCloud-NATGW` + +#### Cloud Provider + +AlibabaCloud + +#### 插件说明 + +- AlibabaCloud-NATGW 使用阿里云公网网关作为游戏服对外服务的承载实体,外网流量通过DNAT规则转发至对应的游戏服中。 + +- 是否支持网络隔离:否 + +#### 网络参数 + +Ports + +- 含义:填写pod需要暴露的端口 +- 填写格式:port1,port2,port3… 例如:80,8080,8888 +- 是否支持变更:不支持 + +Protocol + +- 含义:填写服务的网络协议 +- 填写格式:例如:tcp,默认为tcp +- 是否支持变更:不支持 + +Fixed + +- 含义:是否固定访问IP/端口。若是,即使pod删除重建,网络内外映射关系不会改变 +- 填写格式:false / true +- 是否支持变更:不支持 + +#### 插件配置 + +无 + +#### 示例说明 + +OKG支持阿里云下NAT网关模型,使用NATGW的外部IP与端口暴露服务,流量最终将转发至Pod之中。使用方式如下: + +```shell +cat < -
+
diff --git a/kruisegame/user-manuals/crd-field-description.md b/kruisegame/user-manuals/crd-field-description.md index c3568e8df8..640ceae634 100644 --- a/kruisegame/user-manuals/crd-field-description.md +++ b/kruisegame/user-manuals/crd-field-description.md @@ -33,6 +33,9 @@ type GameServerSetSpec struct { // Network settings for game server access layer. Network *Network `json:"network,omitempty"` + + // Lifecycle hook defined by users + Lifecycle *appspub.Lifecycle `json:"lifecycle,omitempty"` } ``` @@ -186,6 +189,9 @@ type ServiceQualityAction struct { Result string `json:"result,omitempty"` GameServerSpec `json:",inline"` + + Annotations map[string]string `json:"annotations,omitempty"` + Labels map[string]string `json:"labels,omitempty"` } ``` @@ -211,6 +217,28 @@ type KVParams struct { } ``` +#### Lifecycle + +``` +// Lifecycle contains the hooks for Pod lifecycle. +type Lifecycle struct { + // PreDelete is the hook before Pod to be deleted. + PreDelete *LifecycleHook `json:"preDelete,omitempty"` + // InPlaceUpdate is the hook before Pod to update and after Pod has been updated. + InPlaceUpdate *LifecycleHook `json:"inPlaceUpdate,omitempty"` +} + +type LifecycleHook struct { + LabelsHandler map[string]string `json:"labelsHandler,omitempty"` + FinalizersHandler []string `json:"finalizersHandler,omitempty"` + // MarkPodNotReady = true means: + // - Pod will be set to 'NotReady' at preparingDelete/preparingUpdate state. + // - Pod will be restored to 'Ready' at Updated state if it was set to 'NotReady' at preparingUpdate state. + // Default to false. + MarkPodNotReady bool `json:"markPodNotReady,omitempty"` +} +``` + ### GameServerSetStatus ``` diff --git a/kruisegame/user-manuals/deploy-gameservers.md b/kruisegame/user-manuals/deploy-gameservers.md index 712e39b6f5..d537359a04 100644 --- a/kruisegame/user-manuals/deploy-gameservers.md +++ b/kruisegame/user-manuals/deploy-gameservers.md @@ -173,16 +173,16 @@ PING minecraft-2.minecraft.default.svc.cluster.local (172.16.0.12): 56 data byte It can be found that the accessor successfully accessed minecraft-2, and the DNS successfully resolved to the corresponding intranet IP address. The DNS rules here are as follows: {pod-name}.{gss-name}.{namespace-name}.svc.cluster.local -## Synchronization of Annotations from GameServer to Pod +## Synchronization of Labels/Annotations from GameServer to Pod -As mentioned above, through the DownwardAPI, information from pod annotations can be propagated downwards into containers. Sometimes, we wish to synchronize the annotations of a GameServer to its Pod, in order to complete the action of sinking GameServer metadata information. +As mentioned above, through the DownwardAPI, information from pod labels/annotations can be propagated downwards into containers. Sometimes, we wish to synchronize the labels/annotations of a GameServer to its Pod, in order to complete the action of sinking GameServer metadata information. ```bash kubectl patch gs minecraft-0 --type='merge' -p '{"metadata":{"annotations":{"gs-sync/test-key":"some-value"}}}' gameserver.game.kruise.io/minecraft-0 patched ``` -OKG supports the synchronization of annotations from GameServer to Pod starting with "gs-sync/," as demonstrated below: +Then, we get pod minecraft-0: ```bash kubectl get po minecraft-0 -oyaml | grep gs-sync diff --git a/kruisegame/user-manuals/lifecycle.md b/kruisegame/user-manuals/lifecycle.md new file mode 100644 index 0000000000..4d7f9b8073 --- /dev/null +++ b/kruisegame/user-manuals/lifecycle.md @@ -0,0 +1,115 @@ +# Custom Lifecycle Management + +Game servers, due to their strong stateful characteristics, have a high demand for graceful shutdown operations. +A game server typically needs to wait until data is fully persisted to disk and ensured to be safe before it can be thoroughly removed. +Although Kubernetes natively provides the preStop hook, which allows containers to execute specific actions before they are about to shut down, there is a limitation: once the preset time limit is exceeded, the container will have to be forcibly terminated, regardless of whether the data processing is complete or not. +In some cases, this approach lacks real gracefulness. We need a more flexible mechanism to ensure that game servers can exit smoothly while protecting all critical states. + +OpenKruise has introduced the Lifecycle Hook feature, which provides precise control and waiting mechanisms for game servers at critical lifecycle moments. +This allows servers to execute the actual deletion or update operations only after meeting specific conditions. +By providing a configurable Lifecycle field, combined with the ability to customize service quality, OKG ensures that the game server's shutdown process is both graceful and reliable. +With this advanced feature, maintainers can ensure that all necessary data persistence and internal state synchronization are safely and correctly completed before the server is smoothly removed or updated. + +## Usage Example + +```yaml +apiVersion: game.kruise.io/v1alpha1 +kind: GameServerSet +metadata: + name: minecraft + namespace: default +spec: + replicas: 3 + lifecycle: + preDelete: + labelsHandler: + gs-sync/delete-block: "true" + gameServerTemplate: + metadata: + labels: + gs-sync/delete-block: "true" + spec: + containers: + - image: registry.cn-beijing.aliyuncs.com/chrisliu95/minecraft-demo:probe-v0 + name: minecraft + volumeMounts: + - name: gsState + mountPath: /etc/gsinfo + volumes: + - name: gsinfo + downwardAPI: + items: + - path: "state" + fieldRef: + fieldPath: metadata.labels['game.kruise.io/gs-state'] + serviceQualities: + - name: healthy + containerName: minecraft + permanent: false + exec: + command: ["bash", "./probe.sh"] + serviceQualityAction: + - state: true + result: done + labels: + gs-sync/delete-block: "false" + - state: true + result: WaitToBeDeleted + opsState: WaitToBeDeleted + - state: false + opsState: None +``` + +The corresponding script is as follows. The script performs the following actions: + +- Acquires the current state of gs from /etc/gsinfo/state and determines whether it is "PreDelete" + - If it is PreDelete, it indicates that the current gs should be in the offline phase. It checks whether the data flushing has been completed (in this example, the presence of a file indicates data flushing completion) + - If the data flushing is not completed, it executes the data flushing action (in this example, it creates a file) + - If the data flushing is completed, it outputs "done" and exits with 1. + - If it is not PreDelete, it indicates that the gs has not entered the offline stage. It uses the number of people in the game server to determine whether it should now go offline. + - If the number of people on the game server equals 0, it outputs "WaitToBeDeleted" and exits with 1. + - If the number of people on the game server is not 0, it exits with 0. + +``` +#!/bin/bash + +file_path="/etc/gsinfo/state" +data_flushed_file="/etc/gsinfo/data_flushed" + +if [[ ! -f "$file_path" ]]; then + exit 0 +fi + +state_content=$(cat "$file_path") + +if [[ "$state_content" == "PreDelete" ]]; then + if [[ -f "$data_flushed_file" ]]; then + echo "done" + exit 1 + else + touch "$data_flushed_file" + echo "WaitToBeDeleted" + exit 1 + fi +else + people_count_file="/etc/gsinfo/people_count" + + people_count=$(cat "$people_count_file") + + if [[ "$people_count" -eq 0 ]]; then + echo "WaitToBeDeleted" + exit 1 + else + exit 0 + fi +fi +``` + +![grace-deletion.png](../../static/img/kruisegame/user-manuals/gs-lifecycle-delete.png) + +The process of elegant delete as follow: +1. The game server is running normally, and the number of players is not 0. +2. When the number of players drops to 0, set the opsState to WaitToBeDeleted using custom service quality settings. +3. Through the automatic scaling policy, OKG deletes the GameServer with WaitToBeDeleted opsState. Since the lifecycle hook is configured and the delete-block label wil be set to true, the gs is not truly deleted but enters the PreDelete state, and the data flushing process is triggered by custom service quality. +4. Once data flushing is complete, set the delete-block label to false using custom service quality to release the checkpoint. +5. After the checkpoint is released, the PreDelete phase moves into the Delete phase. The gs is then truly deleted. \ No newline at end of file diff --git a/kruisegame/user-manuals/network.md b/kruisegame/user-manuals/network.md index b6a416cb9b..441930e744 100644 --- a/kruisegame/user-manuals/network.md +++ b/kruisegame/user-manuals/network.md @@ -19,6 +19,7 @@ OpenKruiseGame supports the following network plugins: - AlibabaCloud-SLB-SharedPort - AlibabaCloud-NLB-SharedPort - Volcengine-CLB +- AmazonWebServices-NLB --- ### Kubernetes-HostPort @@ -533,6 +534,72 @@ AllowNotReadyContainers - Value: {containerName_0},{containerName_1},... Example:sidecar - Configuration change supported or not: It cannot be changed during the in-place updating process. +LBHealthCheckSwitch + +- Meaning:Whether to enable health check +- Format:"on" means on, "off" means off. Default is on +- Whether to support changes: Yes + +LBHealthCheckFlag + +- Meaning: Whether to enable http type health check +- Format: "on" means on, "off" means off. Default is on +- Whether to support changes: Yes + +LBHealthCheckType + +- Meaning: Health Check Protocol +- Format: fill in "tcp" or "http", the default is tcp +- Whether to support changes: Yes + +LBHealthCheckConnectTimeout + +- Meaning: Maximum timeout for health check response. +- Format: Unit: seconds. The value range is [1, 300]. The default value is "5" +- Whether to support changes: Yes + +LBHealthyThreshold + +- Meaning: After the number of consecutive successful health checks, the health check status of the server will be determined from failure to success. +- Format: Value range [2, 10]. Default value is "2" +- Whether to support changes: Yes + +LBUnhealthyThreshold + +- Meaning: After the number of consecutive health check failures, the health check status of the server will be determined from success to failure. +- Format: Value range [2, 10]. The default value is "2" +- Whether to support changes: Yes + +LBHealthCheckInterval + +- Meaning: health check interval. +- Format: Unit: seconds. The value range is [1, 50]. The default value is "10" +- Whether to support changes: Yes + +LBHealthCheckProtocolPort + +- Meaning:the protocols & ports of HTTP type health check. +- Format:Multiple values are separated by ','. e.g. https:443,http:80 +- Whether to support changes: Yes + +LBHealthCheckUri + +- Meaning: The corresponding uri when the health check type is HTTP. +- Format: The length is 1~80 characters, only letters, numbers, and characters can be used. Must start with a forward slash (/). Such as "/test/index.html" +- Whether to support changes: Yes + +LBHealthCheckDomain + +- Meaning: The corresponding domain name when the health check type is HTTP. +- Format: The length of a specific domain name is limited to 1~80 characters. Only lowercase letters, numbers, dashes (-), and half-width periods (.) can be used. +- Whether to support changes: Yes + +LBHealthCheckMethod + +- Meaning: The corresponding method when the health check type is HTTP. +- Format: "GET" or "HEAD" +- Whether to support changes: Yes + #### Plugin configuration ``` [alibabacloud] @@ -585,6 +652,66 @@ AllowNotReadyContainers - Value: {containerName_0},{containerName_1},... Example:sidecar - Configuration change supported or not: It cannot be changed during the in-place updating process. +LBHealthCheckFlag + +- Meaning: Whether to enable health check +- Format: "on" means on, "off" means off. Default is on +- Whether to support changes: Yes + +LBHealthCheckType + +- Meaning: Health Check Protocol +- Format: fill in "tcp" or "http", the default is tcp +- Whether to support changes: Yes + +LBHealthCheckConnectPort + +- Meaning: Server port for health check. +- Format: Value range [0, 65535]. Default value is "0" +- Whether to support changes: Yes + +LBHealthCheckConnectTimeout + +- Meaning: Maximum timeout for health check response. +- Format: Unit: seconds. The value range is [1, 300]. The default value is "5" +- Whether to support changes: Yes + +LBHealthyThreshold + +- Meaning: After the number of consecutive successful health checks, the health check status of the server will be determined from failure to success. +- Format: Value range [2, 10]. Default value is "2" +- Whether to support changes: Yes + +LBUnhealthyThreshold + +- Meaning: After the number of consecutive health check failures, the health check status of the server will be determined from success to failure. +- Format: Value range [2, 10]. The default value is "2" +- Whether to support changes: Yes + +LBHealthCheckInterval + +- Meaning: health check interval. +- Format: Unit: seconds. The value range is [1, 50]. The default value is "10" +- Whether to support changes: Yes + +LBHealthCheckUri + +- Meaning: The corresponding uri when the health check type is HTTP. +- Format: The length is 1~80 characters, only letters, numbers, and characters can be used. Must start with a forward slash (/). Such as "/test/index.html" +- Whether to support changes: Yes + +LBHealthCheckDomain + +- Meaning: The corresponding domain name when the health check type is HTTP. +- Format: The length of a specific domain name is limited to 1~80 characters. Only lowercase letters, numbers, dashes (-), and half-width periods (.) can be used. +- Whether to support changes: Yes + +LBHealthCheckMethod + +- Meaning: The corresponding method when the health check type is HTTP. +- Format: "GET" or "HEAD" +- Whether to support changes: Yes + #### Plugin configuration ``` [alibabacloud] @@ -1081,6 +1208,194 @@ networkStatus: networkType: Volcengine-CLB ``` +--- + +### AmazonWebServices-NLB + +#### Plugin name + +`AmazonWebServices-NLB` + +#### Plugin description + +For game businesses using OKG in AWS EKS clusters, routing traffic directly to Pod ports via network load balancing is the foundation for achieving high-performance real-time service discovery. Using NLB for dynamic port mapping simplifies the forwarding chain and avoids the performance loss caused by Kubernetes kube-proxy load balancing. These features are particularly crucial for handling replica combat-type game servers. For GameServerSets with the network type specified as AmazonWebServices-NLB, the AmazonWebServices-NLB network plugin will schedule an NLB, automatically allocate ports, create listeners and target groups, and associate the target group with Kubernetes services through the TargetGroupBinding CRD. If the cluster is configured with VPC-CNI, the traffic will be automatically forwarded to the Pod's IP address; otherwise, it will be forwarded through ClusterIP. The process is considered successful when the network of the GameServer is in the Ready state. + +#### Preparation + +Due to the difference in AWS design, to achieve NLB port-to-Pod port mapping, three types of CRD resources need to be created: Listener/TargetGroup/TargetGroupBinding + +##### Deploy elbv2-controller: + +Definition and controller for Listener/TargetGroup CRDs: https://github.com/aws-controllers-k8s/elbv2-controller. This project links k8s resources with AWS cloud resources. Download the chart: https://gallery.ecr.aws/aws-controllers-k8s/elbv2-chart, example value.yaml: + +```yaml +serviceAccount: + annotations: + eks.amazonaws.com/role-arn: "arn:aws:iam::xxxxxxxxx:role/test" +aws: + region: "us-east-1" + endpoint_url: "https://elasticloadbalancing.us-east-1.amazonaws.com" +``` + +The key to deploying this project lies in authorizing the k8s ServiceAccount to access the NLB SDK, which is recommended to be done through an IAM role: + +###### Step 1:Enable OIDC provider for the EKS cluster + +1. Sign in to the AWS Management Console. +2. Navigate to the EKS console:https://console.aws.amazon.com/eks/ +3. Select your cluster. +4. On the cluster details page, ensure that the OIDC provider is enabled. Obtain the OIDC provider URL for the EKS cluster. In the "Configuration" section of the cluster details page, find the "OpenID Connect provider URL". + +###### Step 2:Configure the IAM role trust policy +1. In the IAM console, create a new identity provider and select "OpenID Connect". + - For the Provider URL, enter the OIDC provider URL of your EKS cluster. + - For Audience, enter: `sts.amazonaws.com` + +2. In the IAM console, create a new IAM role and select "Custom trust policy". + - Use the following trust policy to allow EKS to use this role: + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Federated": "arn:aws:iam:::oidc-provider/oidc.eks..amazonaws.com/id/" + }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringEquals": { + "oidc.eks..amazonaws.com/id/:sub": "system:serviceaccount::ack-elbv2-controller", + "oidc.eks..amazonaws.com/id/:aud": "sts.amazonaws.com" + } + } + } + ] + } + ``` + - Replace ``、``、``、`` and `` with your actual values. + - Add the permission `ElasticLoadBalancingFullAccess` + +##### Deploy AWS Load Balancer Controller: + +CRD and controller for TargetGroupBinding: https://github.com/kubernetes-sigs/aws-load-balancer-controller/ + +Official deployment documentation: https://docs.aws.amazon.com/eks/latest/userguide/lbc-helm.html, essentially authorizing k8s ServiceAccount in a way similar to an IAM role. + +#### Network Parameters + +NlbARNs +- Meaning: Fill in the ARN of the nlb, you can fill in multiple, and nlb needs to be created in AWS in advance. +- Format: Separate each nlbARN with a comma. For example: arn:aws:elasticloadbalancing:us-east-1:888888888888:loadbalancer/net/aaa/3b332e6841f23870,arn:aws:elasticloadbalancing:us-east-1:000000000000:loadbalancer/net/bbb/5fe74944d794d27e +- Support for change: Yes + +NlbVPCId + +- Meaning: Fill in the vpcid where nlb is located, needed for creating AWS target groups. +- Format: String. For example: vpc-0bbc9f9f0ffexxxxx +- Support for change: Yes + +NlbHealthCheck + +- Meaning: Fill in the health check parameters for the nlb target group, can be left blank to use default values. +- Format: Separate each configuration with a comma. For example: "healthCheckEnabled:true,healthCheckIntervalSeconds:30,healthCheckPath:/health,healthCheckPort:8081,healthCheckProtocol:HTTP,healthCheckTimeoutSeconds:10,healthyThresholdCount:5,unhealthyThresholdCount:2" +- Support for change: Yes +- Parameter explanation: + - **healthCheckEnabled**:Indicates whether health checks are enabled. If the target type is lambda, health checks are disabled by default but can be enabled. If the target type is instance, ip, or alb, health checks are always enabled and cannot be disabled. + - **healthCheckIntervalSeconds**:The approximate amount of time, in seconds, between health checks of an individual target. The range is 5-300. If the target group protocol is TCP, TLS, UDP, TCP_UDP, HTTP, or HTTPS, the default is 30 seconds. If the target group protocol is GENEVE, the default is 10 seconds. If the target type is lambda, the default is 35 seconds. + - **healthCheckPath**:The destination for health checks on the targets. For HTTP/HTTPS health checks, this is the path. For GRPC protocol version, this is the path of a custom health check method with the format /package.service/method. The default is /Amazon Web Services.ALB/healthcheck. + - **healthCheckPort**:The port the load balancer uses when performing health checks on targets. The default is traffic-port, which is the port on which each target receives traffic from the load balancer. If the protocol is GENEVE, the default is port 80. + - **healthCheckProtocol**:The protocol the load balancer uses when performing health checks on targets. For Application Load Balancers, the default is HTTP. For Network Load Balancers and Gateway Load Balancers, the default is TCP. The GENEVE, TLS, UDP, and TCP_UDP protocols are not supported for health checks. + - **healthCheckTimeoutSeconds**:The amount of time, in seconds, during which no response from a target means a failed health check. The range is 2–120 seconds. For target groups with a protocol of HTTP, the default is 6 seconds. For target groups with a protocol of TCP, TLS, or HTTPS, the default is 10 seconds. For target groups with a protocol of GENEVE, the default is 5 seconds. If the target type is lambda, the default is 30 seconds. + - **healthyThresholdCount**:The number of consecutive health check successes required before considering a target healthy. The range is 2-10. If the target group protocol is TCP, TCP_UDP, UDP, TLS, HTTP, or HTTPS, the default is 5. For target groups with a protocol of GENEVE, the default is 5. If the target type is lambda, the default is 5. + - **unhealthyThresholdCount**:The number of consecutive health check failures required before considering a target unhealthy. The range is 2-10. If the target group protocol is TCP, TCP_UDP, UDP, TLS, HTTP, or HTTPS, the default is 2. For target groups with a protocol of GENEVE, the default is 2. If the target type is lambda, the default is 5. + +PortProtocols + +- Meaning: Ports and protocols exposed by the pod, supports specifying multiple ports/protocols. +- Format: port1/protocol1,port2/protocol2,... (protocol should be uppercase) +- Support for change: Yes + +Fixed + +- Meaning: Whether the access port is fixed. If yes, even if the pod is deleted and rebuilt, the mapping between the internal and external networks will not change. +- Format: false / true +- Support for change: Yes + +AllowNotReadyContainers + +- Meaning: The corresponding container name that allows continuous traffic during in-place upgrades. +- Format: {containerName_0},{containerName_1},... For example: sidecar +- Support for change: Not changeable during in-place upgrades + +Annotations + +- Meaning: Annotations added to the service, supports specifying multiple annotations. +- Format: key1:value1,key2:value2... +- Support for change: Yes + +#### #### Plugin configuration +```toml +[aws] +enable = true +[aws.nlb] +# Specify the range of free ports that NLB can use to allocate external access ports for pods, with a maximum range of 50 (closed interval) +# The limit of 50 comes from AWS's limit on the number of listeners, see: https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-limits.html +max_port = 32050 +min_port = 32001 +``` + +#### Example +```shell +cat <