55.1 发布策略 (滚动 / 蓝绿 / 金丝雀)
一句话
发布策略决定 “新版本怎么替换旧版本”,核心权衡是风险 ↔ 成本 ↔ 速度。 没有最好的策略,只有最适合你回滚能力的策略。
四种策略一张图看全
| 策略 | 做法 | 停机 | 风险 | 回滚速度 | 资源成本 | 实现难度 |
|---|---|---|---|---|---|---|
| 停机发布 | 停服务 → 更新 → 启动 | 有 | 中 | 慢 | 1× | 最低 |
| 滚动发布 | 逐台替换,边更新边服务 | 无 | 中 | 慢(逐台滚回) | 1× | 低 |
| 蓝绿发布 | 备一套全新环境,流量一次切换 | 无 | 低 | 秒级 | 2× | 中 |
| 金丝雀发布 | 新版本先给 1–5% 流量,观察后放量 | 无 | 最低 | 快(切走流量) | 略 >1× | 高 |
逐个拆解
① 停机发布 (Big Bang / Recreate)
做法:停掉旧版本 → 部署新版本 → 启动。
- 优点:最简单,没有 “新旧共存” 的问题,不用考虑兼容。
- 缺点:有停机窗口 (哪怕 10 秒)。
- 适合:内部系统、半夜低峰可停机的服务、早期产品。
- 现实:K8s 里的
Recreate策略就是它——先杀光旧 Pod,再起新 Pod。
💡 别瞧不起它。“能停机” 是一种巨大的奢侈——它意味着你可以不做任何兼容性设计。很多系统为了 “不停机”,付出了远超停机窗口价值的复杂度。
② 滚动发布 (Rolling Update)
做法:一台一台地替换。更新完一个实例、验证通过,再更新下一个,直到全部换成新版本。
- 优点:不停机、不需要额外资源。
- 缺点:发布期间新旧版本同时在线 (这是关键,见下面的铁律);回滚也是滚动的 (慢)。
- 适合:无状态服务。这是 K8s 的默认策略。
K8s 里控制滚动速度的两个参数:
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 滚动期间最多多起几个新 Pod(腾挪空间)
maxUnavailable: 0 # 滚动期间最多允许几个 Pod 不可用(0 = 不中断服务)
🔑 滚动发布的命门是 “新旧共存窗口”:发布过程中,一部分请求打到新版本,一部分打到旧版本。 如果新旧版本不兼容 (接口变了、数据格式变了),这个窗口里就会随机报错——而且是 “时好时坏” 的随机报错,最难查。
③ 蓝绿发布 (Blue-Green)
做法:备一套完整的新环境 (绿),旧环境 (蓝) 继续服务;新环境验证通过后,把流量一次性从蓝切到绿。
┌─────┐ 切换 ┌─────┐
用户 ──▶│ 蓝 │ ─────▶ │ 绿 │
│旧版本│ │新版本│
└─────┘ └─────┘
- 优点:回滚是秒级的 (把流量切回蓝即可),风险低。
- 缺点:资源成本翻倍 (两套环境同时存在)。
- 关键问题——数据库怎么办:
| 方案 | 做法 | 代价 |
|---|---|---|
| 共享一个数据库 | 蓝绿只切应用,不切数据库 | 简单,但数据库 schema 变更必须向后兼容(否则绿的代码读不懂蓝的数据) |
| 各自独立数据库 | 蓝绿各带一套库 | 切流量前要处理数据同步/迁移,复杂 |
- 适合:核心链路、对 “回滚要快” 有强要求的场景。但先想清楚数据库,再谈蓝绿。
⚠️ 蓝绿最大的坑:只做了应用蓝绿,没做数据库兼容。结果一切到绿,绿代码读不懂旧表结构报错;想切回蓝,蓝代码也读不懂绿改过的数据——卡死,两头都不是。
④ 金丝雀发布 (Canary)
做法:新版本先只接 1–5% 流量,盯着指标看;没问题再逐步放量 (5% → 25% → 50% → 100%)。
用户流量 ──┬── 95% ──▶ 旧版本
└── 5% ──▶ 新版本(金丝雀)← 出了问题只炸这 5%
- 优点:风险最小——真出了问题,只影响一小撮用户,且能立刻切走。
- 缺点:需要流量治理能力 (按比例 / 按用户路由);实现和运维成本高。
- 怎么分流:
| 方式 | 做法 |
|---|---|
| 权重路由 | 网关/Ingress 按 95:5 比例分发 |
| 用户分片 | 按 userID % N 或 hash 把固定用户导到新版本 |
| Service Mesh | Istio 的 traffic shifting,按权重切 |
- 看什么指标再放量 (不是 “看看没报错”):
| 指标 | 看什么 |
|---|---|
| 错误率 | 金丝雀版本错误率是否显著高于旧版 |
| 延迟 | P99 是否恶化 |
| 业务指标 | 下单成功率、支付成功率 |
- 适合:有流量治理能力、流量足够大的服务 (流量太小,5% 样本没意义)。
三条铁律
- **回滚能力比发布能力更重要。**判断发布策略好不好,先问 “回滚要几秒、演练过没”。
- **滚动 / 金丝雀期间,新旧版本必须兼容。**因为两者会同时在线——兼容不是可选项,是前提。
- 数据库变更必须走 Expand-Contract (先加列 → 迁移 → 再删列)。否则一旦改了表,新旧两个版本都没法回滚。(详细见 55.4 数据库变更管理 (Expand-Contract))
怎么选:一张决策表
| 你的情况 | 选 |
|---|---|
| 内部系统、能接受停机 | 停机发布(别为它过度设计) |
| 无状态服务、要不停机、想简单 | 滚动发布(K8s 默认) |
| 核心链路、回滚必须秒级、能接受双倍成本 | 蓝绿发布(先解决数据库) |
| 流量大、有流量治理、要风险最小 | 金丝雀发布 |
🔑 现实里最常见的是组合:滚动 + 金丝雀——先放 5% 金丝雀,再滚动全量。既拿了金丝雀的低风险,又不用长期养两套环境。
反模式
| 反模式 | 后果 |
|---|---|
| 蓝绿只切应用、不处理数据库 | 切过去报错,切回来也报错,卡死 |
| 金丝雀放量不看指标,只"看看没报错" | 5% 时的隐性 bug 在 100% 时爆炸 |
| 滚动期间新旧接口不兼容 | 随机报错,时好时坏,最难查 |
| 没有演练过回滚 | 真出事的回滚,大概率失败 |
落到数字:批次、观察窗口与阈值
| 策略 | 批次 | 观察窗口 | 自动回滚阈值 |
|---|---|---|---|
| 滚动 | 25% × 4 | 每批 2–5min | 错误率 > 1% 或 P99 > 基线 1.5× |
| 蓝绿 | 0% → 100% | 切换后 5–15min | 同上,回滚 = 切回旧池 |
| 金丝雀 | 1% → 5% → 25% → 100% | 每档 5–30min | 错误率 / 延迟 / 业务指标三选二 |
| 停机 | — | — | 有维护窗口才用 |
# Argo Rollouts 风格(示意)
strategy:
canary:
steps:
- setWeight: 1
- pause: {duration: 10m}
- setWeight: 5
- pause: {duration: 10m}
- analysis: {templates: [{templateName: error-rate}]}
- setWeight: 25
关键不在 “怎么切”,而在 “切多少、看多久、什么指标算坏”——这三件事没有数字,金丝雀就只是 “慢一点的滚动”。
自检
Q1 你选蓝绿发布,但应用绿环境读旧数据库时一直报错。根因是什么?怎么提前避免?
▸ 答案:只做了应用蓝绿,没做数据库兼容。根因是数据库 schema 变更没走 Expand-Contract——绿代码读不懂旧表结构。避免方法:先做向后兼容的 schema 变更 (先加列),再切换;回滚时蓝代码也能读懂绿改过的数据。
Q2 滚动发布期间,监控显示错误率 “时好时坏”、随机波动。最可能的原因是什么?
▸ 答案:新旧版本不兼容。发布窗口里一部分请求落到新版本、一部分落到旧版本,接口或数据格式不一致导致随机报错。滚动发布的前提就是 “新旧兼容”——这个前提没满足,滚动就是事故。
Q3 金丝雀的 “坏” 用什么指标判定?
▸ 答案:至少两类:技术指标(错误率、P99、CPU)与业务指标(下单成功率、支付转化)。只看技术指标会漏掉 “接口 200 但业务失败”;只看业务指标则反应太慢。常见做法是 “错误率 1% 或业务成功率下降 2% 即回滚”。
Q4 蓝绿发布最大的隐性成本是什么?
▸ 答案:双倍资源与数据兼容:绿环境要完整跑起来,资源翻倍;更贵的是数据库要同时兼容新旧代码(Expand-Contract),否则切换瞬间就报错。蓝绿不是 “点一下切流量”,而是 “两套兼容性都要维护”。
Q5 数据库变更怎么和发布策略配合?
▸ 答案:走 Expand → Migrate → Contract:先加列/加表(新旧都能跑),再双写/回填,最后删旧列。任何 “先删后发” 的做法,都会让滚动/蓝绿在切换窗口里直接失败——回滚也没用,因为旧代码已经读不到数据了。