Nginx 零停机灰度发布与回滚策略

从运维架构视角,结合 Nginx 的多种路由能力(权重、Header/Cookie、子域名、子路径)与容器编排(Docker/Kubernetes),构建“低风险、可审计、可回滚”的上线流程。本文给出平滑发布步骤、生产级配置、容器化集成与回滚预案。

0. 目标与原则

  • 不中断:对外 0 失败率、0 连接重置;
  • 可回滚:故障秒级回退;
  • 可观测:全链路指标与日志可追溯;
  • 可审计:变更有记录,可复现。

1. 平滑发布(通用步骤)

1) 版本准备:构建 v2 镜像(含健康检查、版本信息接口),在 v1 旁路启动; 2) 预热:v2 只接入探活与预热流量(本地缓存、JIT、连接池预连接); 3) 小流量灰度:按 1%/5%/10%/20%/50%/100% 切流,每步 5-15 分钟观察 SLI; 4) 监控门禁:4xx/5xx、P95/P99、错误率、特定业务 KPI(下单/支付成功率); 5) 扩展面:流量达到 100% 后保持观察窗口(30-60 分钟); 6) 收尾:下线 v1 或保留一段时间作为热备用。

SLI/SLO 建议:错误率 < 0.1%,P95 < 目标阈值(如 300ms),下单成功率不下降。


2. Nginx 路由策略

2.1 权重切流

upstream svc_v1 { server 10.0.0.1:8080 max_fails=2 fail_timeout=10s; }
upstream svc_v2 { server 10.0.0.2:8080 max_fails=2 fail_timeout=10s; }
map $upstream_choice $backend {
  default      svc_v1;
  v2           svc_v2;
}
# 灰度权重由外部工具写入变量(例如 lua_shared_dict / env / include 片段)
map $cookie_gray $upstream_choice {
  default      v1;
  ~*gray=1     v2;  # 指定用户灰度
}
server {
  location / {
    proxy_next_upstream error timeout http_502 http_503 http_504; # 故障向上游重试
    proxy_pass http://$backend;
  }
}

借助 Nginx JavaScript(njs)或 lua,按哈希实现稳定的百分比切分:

# 伪代码:基于 IP/用户ID 哈希到 0..99,<10 命中 v2(10%)

优势:用户命中稳定,不会在刷新间抖动;便于问题复现。

2.3 子路径/子域名灰度

  • 子路径:/v2/ 仅路由到 v2,便于 A/B 对比;
  • 子域名:v2.api.example.com 专供内测或机器人流量。

3. 容器化集成

3.1 Docker Compose(蓝/绿)

services:
  nginx:
    image: nginx:1.25
    volumes: ["./nginx.conf:/etc/nginx/nginx.conf:ro"]
    ports: ["80:80"]
  app_v1:
    image: app:1.0.0
    healthcheck: { test: ["CMD", "curl", "-f", "http://localhost:8080/health"], interval: 5s, retries: 5 }
  app_v2:
    image: app:1.1.0
    healthcheck: { test: ["CMD", "curl", "-f", "http://localhost:8080/health"], interval: 5s, retries: 5 }
  • 切换方式:通过替换 map 变量/包含片段,或修改 upstream 指向容器服务名;
  • 回滚:即时切回 app_v1

3.2 Kubernetes(Ingress/Service)

  • Ingress-Nginx + 两个 Service(v1/v2),通过 canary 注解分流:
    metadata:
    annotations:
      nginx.ingress.kubernetes.io/canary: "true"
      nginx.ingress.kubernetes.io/canary-weight: "10" # 10%
    
  • 替代方案:使用 nginx-ingress + njs/lua 做更复杂的路由,或使用 Gateway API/Service Mesh(Istio/Linkerd)进行百分比灰度、熔断、重试与熔断。

4. 策略矩阵与适用场景

  • 权重路由:最通用,适合整体灰度;
  • Cookie 灰度:便于定向用户/业务方验证;
  • Header 灰度:CI/CD/自动化探测流量;
  • 子路径/子域名:A/B 实验或大版本对照;
  • 哈希百分比:稳定命中,适合逐步放量。

5. 生产案例(示意)

  • 背景:交易系统网关,QPS 峰值 3w/s;
  • 步骤: 1) v2 部署完成,预热接口返回 200; 2) Cookie 灰度给内部账号与监控机器人; 3) 百分比灰度 1% -> 5% -> 10% -> 20%(每步 10 分钟),观察错误率、P95、下单成功率; 4) 50% -> 100%,保持观察 30 分钟; 5) 稳定后下线 v1,保留应急镜像与配置。
  • 指标与日志:接入 Prometheus/Grafana,日志落 ES/ClickHouse,保留版本号与路由信息便于追踪。

6. 回滚策略与演练

  • 触发条件:错误率 > 0.2% 或 P95 恶化 30% 且持续 5 分钟;
  • 动作: 1) 立即将灰度比例设为 0(或 Cookie 开关关闭); 2) 恢复 v1 权重至 100%; 3) 保持观察窗口(10-30 分钟),同时收集 v2 诊断材料; 4) 进入问题单流程与修复迭代;
  • 演练:季度至少一次“带压回滚”演练(非峰值时段),验证脚本与值守响应。

7. 关键配置清单

  • 上游健康检查与 proxy_next_upstream 策略;
  • keepalive 连接池,proxy_http_version 1.1 与关闭 Connection: close
  • 请求超时/重试上限(避免风暴);
  • 限流与熔断(njs/lua 或接入网关/Service Mesh)。

8. 审计与自动化

  • 配置即代码(Git 管控),灰度参数来自集中配置;
  • CI/CD:合规检查(lint)、预热检查通过才允许放量;
  • ChatOps:发布与回滚都有机器人宣告与记录。

9. 常见坑

  • 预热不足:v2 首次请求抖动;
  • 粘性策略缺失:会话跨版本导致登录/购物车异常;
  • 观测延迟太长:等到告警触发时已影响大量用户;
  • 权限与合规:回滚权限受限导致响应慢。

结论:Nginx + 容器编排可实现高可靠的零停机灰度。把“参数化灰度 + 可观测 + 自动化回滚”做成流程与工具,才是长期可靠之道。