1. 6.4 生产环境部署建议

6.1–6.3 解决「看见系统」;本节从 拓扑、容器、探针、依赖与退出 角度归纳生产约定。二进制名、配置路径以你的 cmd/ 与仓库为准,文中 server 仅为示例。

1.1. 6.4.1 部署架构

一个典型的生产部署:

                    ┌─────────────┐
                    │   Etcd × 3   │  服务发现
                    └──────┬──────┘
                           │
                    ┌──────┴──────┐
                    │   NATS × 3   │  跨进程通信
                    └──────┬──────┘
                           │
            ┌──────────────┼──────────────┐
            │              │              │
     ┌──────┴─────┐ ┌──────┴─────┐ ┌──────┴─────┐
     │  进程 1     │ │  进程 2     │ │  进程 3     │
     │ Gate+IM    │ │ Gate+IM    │ │ Gate+Chat   │
     │ Match      │ │ Chat       │ │ Match       │
     └─────────────┘ └─────────────┘ └─────────────┘
            │              │              │
     ┌──────┴─────┐ ┌──────┴─────┐ ┌──────┴─────┐
     │ Prometheus  │ │   Grafana   │ │   Jaeger    │
     └─────────────┘ └─────────────┘ └─────────────┘

关键依赖:

  • 单进程部署:无外部依赖(Etcd/NATS 都不需要)
  • 多进程部署:Etcd(服务发现)+ NATS(跨进程通信)
  • 可选:Prometheus + Grafana(强烈建议)

1.2. 6.4.2 容器化

1.2.1. Dockerfile

FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o server ./cmd/server

FROM alpine:3.19
RUN apk add --no-cache ca-certificates tzdata
COPY --from=builder /app/server /server
COPY --from=builder /app/configs /configs
EXPOSE 8000 9090
ENTRYPOINT ["/server"]

注意事项:

  • CGO_ENABLED=0:静态编译,避免依赖 glibc
  • 多阶段构建:最终镜像只有二进制 + 配置文件,约 20MB
  • 时区数据tzdata 确保日志时间正确

1.2.2. K8s 部署

apiVersion: apps/v1
kind: Deployment
metadata:
  name: zhenyi-server
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: server
        image: zhenyi:latest
        ports:
        - containerPort: 8000  # 客户端连接
        - containerPort: 9090  # Prometheus 指标
        env:
        # 需要稳定分片序号时请用 StatefulSet,并从 pod 名或 Downward API 解析 index;
        # 普通 Deployment 没有 statefulset 的 pod-index 标签,勿照搬此字段。
        resources:
          requests:
            cpu: "1"
            memory: "512Mi"
          limits:
            cpu: "4"
            memory: "2Gi"
        livenessProbe:
          httpGet:
            path: /healthz
            port: 9090
          initialDelaySeconds: 5
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /readyz
            port: 9090
          initialDelaySeconds: 5
          periodSeconds: 5
        lifecycle:
          preStop:
            exec:
              command: ["sh", "-c", "sleep 5"]  # 等待 NATS/Etcd 感知下线

1.2.3. StatefulSet vs Deployment

如果需要固定的进程索引(比如 PROC_INDEX=0 用于区分分片),用 StatefulSet

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: zhenyi-server
spec:
  serviceName: zhenyi
  replicas: 3
  # Pod 名: zhenyi-server-0, zhenyi-server-1, zhenyi-server-2

StatefulSet 保证 Pod 名称和顺序稳定,适合需要进程 ID 作为分片标识的场景。

如果不需要固定索引,普通 Deployment 就够了。

1.3. 6.4.3 Etcd 部署

1.3.1. 推荐配置

Etcd 至少 3 节点集群(容忍 1 节点故障):

# 关键参数
--quota-backend-bytes=8589934592  # 8GB 存储上限
--max-request-bytes=1048576         # 1MB 单次请求上限
--snapshot-count=10000              # 每 1 万次提交做快照
--auto-compaction-retention=1h       # 1 小时后自动压缩

1.3.2. 资源需求

