滚动重启、金丝雀发布、AB testing 和蓝绿部署
本文讨论发布周期(release cycles)里 deployment strategy 的问题,抛开大规模部署的 big bang deployment。
滚动重启、金丝雀发布、AB testing
在 martin fowler 的博客里,金丝雀发布和滚动重启和 AB testing 并没有本质区别,都是 phased approach或者 incremental approach,是 ParallelChange 思想的实践。
当我们拥有一个新版本时:
滚动重启(rolling restart)
rolling restart 会让新旧版本在环境里长时间共存,逐一使节点部署新版本,这样易于发现问题和回滚。
金丝雀发布(canary release)
而金丝雀发布同样允许新旧版本长时间共存,在逐一部署新节点的前提下,逐步利用 LB 之类的基础设施来切分用户,其策略还可以细分为:
先不给新版本,在无流量的情况下在生产环境验证 - 很多大厂的实现都忽略了这点。
尽量让内部用户先使用 - FB 之类的大厂的员工都非常多,使用一个特性开关(名字很多,比如 feature bits, flags, flippers, switches, martin fowler prefers FeatureToggle),单独让内部员工使用,来检查其中的问题。 amazon 使用暗部署(dark launch),而蚂蚁金服使用灰度环境(grey environment) 来将生产的真流量释放到新版本上。
然后逐步开放给新用户使用。这个过程中涉及到的策略和方法是:使用 LB 的路由策略,将流量逐一发布到特定新版本节点上;基于用户选择,只有特定用户的流量可以进入到新版本的机器里。大部分大厂都采用基于节点的流量分配法则,实际上还可以根据源 ip、地理位置和用户人群划分来解决这个问题。
AB testing
金丝雀发布因为可以使不同的人群体验不同版本,所以可以被看作 AB testing 的等同 implementation。
但是,金丝雀发布的用意是“发现新版本问题,提供回滚的灵活性”,而 AB testing 的用意是为了验证和比对不同的具体策略的效果。金丝雀发布必然导致软件的新版本代替旧版本(注意软件的版本和代码的版本是不一样的),间接地提供了 AB testing 的能力;而真正厉害的 ab testing 却可以不依赖软件版本更新,只把用户加以区分,并配以不同的策略即可。
蓝绿部署
蓝绿部署的特性要求:
1 旧版本存在于蓝集群。
2 新版本部署于绿集群。
绿集群在上线的时候完全没有流量,在充分验证完成以后一下子通过 LB 把流量完全切入绿集群。
金丝雀发布在事实上是在同一套物理环境里实现渐进式替换,优点是要求的节点数量更小,发现 last minute 问题的时候影响面更可控,缺点是出了问题也要逐步回滚,系统是在进行有损服务的。
蓝绿部署要求生产环境有两套环境,优点是可以直接通过 LB 一键回滚,缺点是占用节点数量过多,出现 last minute 问题的时候影响面更大。蓝绿部署的缺点是大部分公司不直接采用它的原因。
特性开关
特性开关的细节可以参考Feature Toggles (aka Feature Flags),不同的开关实际上体现的是不同的精细化程度。
特性开关的要点是解耦 decision point 和 decision 决策逻辑。
维护这些开关的长期性和动态性实际上需要很重的架构权衡。

