Nakama 上 K8s:从 CockroachDB 连接串到 HPA 的落地走法
【免费下载链接】nakamaScalable open-source game backend server: multiplayer, matchmaking, leaderboards, chat, and social features for games.项目地址: https://gitcode.com/GitHub_Trending/na/nakama
把 Nakama Kubernetes 部署从 docker-compose 搬进你的集群,思路就三层:存储层用 CockroachDB 三副本撑住数据,服务层一个 Deployment 拉起 N 个无状态节点,入口层拆 API 和 Console 两个域名。走完这套清单,你手上会有一套多副本、带弹性伸缩和指标采集的 Nakama 集群,kubectl get pods全绿,客户端能直连。
为什么单机 Nakama 撑不住在线增长
Nakama 的会话、match、排行榜调度器都跑在内存里,单机部署时进程即上限:用户翻一倍,要么加机器扛 CPU,要么接受 match 排队。更麻烦的是故障恢复——单机挂了,WebSocket 全断,只能等进程重启。
迁到 K8s 后,Nakama 节点本身是无状态的(状态全在数据库里),横向扩容就是加副本;会话 token 由加密密钥统一签发,任何节点都能验证任何节点签发的 token,所以 Pod 之间互相替换不掉线。架构长这样:
客户端只接触 7350,节点之间靠 7349 路由跨 Pod 的 match 消息,所以扩容对客户端透明。
版本与变量核对表
动手前把这张表对一遍,后面所有清单都基于这些取值:
| 项 | 取值 | 说明 |
|---|---|---|
| Kubernetes | 1.24+ | Ingress 和 HPA v2 的最低可用版本 |
| Helm | 3.8+ | 拉 CockroachDB chart 用 |
| CockroachDB | v24.1+ | 兼容 PostgreSQL 协议,Nakama 默认走 26257 端口 |
| Nakama 镜像 | registry.heroiclabs.com/heroiclabs/nakama:3.37.0 | 官方 compose 同款版本 |
| 数据库连接串 | root@cockroachdb-public:26257 | 格式user@host:port,后面多处以它为准 |
| 会话密钥 | 32 字节随机串 | 所有 Nakama 副本必须一致 |
| 端口 | 7350 / 7351 / 7349 / 9100 | API 与 Socket / Console / 节点间 / Prometheus |
🐘 存储层:CockroachDB 三节点拉起
存储层只干一件事:把数据库的高可用交给 StatefulSet,给 Nakama 一个稳定的连接串。用官方 chart 三副本起步:
helm repo add cockroachdb https://charts.cockroachdb.com/ \ && helm install cockroachdb cockroachdb/cockroachdb \ --namespace nakama-system \ --create-namespace \ --set statefulset.replicas=3 \ --set storage.persistentVolume.size=100Gi拉起后记下服务名cockroachdb-public,拼出连接串root@cockroachdb-public:26257。Nakama 只认这一条,所以服务层和迁移 Job 都直接注入它,不再各自维护。
服务层:迁移 Job 先跑,Deployment 再拉起
服务层拆两半:一次性的 schema 迁移 Job,和常驻的 Nakama Deployment。迁移单独跑的好处是多副本同时启动时不会互相抢 schema 锁——Nakama 启动时会校验 schema 是否落后,发现不一致直接 fail fast 退出。
迁移 Job 用同一个镜像的migrate子命令,和 compose 里的 entrypoint 等价:
apiVersion: batch/v1 kind: Job metadata: name: nakama-migrate namespace: nakama-system spec: backoffLimit: 5 template: spec: restartPolicy: OnFailure containers: - name: migrate image: registry.heroiclabs.com/heroiclabs/nakama:3.37.0 command: ["/nakama/nakama", "migrate", "up", "--database.address", "root@cockroachdb-public:26257"]运行时模块(Lua)不走 PVC,改用 ConfigMap 挂进默认的/nakama/data/modules目录,更新就是kubectl apply一次加滚动重启:
apiVersion: v1 kind: ConfigMap metadata: name: nakama-modules namespace: nakama-system data: match.lua: | local M = {} function M.match_init(nk, logger, match) logger:info("match created: %s", match.match_id) return nil end return M会话密钥单独放 Secret。多节点下每个 Pod 用同一个密钥签发 token,客户端在哪个节点断开重连都不会失效:
apiVersion: v1 kind: Secret metadata: name: nakama-secrets namespace: nakama-system type: Opaque stringData: session-token-key: "0123456789abcdef0123456789abcdef"(正式环境先openssl rand -hex 16生成一串再贴进来。)
最后是 Deployment 本体。注意--name是节点名,集群内必须唯一;NAKAMA_TELEMETRY=0关掉匿名遥测:
apiVersion: apps/v1 kind: Deployment metadata: name: nakama namespace: nakama-system spec: replicas: 3 selector: matchLabels: app: nakama template: metadata: labels: app: nakama spec: containers: - name: nakama image: registry.heroiclabs.com/heroiclabs/nakama:3.37.0 command: ["/bin/sh", "-ecx"] args: - | exec /nakama/nakama \ --name nakama \ --database.address root@cockroachdb-public:26257 \ --session.encryption_key "$SESSION_TOKEN_KEY" \ --session.token_expiry_sec 7200 \ --metrics.prometheus_port 9100 env: - name: SESSION_TOKEN_KEY valueFrom: secretKeyRef: name: nakama-secrets key: session-token-key - name: NAKAMA_TELEMETRY value: "0" ports: - containerPort: 7350 name: api - containerPort: 7351 name: console - containerPort: 7349 name: nodes - containerPort: 9100 name: metrics readinessProbe: exec: command: ["/nakama/nakama", "healthcheck"] initialDelaySeconds: 5 periodSeconds: 5 livenessProbe: exec: command: ["/nakama/nakama", "healthcheck"] initialDelaySeconds: 30 periodSeconds: 10 resources: requests: cpu: 500m memory: 512Mi limits: memory: 1Gi volumeMounts: - name: modules mountPath: /nakama/data/modules volumes: - name: modules configMap: name: nakama-moduleshealthcheck子命令就是对着本 Pod 的 7350 发 HTTP 请求,非 200 直接失败,所以 readiness 和 liveness 共用一个命令,不用另写探活脚本。
入口层:Service 与 Ingress 拆两个域名
服务层暴露出四个端口,但对外只有两个:7350 接客户端(REST 和 WebSocket 都走它),7351 留给运维的 Console;7349 只在 Pod 之间用,不要挂到 Service 上。Service 把端口名定好,后面 Ingress 和 ServiceMonitor 都按名字引用:
apiVersion: v1 kind: Service metadata: name: nakama namespace: nakama-system spec: selector: app: nakama ports: - port: 7350 targetPort: api name: api - port: 7351 targetPort: console name: console - port: 9100 targetPort: metrics name: metricsIngress 按域名拆开:客户端域名只指 API,Console 单独一个域名,后续给 Console 收权限时不用动客户端那条规则:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nakama namespace: nakama-system annotations: nginx.ingress.kubernetes.io/proxy-buffering: "off" spec: rules: - host: api.nakama.example.com http: paths: - path: / pathType: Prefix backend: service: name: nakama port: name: api - host: console.nakama.example.com http: paths: - path: / pathType: Prefix backend: service: name: nakama port: name: console7350 上同时跑 REST 和实时 Socket,nginx Ingress 默认支持 WebSocket 升级,加proxy-buffering: off是避免长连接被代理层截断。
📊 生产加固:HPA、指标采集与 Console 收口
弹性伸缩:HPA 盯 CPU 和会话数
CPU 指标兜底,自定义指标按会话数扩。下面这个例子按每 Pod 1000 个活跃会话触发扩容,指标名换成你实际接的 exporter 暴露的值:
apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: nakama namespace: nakama-system spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: nakama minReplicas: 3 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 - type: Pods pods: metric: name: nakama_active_sessions target: type: AverageValue averageValue: 1000指标采集:ServiceMonitor 指向 9100 端口
前面--metrics.prometheus_port 9100已经起了 exporter,官方 compose 里 Prometheus 就是抓这个端口的/路径,ServiceMonitor 照抄这个约定:
apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: nakama namespace: monitoring spec: selector: matchLabels: app: nakama endpoints: - port: metrics path: / interval: 15s安全:Console 域名加 NetworkPolicy
Console 能改玩家数据、执行运行时操作,不能对全网开放。两条底线:NAKAMA_TELEMETRY=0在 Deployment 里已经带上;Console 入口用 NetworkPolicy 圈到办公网段。
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: nakama-console-allow namespace: nakama-system spec: podSelector: matchLabels: app: nakama ingress: - from: - ipBlock: cidr: 203.0.113.0/24 ports: - port: 7351🚀 上线首日:验证命令和三个高频排障
验证按存储到服务的顺序来:Job 日志先绿,Pod 再全 Ready,最后在 Pod 内跑一次健康检查(成功时打healthcheck ok):
kubectl -n nakama-system logs job/nakama-migrate \ && kubectl -n nakama-system get pods -l app=nakama \ && kubectl -n nakama-system exec deploy/nakama -- /nakama/nakama healthcheck三个高频排障,按出现概率排:
- Pod CrashLoop,日志卡在连数据库——九成是 NetworkPolicy 拦了 26257,或者连接串里 namespace 没解析对。丢一个临时 Pod 实测:
kubectl -n nakama-system run dbtest --rm -it --restart=Never --image postgres:16 -- psql "host=cockroachdb-public port=26257 user=root dbname=defaultdb sslmode=disable"。 - Console 域名 404 或超时——先确认 Ingress 里 console 那条规则的端口名写的是
console而不是api,两者在 Service 里是独立的。 - 客户端换个节点就 401 / token 集体失效——副本间的
session.encryption_key不一致,通常是改了 Secret 只滚了一半副本,kubectl -n nakama-system rollout restart deploy/nakama让所有 Pod 吃到同一份密钥。
到这里,Nakama 在 K8s 上已经具备三副本存储、多节点服务和弹性伸缩,日常运维只剩盯 HPA 和指标两条线。 把清单里的 namespace 和两个域名换成你的,kubectl apply全套资源,再跑一次helm upgrade cockroachdb --set statefulset.replicas=3确认存储层到位。
【免费下载链接】nakamaScalable open-source game backend server: multiplayer, matchmaking, leaderboards, chat, and social features for games.项目地址: https://gitcode.com/GitHub_Trending/na/nakama
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考