规模 CPU 内存 磁盘
小型(< 10 节点服务) 1 核 512MB SSD 10GB
中型(10~50 节点) 2 核 1GB SSD 50GB
大型(> 50 节点) 4 核 2GB SSD 100GB

Etcd 对磁盘 I/O 敏感,必须用 SSD,不要用网络存储。

1.4. 6.4.4 NATS 部署

1.4.1. 单节点(开发/小型)

nats-server -js  # 启用 JetStream(可选持久化)

1.4.2. 集群(生产)

3 节点集群:

# 节点 1
nats-server --name nats-1 \
  --cluster nats://0.0.0.0:6222 \
  --routes nats://nats-2:6222 --routes nats://nats-3:6222 \
  --port 4222 --monitor 8222

# 节点 2、3 类似

1.4.3. 资源需求

规模 CPU 内存
小型 0.5 核 128MB
中型(< 10 万 QPS) 1 核 256MB
大型(> 10 万 QPS) 2~4 核 512MB~1GB

1.5. 6.4.5 资源配置建议

1.5.1. zhenyi 进程

场景 CPU 内存 goroutine
开发 1 核 256MB < 100
生产(单 Gate + 少量 Actor) 2 核 1GB < 500
生产(Gate + 多业务 Actor) 4 核 2GB < 2000
生产(高并发 + 大量在线) 8 核 4GB < 10000

goroutine 数量可以通过 go_goroutines 指标监控。如果超过预期,可能有 goroutine 泄漏。

1.5.2. GOMAXPROCS

默认等于 CPU 核心数。如果容器限制了 CPU(比如 limit 4 核),K8s 会自动设置。手动设置:

runtime.GOMAXPROCS(runtime.NumCPU())

1.5.3. GOGC

默认 100(堆达到当前 2 倍时触发 GC)。对低延迟场景,可以调高:

debug.SetGCPercent(200)  // 堆达到 3 倍才 GC,减少频率但增加内存

通过 go_gc_gogc_percent 指标可以监控当前值。

1.6. 6.4.6 优雅退出

zhenyi 的优雅退出流程:

收到 SIGTERM
    ↓
1. metricsSrv.SetDraining() → /healthz 与 /readyz 均可返回 503(draining/不健康语义见 zmetrics/server.go)
    ↓
2. 停止接受新连接(网络层关闭监听)
    ↓
3. 等待 Mailbox 排空(Actor 处理完队列中的消息)
    ↓
4. Etcd 注销(Unregister + Revoke lease)
    ↓
5. NATS 连接关闭
    ↓
6. 进程退出

K8s 的 terminationGracePeriodSeconds 应该设置得比排空时间长:

spec:
  terminationGracePeriodSeconds: 30  # 给 30 秒排空时间
  containers:
  - name: server
    lifecycle:
      preStop:
        exec:
          command: ["sh", "-c", "sleep 5"]  # 额外等 5 秒,确保 NATS/Etcd 感知

1.6.1. 热更新配合

利用第三章讲的热更新能力,很多参数调整不需要重启:

// 运行时调整,不需要重启
actor.UpdateWorkerPoolSize(64)
actor.UpdateRateLimit(10000, 20)

但 Actor 代码变更仍然需要重启(Go 是编译型语言)。

1.7. 6.4.7 配置管理

1.7.1. 外部化配置

不要硬编码配置,用环境变量或配置文件:

// 推荐方式
etcdURL := os.Getenv("ETCD_URL")         // "http://etcd:2379"
natsURL := os.Getenv("NATS_URL")         // "nats://nats:4222"
metricsAddr := os.Getenv("METRICS_ADDR") // ":9090"

1.7.2. 敏感信息

密码、密钥不要放在环境变量里(K8s 会明文记录到 Pod spec 中)。用 Secret:

apiVersion: v1
kind: Secret
metadata:
  name: zhenyi-secret
type: Opaque
stringData:
  db-password: xxx
  tls-cert: |
    -----BEGIN CERTIFICATE-----
    ...

1.8. 6.4.8 监控告警规则

1.8.1. 必须告警的指标

