2618 字
约 8 分钟
17
S02-A15 · 蓝绿与灰度:切换单元不同,风险也不同

一个团队说"我们做了蓝绿部署",问下去发现两个环境共用一套数据库,切换时新版本要跑迁移脚本,回滚就意味着把数据改回去。这不是蓝绿,这是"两个应用实例轮流承担全部流量",风险与单实例切换差不多。

蓝绿与灰度经常被当成同义词,实际差别在一个问题上:切换的单位是什么。蓝绿切换的是"整套环境",灰度切换的是"一部分流量"。前者要求两套环境彼此独立,后者要求能按比例分流——两者的前置条件完全不同。


1. 两者的定义性差别

维度 蓝绿 灰度
切换单位 整套环境(全量) 流量比例(部分)
需要什么 两套完整环境 + 数据兼容 分流能力 + 指标对比
回滚方式 切回旧环境 把比例调回 0
暴露窗口 切换瞬间全量暴露 逐步扩大
主要风险 数据与状态不兼容 分流不均与指标误判

这个差别还会影响"谁能决定切换":蓝绿是一把手决策(一次到位,需要一个明确的执行时刻),灰度是持续判断(每次扩大比例都要看指标)。所以蓝绿的难点在准备,灰度的难点在观察。

第一项决定了一切:蓝绿是二元的(0 或 100%),灰度是连续的(0 到 100% 之间任意值)。二元切换的优点是干净、回滚快;缺点是切换那一刻所有风险同时兑现。

2. 蓝绿的前提条件

条件 不满足的后果
两套环境独立 实际是同一套,回滚困难
数据双向兼容 回滚要改数据
会话不粘在实例上 切换后用户被登出
外部依赖能同时服务两套 回调地址、白名单冲突
切换是原子操作 部分流量走新、部分走旧

第二项是最容易被忽略的:只要新版本写了旧版本读不懂的数据,蓝绿就退化成单向迁移。判断方法是问一句"切回去之后,旧版本能正常读这些数据吗"。

第四项常被当成小事,实际上它经常成为卡点:支付回调地址、第三方白名单、短信签名这些都是外部登记的,改一次要走对方的流程。两套并存意味着这些登记得同时有效。

第三项在登录系统上很常见:会话存在实例本地内存里,切换后所有用户重新登录。解法是会话外置,或接受"切换即登出"的代价并在低峰做。

// 蓝绿可行性四问
1. 两套环境是否完全独立(含数据库、缓存、密钥、域名)?
2. 新版本写入的数据,旧版本能否读取?(决定能否回滚)
3. 会话与本地状态是否外置?
4. 外部系统是否同时接受两套地址(回调、IP 白名单)?
全"是" → 真蓝绿;任一"否" → 只是双实例轮换

3. 灰度的前提条件

条件 不满足的后果
可按稳定维度分流 同一用户在两版间跳变
两版共享兼容数据 互相读不懂对方写入
有可对比的指标 无法判断该不该扩大
有明确的扩大/回退规则 靠感觉调比例

可以直接用一段判断把"稳定"这件事写清楚:

// 稳定维度的三个要求
1. 同一用户多次请求落在同一侧(否则行为不一致)
2. 维度可获取且不随环境变化(登录态、租户、地域都满足)
3. 侧别可追溯(日志里能看出这个请求走了哪一版)
// 反例:按请求 ID 哈希 → 同一用户在两版间跳变,问题被平均掉

第一项是灰度的核心:分流维度必须稳定。按用户 ID 哈希、按租户、按地域都稳定;按请求随机则不稳定——同一个用户这次走新版、下次走旧版,行为不一致会掩盖真实问题。

// 灰度分流:维度稳定 + 比例可调 + 白名单优先
canary:
  dimension: "header[x-user-id]"     # 稳定维度
  buckets: 1000                      # 分母,便于精细调整
  percent: 50                        # 千分之五十
  allowlist: ["U-1001", "U-1002"]    # 内部账号先走新版
  metrics:                           # 判定依据,必须两版可比
    - error_rate
    - p99_latency
    - business_conversion

4. 指标对比是灰度最难的部分

陷阱 表现
样本量不足 小比例下指标波动大,看不出差异
人群不同质 白名单用户与普通用户行为不同
指标口径不同 两版埋点位置变了,不可比
时间窗口错位 新旧版覆盖的业务高峰不同

