灰度发布与快速回滚
每次上线都是一次风险注入:代码有缺陷、配置写错、SQL 变成慢查询、依赖行为不兼容,任何一种都可能造成线上故障。灰度发布把“一次性全量”改成“小范围验证后逐步放大”,快速回滚则把故障时间从小时级压缩到分钟级的保险。本章讲清灰度策略、功能开关与金丝雀流程。
发布为什么危险
| 风险来源 | 典型表现 | 缓解手段 |
|---|---|---|
| 代码缺陷 | 特定参数下报错、空指针 | 灰度放量 + 监控 + 快速回滚 |
| 数据库变更 | 加字段后旧代码写入失败 | 先加后删、双写、兼容性评审 |
| 配置错误 | 超时、阈值、开关写错 | 配置校验、灰度生效、可一键回退 |
| 依赖变更 | 下游接口行为变化 | 契约测试、上游先做兼容 |
| 容量误判 | 新版本资源占用上升 | 压测 + 发布后容量观察 |
关键认知:发布是常态操作,所以必须为“发布途中发现异常”设计退出路径,而不是指望一次成功。
灰度策略
| 策略 | 分流维度 | 适用场景 | 注意点 |
|---|---|---|---|
| 按机器 | 集群中的部分实例 | 内部验证,实现最简单 | 需保证流量均衡落到灰度机器 |
| 按用户 | 用户 id 哈希、白名单 | 与用户状态强相关的功能 | 同一用户体验要一致,哈希需稳定 |
| 按流量比例 | 随机百分比 | 通用新功能、算法调整 | 比例要能平滑放大:1% → 5% → 20% → 100% |
| 按地域/机房 | 城市、机房、运营商 | 地域相关功能、单元化部署 | 便于按机房整体回退 |
分流规则要满足两个性质:稳定(同一用户多次请求落到同一版本)与可关闭(能一键把灰度比例归零)。
功能开关与配置中心
功能开关把“发布”与“上线”解耦:代码先发布但开关关闭,验证通过后再打开,出问题时先关开关而不是回滚代码。
开关的三种典型用法
├─ 发布开关:新功能默认关闭,验证后打开(验证完成应尽快清理)
├─ 灰度开关:按用户或比例控制生效范围
└─ 降级开关:依赖故障时关闭非核心逻辑,保住主流程
开关必须遵守:有默认值(配置中心不可用时用默认值启动)、可热生效、有归属人与清理计划。长期堆积不清理的开关会变成“配置迷宫”,本身就是故障来源。
金丝雀发布流程
1. 准备 代码评审通过、压测完成、变更单与回滚方案就绪
2. 小流量 1 台实例或 1% 流量上线,观察 10~30 分钟
3. 观察 对比灰度与基线的错误率、P99、资源占用、业务指标
4. 放大 逐步 5% → 20% → 50% → 100%,每一档都要有观察窗口
5. 全量与收尾 清理旧版本、关闭临时开关、更新变更文档
异常时:停止放大 → 灰度比例归零 → 必要时回滚版本
观察指标必须包含业务指标(下单成功率、支付成功率),只看 CPU 与错误码会漏掉“程序正常但业务结果错”的问题。
快速回滚
按代价从低到高选择回滚方式:
| 方式 | 生效速度 | 适用情况 |
|---|---|---|
| 关闭开关 | 秒级 | 新功能引发的问题,优先使用 |
| 调回配置 | 秒级到分钟级 | 阈值、限流参数配置错误 |
| 切回旧版本 | 分钟级 | 代码缺陷,需保留上一版本镜像可快速部署 |
| 数据库回滚 | 慢且危险 | 尽量通过兼容设计避免走到这一步 |
回滚的前提是“可回滚性”:
- 镜像、配置、SQL 脚本都保留上一个可用版本,且能一键部署。
- 发布过程不停机(滚动或蓝绿),保证随时有健康实例可以切回。
- 定期演练回滚,避免真出事时才发现脚本已经失效。
数据库变更的兼容原则
线上数据库变更是最难回滚的部分,遵循“先加后删、双写、兼容两版代码”:
-- 第 1 步:先加新列,允许 NULL(旧代码不受影响)
ALTER TABLE orders ADD COLUMN channel VARCHAR(32) NULL;
-- 第 2 步:双写,新代码同时写旧列与新列,灰度期间两版代码共存
-- 第 3 步:分批回填历史数据,避免大事务
UPDATE orders SET channel = 'app' WHERE channel IS NULL LIMIT 1000;
-- 第 4 步:确认新列被完整使用后,再删除旧列(放到下一个发布周期)
ALTER TABLE orders DROP COLUMN pay_channel;
三条铁律:不加非空且无默认值的列、不改列名与列类型、不在业务高峰对超大表做 DDL(大表加索引要评估锁表时间,必要时使用在线 DDL 工具)。
小结:灰度的价值在于把风险限制在小范围,前提是分流规则稳定且可一键关闭;功能开关让回滚变成秒级操作,而数据库变更只能靠先加后删、双写与两版兼容来获得回退空间,因此发布前必须准备好并演练回滚路径。