groups:
- name: zhenyi-critical
  rules:
  # Actor 频繁重启
  - alert: ActorRestartTooFrequent
    expr: rate(zhenyi_actor_restarts_total[5m]) > 0.1
    for: 2m
    labels:
      severity: critical

  # 消息丢失
  - alert: MessagesDropped
    expr: rate(zhenyi_actor_msg_dropped_total[5m]) > 0
    for: 1m
    labels:
      severity: critical

  # NATS 发布失败
  - alert: NatsPublishError
    expr: rate(zhenyi_nats_publish_errors_total[5m]) > 0
    for: 1m
    labels:
      severity: critical

  # 对象池重复释放(有 bug)
  - alert: MsgPoolDoubleRelease
    expr: increase(zhenyi_msgpool_double_release_total[5m]) > 0
    for: 0m
    labels:
      severity: warning

  # RPC 超时率过高
  - alert: RPCTimeoutRateHigh
    expr: |
      rate(zhenyi_rpc_timeout_total[5m]) 
      / rate(zhenyi_rpc_sent_total[5m]) > 0.1
    for: 5m
    labels:
      severity: warning

1.8.2. 建议告警的指标

- name: zhenyi-warning
  rules:
  # 处理延迟飙升
  - alert: HandlerLatencyHigh
    expr: |
      histogram_quantile(0.99, 
        rate(zhenyi_handler_latency_ms_bucket[5m])) > 100
    for: 5m
    labels:
      severity: warning

  # 队列堆积
  - alert: MailboxBacklog
    expr: zhenyi_actor_queue_depth > 1000
    for: 5m
    labels:
      severity: warning

  # goroutine 泄漏
  - alert: GoroutineLeak
    expr: go_goroutines > 10000
    for: 10m
    labels:
      severity: warning

1.9. 6.4.9 日志采集

1.9.1. 推荐方案:Loki + Promtail

轻量、原生支持 Kubernetes:

# Promtail 配置
scrape_configs:
- job_name: zhenyi
  kubernetes_sd_configs:
  - role: pod
  pipeline_stages:
  - json:
      expressions:
        level: level
        msg: msg
        traceIdHi: traceIdHi
        actorId: actorId
  - labels:
      level:
      traceIdHi:

1.9.2. 日志保留策略

环境 保留时间 原因
开发 3 天 够排查就行
生产 7~30 天 满足运维排查和审计要求

zhenyi 的日志默认输出到 stdout,容器化后由日志采集组件收集。

1.10. 6.4.10 安全加固

1.10.1. 网络层

  • Gate 端口只对客户端开放,不暴露内网服务
  • Etcd 和 NATS 端口只在内网开放
  • K8s NetworkPolicy 限制 Pod 间通信

1.10.2. TLS

zhenyi 支持标准 TLS 和国密 GM-TLS:

// 标准 TLS
gate.SetStandardTLS("server.crt", "server.key")

// GM-TLS(信创环境)
gate.SetTLSConfig(&baseziface.TLSConfig{
    Mode: baseziface.TLSModeGMTLS,
    CertFile: "sm2_cert.pem",
    KeyFile:  "sm2_key.pem",
})

1.10.3. 限流

单连接级限流防止恶意客户端:

限流阈值来自 ActorConfig.Rate / Burst 或运行时 UpdateRateLimit(见 3.4);与 Gate/Channel 绑定方式以你的启动代码为准。

1.11. 6.4.11 本节要点

  1. 模式:单机可无 Etcd/NATS;多进程需 发现 + TopicBus(常用 Etcd + NATS)。
  2. 镜像多阶段 + CGO_ENABLED=0;暴露 业务端口 + zmetrics 端口(如 9090)。
  3. 探针/healthz /readyzSetDraining 后两者均可 503,配合 terminationGracePeriodSeconds
  4. 配置:环境变量/Secret;敏感数据优先 K8s Secret 而非明文 env。
  5. 可观测:Prometheus 抓取 /metrics;日志 stdout;追踪见 6.3
  6. 安全SetStandardTLS / SetTLSConfig(类型在 zhenyi-base/ziface);内网隔离 Etcd/NATS。

第七章(大纲)进入实战业务组装;本章素材已覆盖运维侧常见决策点。

results matching ""

    No results matching ""