灰度发布、流量染色与回滚
灰度发布先让一小部分稳定流量进入新版本,再根据技术和业务指标逐步放量。它降低一次变更的影响范围,但只有分组稳定、指标可比较、数据兼容且停止条件明确时,灰度结果才可信。
1. 部署和发布可以分开
部署把新版本进程放进生产环境,发布决定哪些请求开始使用它。使用网关、负载均衡或 service mesh 调整路由,可以先部署并验证 readiness,再逐步开放流量。
功能开关还可以把代码部署与功能启用分开。开关需要负责人、到期时间和清理计划,否则旧分支长期留在代码中,会增加测试组合和运行复杂度。
2. 灰度分组必须稳定
按随机请求比例切流很简单,但同一用户可能连续落到新旧版本,导致会话、缓存和业务体验不一致。需要用户级稳定体验时,可以根据 user ID、tenant ID 或设备 ID 做一致分桶。
请求经过多层服务时,入口可以写入受保护的实验或发布标签,并沿 trace context 或可信 header 传播。下游只能接受网关或服务网格生成的标签,不能允许外部客户端任意选择内部版本。
3. 先定义观察指标和停止条件
每个放量阶段都应比较新旧版本的:
- 错误率、超时率和延迟分位数。
- CPU、内存、线程池、连接池和下游调用量。
- 核心业务成功率、金额、转化或投诉指标。
- 新错误类型、日志异常和数据一致性结果。
样本太少时,短时间没有错误不能证明安全。高风险发布要设最小样本和观察时间,并提前定义自动暂停或回滚阈值,避免事故发生后临时争论。
4. 流量比例要结合实例容量
10% 请求不一定均匀落到 10% 实例。长连接、连接池和会话黏性会让实际分布偏斜。新版本实例数量太少时,单实例负载可能高于旧版本,性能差异来自容量而非代码。
应同时观察请求比例、实例负载和业务键分布。必要时先扩大新版本实例,再限制入口比例。
5. 数据库变更遵循扩展再收缩
新旧版本灰度共存时,它们必须同时理解数据库和消息格式。常见步骤是:
- 先增加兼容字段或新表,旧代码仍可运行。
- 发布能同时读写新旧格式的过渡版本。
- 回填历史数据并核对。
- 切换主要读取路径。
- 确认没有旧版本后再删除旧结构。
直接重命名字段或改变事件语义,会让旧版本在灰度期间失败,也让代码回滚无法恢复数据库状态。
6. 回滚只停止后续影响
路由回到旧版本可以阻止更多请求进入新代码,却不能撤销已经写入的数据、发出的消息和调用的外部接口。发布计划要为这些副作用准备兼容读取、补偿脚本、对账和正向修复。
若新版本写入旧版本无法解析的数据,简单回滚可能让故障扩大。回滚可行性必须在发布前验证,而不是只保留上一份镜像。
7. Shadow traffic 用于验证只读行为
影子流量把生产请求复制给新版本,但不把其响应返回用户。它适合比较解析、查询和性能,不能直接执行支付、发消息等副作用,除非依赖被隔离或替换。
影子请求要有明确标识,避免污染业务指标和审计。复制流量也会增加下游容量,不能假设它没有生产成本。
8. 发布结束后清理临时状态
全量后要确认旧实例已经排空,灰度路由和临时标签被删除,功能开关进入固定状态,监控基线更新,并记录实际结果。失败发布则保留时间线、触发阈值和数据修复记录。
长期并存的“临时灰度”会让每次故障都难以确认请求究竟走了哪个版本。
9. 常见问题
9.1 灰度 1% 没有问题,为什么全量后仍会故障
小流量可能没有覆盖特定租户、长尾数据和容量拐点,后台任务也可能不按请求比例切分。需要结合代表性分组、样本量、压力测试和分阶段扩容,而不是只看一个百分比。
9.2 蓝绿发布和灰度发布有什么区别
蓝绿通常同时维护两套完整环境,并在验证后整体切换;灰度按比例或用户组逐步切流。两者都要处理数据兼容、连接排空和不可逆副作用,可以组合使用。
10. 面试题
10.1 如何设计一次可以安全回滚的灰度发布
出现公司:Shopee
考察重点
- 稳定分桶、标签传播和实际容量。
- 技术指标、业务指标与停止条件。
- 数据兼容、不可逆副作用和回滚边界。
相关内容:第 1 节“部署和发布可以分开”至第 8 节“发布结束后清理临时状态”。
参考回答
先部署通过 readiness 的新版本,再由网关按稳定用户或租户分桶逐步切流,必要时把受保护的版本标签传到下游。每一阶段预先设置最小样本、观察时间,以及错误率、P99、资源和核心业务指标的停止阈值;同时核对实际请求和实例负载,不只看配置比例。
新旧版本共存要求数据库与消息 schema 向后兼容,采用先扩展、过渡读写、回填核对、最后收缩的步骤。路由回滚只能停止后续流量,不能撤销已写数据和外部副作用,因此发布前还要准备补偿、对账和正向修复,并实际验证旧版本能读取新版本产生的数据。