影子部署(Shadow Deployment / Traffic Mirroring)
影子部署是一种高级的发布策略,它将生产环境的流量镜像复制到新版本服务上,但新版本的响应不会被返回给用户。这种策略主要用于:
- 在生产环境中验证新版本的正确性,而不影响真实用户
- 测试新版本的性能和稳定性
- 验证数据库迁移的正确性
- 在实际流量下进行压力测试
优点:
- 完全不影响用户体验
- 可以在真实生产流量下验证新版本
- 可以提前发现潜在问题
缺点:
- 需要双倍的计算资源
- 增加了监控和日志的复杂度
- 不适合有写操作的场景(可能造成数据不一致)
典型实现: Istio 的 Traffic Mirroring 功能
红黑部署(Red-Black Deployment)
红黑部署实际上是蓝绿部署的一种变体,两者的核心思想完全一致:
- 维护两套独立的生产环境
- 新版本在完全隔离的环境中进行验证
- 通过切换流量实现版本切换
与蓝绿部署的区别:
- 命名习惯不同:蓝绿 vs 红黑
- 红黑部署通常更强调版本间的完全隔离和并行运行
- 某些实现中,红黑部署可能同时运行多个版本(红、黑、黄等)
在实际应用中,红黑部署和蓝绿部署可以视为同一类策略的不同命名方式。
渐进式交付(Progressive Delivery)
渐进式交付是一种发布理念的统称,它强调通过渐进式的方式来发布新功能,包括但不限于:
- 金丝雀发布
- 蓝绿部署
- A/B 测试
- 特性开关
- 影子部署
渐进式交付的核心价值在于:
- 降低风险:渐进式地暴露新功能,发现问题可以快速回滚
- 快速反馈:通过真实用户的反馈来优化产品
- 精细控制:可以基于用户属性、地理位置、设备类型等进行精准的流量控制
- 自动化验证:结合自动化测试和监控,实现智能化的发布决策
各部署策略对比
| 策略 | 资源占用 | 回滚速度 | 风险控制 | 适用场景 |
|---|---|---|---|---|
| 滚动重启 | 低 | 慢(逐步) | 中等 | 一般业务,资源受限 |
| 金丝雀发布 | 中 | 中等 | 高 | 需要精细流量控制 |
| 蓝绿/红黑部署 | 高 | 快 | 中 | 关键业务,要求快速回滚 |
| 影子部署 | 高 | 不适用 | 极高 | 仅用于验证,不切换流量 |
| A/B 测试 | 中 | 不适用 | 中 | 产品功能验证 |
Kubernetes 中的实现方式
滚动重启(Rolling Update)
1 | |
蓝绿部署
通过两个 Deployment 实现,配合 Service selector 切换流量。
金丝雀发布
使用 Istio 进行流量分割:
1 | |
特性开关分类
根据 Martin Fowler 的分类,特性开关可以分为以下几类:
Release Toggles(发布开关)
用于控制新功能的发布时机。一旦功能完全发布,这类开关应该从代码中移除。
- 用途:解耦部署和发布
- 生命周期:短期(通常几天到几周)
- 示例:新功能开关,功能稳定后即删除
Experiment Toggles(实验开关)
用于 A/B 测试,验证不同策略的效果。
- 用途:产品决策支持
- 生命周期:短期(实验周期)
- 示例:不同 UI 布局的测试开关
Ops Toggles(运维开关)
用于控制系统的运行时行为,帮助运维人员处理紧急情况。
- 用途:运维应急响应
- 生命周期:长期(可能存在数月甚至数年)
- 示例:限流开关、降级开关、熔断开关
Permission Toggles(权限开关)
基于用户属性控制功能的可见性。
- 用途:权限管理和功能授权
- 生命周期:长期
- 示例:VIP 功能开关、内测用户专属功能
特性开关的维护成本很高,需要:
- 统一的管理平台
- 监控和告警机制
- 定期清理机制
- 文档和规范
回滚策略对比
| 策略 | 回滚方式 | 回滚时间 | 数据一致性 | 复杂度 |
|---|---|---|---|---|
| 滚动重启 | 逐步回滚到旧版本 | 较长 | 可能不一致 | 中等 |
| 金丝雀发布 | 调整流量比例 | 快 | 一致 | 较高 |
| 蓝绿/红黑部署 | 切换流量 | 极快 | 一致 | 低 |
| 影子部署 | 无需回滚 | 不适用 | 不适用 | 低 |
数据库迁移处理
不同部署策略下的数据库迁移需要特别注意:
滚动重启
- 采用渐进式数据库迁移:
- 先添加新字段/表(不删除旧字段)
- 部署兼容新旧版本的应用
- 进行滚动重启
- 数据迁移完成后,部署只使用新版本的应用
- 最后删除旧字段/表
蓝绿部署
- 可以采用停机迁移或双写迁移:
- 停机迁移:停服 → 迁移数据库 → 启动新版本(简单但影响用户体验)
- 双写迁移:新旧版本同时读写 → 数据同步 → 切换到新版本
金丝雀发布
- 必须使用兼容性迁移:
- 数据库 Schema 必须同时兼容新旧版本
- 采用添加字段而非修改字段的方式
- 使用数据库触发器或应用层逻辑保证数据一致性
最佳实践
- 永远不要修改数据库字段类型,而是添加新字段
- 使用版本化 Schema,记录每次变更
- 自动化数据库迁移脚本,并与应用代码版本绑定
- 生产环境前充分测试迁移脚本
- 保留回滚方案,确保迁移可以撤销
