五种发布模式速查

模式 一句话 主要代价
滚动(Rolling) 一批一批替换旧 Pod 同时存在新旧两版本,DB schema 要兼容
重建(Recreate) 全停旧的,再起新的 有停机窗口;只能用在能停的服务
蓝绿(Blue/Green) 起一套完整新环境,切流量 资源翻倍
金丝雀(Canary) 先发 1%,看正常再扩大 流量切分 + 监控配套
影子(Shadow / Mirror) 真流量复制到新版本,结果不返回用户 实现复杂,对外部副作用风险

滚动发布(K8s 默认)

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 1           # 最多多起 1 个新 Pod
    maxUnavailable: 0     # 旧 Pod 不能少(保证容量)

K8s Deployment 默认用滚动——绝大多数业务这就够

必须前向兼容

  • 新代码必须能读旧版数据库 schema
  • 数据库迁移先于代码上线(加列、加索引);删列要等所有旧 Pod 退完再做

蓝绿

[Blue(线上)] ← Service → 用户
[Green(新版)]   待机

切换:Service selector 从 blue → green
出问题:切回 blue

优点:

  • 切换瞬时(DNS / Service 改一行)
  • 回滚瞬时

代价:

  • 资源翻倍——同时跑两套
  • 数据库还是只有一套,schema 兼容仍是必须

金丝雀

90% 流量 → v1(旧)
10% 流量 → v2(新)
观察 10 分钟 / 1 小时 / 1 天 → 没问题逐步加比例

实现层:

  • Ingress / Service Mesh 层做权重切分(Istio / Linkerd / Nginx Ingress 都支持)
  • Argo Rollouts:K8s 原生金丝雀控制器,可联动 Prometheus 自动决策
  • Flagger:类似,配 Linkerd / Istio

关键是有自动判据

  • 错误率上升超过阈值 → 自动回滚
  • p99 延迟上升 → 暂停 / 回滚

没有判据的"手动观察金丝雀" = 让人盯屏幕,扩大不可避免会被忘掉。

影子(Shadow)

复制真流量到新版本,但不返回响应给用户

  • 验证新版本在真实流量下不崩
  • 验证性能 / 资源用量
  • 不影响线上

⚠ 注意副作用:

  • 不能写数据库(或写到独立 shadow DB)
  • 不能调外部 API(或外部 API 是只读的)
  • 实现成本高

特性开关(Feature Flag)

正交于上面所有策略:

if flag("new-pricing-engine", user=user):
    return new_pricing(user)
return old_pricing(user)

部署 ≠ 启用:代码先上线(走滚动 / 蓝绿),开关默认关;通过控制台逐步打开。

工具:

  • LaunchDarkly / Statsig / GrowthBook:商业 / 开源 flag 平台
  • OpenFeature(CNCF):标准 SDK + 多供应商对接
  • Unleash:开源自托管

Feature Flag 是把"部署"和"发布"解耦的最有效工具。强烈推荐配合金丝雀使用

选择决策树

能停服务(夜间维护窗口)→ Recreate(最简单)
不能停 + 资源够 + 想最快回滚 → 蓝绿
不能停 + 资源紧 + 业务量大 → 金丝雀(推荐有 K8s 的团队)
风险特别高(核心算法)→ 金丝雀 + Feature Flag + Shadow

数据库迁移的"双写期"

无论哪种部署策略,DB 改动跨版本时都要遵循:

1. 加列 / 加表(向后兼容)→ 部署
2. 新代码同时读写新旧 schema(双写)→ 部署
3. 数据回填
4. 切换为只用新 schema(旧的还留着)→ 部署
5. 删旧列 / 旧表 → 部署

每一步都先部署再做下一步,每一步都可独立回滚。这是大型 schema 改动唯一稳妥的姿势。

推荐阅读

至此完成"CI/CD 与发布"4 篇。下一批进入可观测性:三支柱 / Prometheus / 日志 / OTel / 告警。