第二项很实际:先让内部账号走新版,然后用这批人的数据判断新版是否正常,结论往往偏乐观——内部账号的使用路径最简单。

判定规则要事先写下来,而不是临场决定:

// 扩大与回退规则:写在配置里,避免"看感觉"
canary_rules:
  expand_when:
    - "error_rate < baseline * 1.2 持续 30m"
    - "p99 <= baseline * 1.1 持续 30m"
  rollback_when:
    - "error_rate > baseline * 2 或出现新的错误类型"
    - "任一 P1/P2 缺陷"
  max_percent_per_step: 10      # 每步最多 10%,避免一次跳太多
  min_observation: 30m          # 每次扩大后至少观察 30 分钟

第四项在灰度跨天时最明显:新版只在白天承接 10% 流量,而旧版覆盖了全天,两者的延迟分布自然不同。

还有一个实际操作上的细节:蓝绿的切换要有"回切演练"。只演练过切到新版、没演练过切回旧版,等于只验证了一半。回切往往更麻烦,因为它要面对"新版已经写了一些数据"这个状态。

// 切换与回切都要计时并记录:两个方向分别多久
bin/switch.sh --to green && echo "switch-out $(date +%s)"
bin/verify.sh  --expect green
// 回切演练(低峰执行)
bin/switch.sh --to blue  && echo "switch-back $(date +%s)"

5. 组合用法:先灰度,再蓝绿

阶段 做法 目的
1 灰度 1% → 50% 按稳定维度逐步放量 发现功能与性能问题
2 观察一个完整周期 覆盖高峰与低峰 排除时间因素
3 全量切到新环境(蓝绿) 原子切换 完成收尾与资源回收
4 旧环境保留一段时间 可切回 兜底

这个组合把两种机制的优点串起来:灰度负责"发现问题",蓝绿负责"干净收尾"。只用灰度会有"永久两套并存"的问题,只用蓝绿则没有逐步观察的机会。

6. 排错:五种"切换出问题"

现象 成因 处理方向
回滚后数据错乱 新版写了旧版读不懂的数据 先做数据兼容或前向兼容写入
切换后用户被登出 会话存在实例本地 会话外置
灰度期间行为不一致 分流维度不稳定 换稳定维度
灰度看不出问题、全量出事 样本不足或人群不同质 拉长观察、覆盖高峰
切换后回调失败 外部系统只认旧地址 提前登记双地址

排错时有个快速判断:看有没有"只在新版存在"的错误类型。如果新错误类型的数量随流量比例同步增长,说明这是新版引入的确定性问题,而不是随机波动——这时应当立即回退,而不是继续观察。

第一个是最贵的:回滚发现要改数据,等于回滚失败。这类问题的唯一预防手段是切换前问"旧版本能读吗",并真的用旧版本读一遍。

7. 边界:什么情况不必区分

场景 为什么 做法
无状态服务且无数据变更 两者退化为同一件事 直接滚动发布
单实例部署 无"两套"可言 停机发布
内部工具 无用户影响 直接替换
已有服务网格分流能力 蓝绿只是灰度的一档 全部用灰度

还有一条与资源有关的边界:蓝绿要求两套环境同时在线,成本是双份。在小规模系统里这笔成本可能超过风险收益,此时"滚动发布 + 快速回滚"是更合理的选择——它不提供零风险的切换,但成本低得多。

判断口径一句话:这次发布会不会写入旧版本读不懂的数据。不会,灰度就够;会,就必须先解决数据兼容,否则蓝绿也救不了回滚。


8. 自测

  1. 蓝绿与灰度的定义性差别是什么?它如何决定了两种机制的风险形态?
  2. 蓝绿的五个前提条件中,哪一项缺失会让它退化成单向迁移?判断方法是什么?
  3. 灰度的分流维度为什么必须稳定?按请求随机分流会造成什么后果?
  4. 灰度指标对比的四个陷阱里,哪一个会让"内部账号先试"的结论失效?
  5. "先灰度再蓝绿"的组合各自负责什么?只用其中一种会留下什么问题?
  6. 判断"该用蓝绿还是灰度"的那个问题是什么?
S02-A15 · 蓝绿与灰度:切换单元不同,风险也不同
https://www.clxhxhhr.top/posts/6428/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。