cardserver · 卡牌游戏服务端
cardserver 是 game 命名空间下的卡牌(card)游戏服务端,镜像构建自 ghcr.io/yldm-tech/cardserver。它以 Gin(GIN_MODE=release)对外提供 HTTP 与 WebSocket 服务,依赖 PostgreSQL 持久化、Redis 做缓存与 PubSub,并向 Consul 注册自身以供服务发现。配置里声明了 tick_rate: 30 / send_rate: 10 / max_players: 100 等游戏循环参数,属于一个有状态游戏逻辑但无本地持久卷的无状态副本部署。
applications/game/cardserver/ 下的 manifest。CLAUDE.md 中的 game 专门踩坑段只覆盖 DNF 私服(dnf-server),不涉及 cardserver,故本页不转述 DNF 相关内容。部署形态
- Name
- 命名空间
- Description
- game(kustomization 设置
namespace: game+namePrefix: game-,生成资源名game-cardserver-*)
- Name
- 工作负载
- Description
- Deployment,
RollingUpdate(maxSurge: 1/maxUnavailable: 0)
- Name
- 镜像
- Description
ghcr.io/yldm-tech/cardserver:2.3.1(tag 由 argocd-image-updater 按 semver 写回 kustomizationimages:),imagePullPolicy: Always,拉取用ghcr-secret
- Name
- 副本数
- Description
- 2(与 HPA
minReplicas一致)
- Name
- 端口
- Description
- 容器
http8080、metrics9090
- Name
- 资源
- Description
- requests
250mCPU /256Mi,limits500mCPU /512Mi
- Name
- 调度
- Description
- 未设 nodeSelector / workload 标签;
podAntiAffinity以preferred(weight 100,topologyKey: kubernetes.io/hostname)尽量把两个副本分散到不同节点
- Name
- 探针
- Description
- startupProbe
/health(最多 12×5s),livenessProbe/health(15s 周期),readinessProbe/health/ready(5s 周期)
- Name
- 优雅退出
- Description
preStop先sleep 15,terminationGracePeriodSeconds: 30
- Name
- 存储
- Description
- 无 PVC;只挂载两个 ConfigMap(
base-config挂到/root/configs/base,cardserver-config的card.yaml挂到/root/configs/services/games/card.yaml),均 readOnly
配置与依赖
游戏配置来自 ConfigMap cardserver-config 的 card.yaml,关键项:game.tick_rate: 30、send_rate: 10、max_players: 100、world_width/height: 2000;数据库 driver 为 postgres、库名默认 yldm_card、max_open_conns: 25、迁移目录 migrations/card;Redis db: 4、key 前缀 card:;PubSub 用 Redis 引擎(前缀 card:pubsub:,开启 dead_letter,保留 168h)。card.yaml 内用 ${VAR:default} 占位,实际值由下面的环境变量注入。
数据库连向 pgbouncer.postgres.svc.cluster.local(DB_HOST 在 deployment 中硬编码为该地址),即经 PgBouncer 连接池访问 PostgreSQL。服务注册指向 consul.consul.svc.cluster.local:8500(CONSUL_ENABLED=true),以 service_name cardserver、tags game/card/v1 注册并暴露 /health 健康检查。限流相关 env:RATE_LIMIT_GLOBAL=1000、RATE_LIMIT_BURST=2000。
敏感配置由 ExternalSecret cardserver-db-secret(ClusterSecretStore vault-backend,refreshInterval: 1h)从 Vault 拉取,合成同名 Secret 后注入容器:
| Secret key | Vault remoteRef.key | property |
|---|---|---|
| DB_PORT / DB_NAME / DB_USER / DB_PASSWORD / REDIS_DB | yldm/production/cardserver | db_port / db_name / db_user / db_password / redis_db |
| REDIS_HOST / REDIS_PORT / REDIS_PASSWORD | yldm/production/common | redis_host / redis_port / redis_password |
| JWT_SECRET / JWT_EXPIRES_IN / JWT_REFRESH_EXPIRES_IN | yldm/production/common | jwt_secret / jwt_expires_in / jwt_refresh_expires_in |
| DB_ADMIN_USER / DB_ADMIN_PASSWORD | yldm/database | user / password |
注意 DB_HOST 不来自 Vault,而是 deployment 里直接写死的 PgBouncer 地址;JWT 与 Redis 凭据走共享路径 yldm/production/common,DB 管理员账号走共享路径 yldm/database。
访问与监控
集群内通过 Service cardserver(ClusterIP,port 80 → targetPort 8080)访问;另有一个 headless Service cardserver-headless(clusterIP: None),其 manifest 中 namespace 写的是 app(与主 Service 的 game 不同,且 kustomization 会统一覆盖为 game,最终名 game-cardserver-headless)。
外部入口为 Ingress cardserver-ingress,host card.yldm.tech,ingressClassName: traefik,后端指向 game-cardserver:80。TLS 证书由 cert-manager letsencrypt-prod ClusterIssuer 签发到 cardserver-tls-cert。DNS 经 external-dns 写一条 CNAME 指向 Cloudflare Tunnel(*.cfargotunnel.com,proxied)。为支持 WebSocket,Ingress 标注了 websocket-services: game-cardserver、读写/连接超时各 3600s,并启用 cookie 粘性会话(cardserver-session,max-age 10800s);另有 proxy-body-size: 10m 与 limit-rps: 100。注意这些 nginx.ingress.kubernetes.io/* 注解为 nginx ingress 语义,而 ingressClassName 是 traefik —— 部分注解在 traefik 下未必生效,以集群实际 ingress controller 行为为准。
监控方面,Pod 带 prometheus.io/scrape: "true"、prometheus.io/port: "9090"、prometheus.io/path: "/metrics" 注解(annotation 抓取),METRICS_ENABLED=true。本目录下没有 ServiceMonitor 或 PrometheusRule。弹性与可用性:HPA cardserver-hpa 按 CPU 70% / 内存 80% 在 minReplicas: 2 ~ maxReplicas: 8 间伸缩;VPA cardserver-vpa 为 updateMode: "Off"(仅给建议,区间 100m1 CPU、128Mi1Gi);PDB cardserver 设 minAvailable: 1。
返回 game 总览