灰度发布:让变更只影响 1% 用户(R02)

21 阅读4分钟

系列《AI 应用生产化手册》第 11 篇(共 30 篇)|配套开源项目:github.com/ChenYingbo/…

一、先看问题:全量上线 = 全量冒险

R01 让提示词有版本了,但上线方式还是老问题:

  1. 全量上线 = 全量冒险——提示词改坏了,所有用户同时踩坑;回滚再快,差评已经产生了。
  2. "要么全上要么不上"——没有中间态,变更决策变成赌博。
  3. 灰度 = 玄学——"先放一部分人",但放给谁?怎么放?出问题怎么收?

解法:配置化灰度——按 user_id 确定性分桶,放量比例走配置,出问题只影响少数用户,且可随时回退。

核心认知:灰度不是"上线技巧",是"风险控制机制"——它把"一次全量的赌博"变成"分步验证的决策过程"。

二、原理

三种灰度模式

模式做法适用
影子模式(shadow)新版本并行跑、结果只记录不展示验证"新版本会怎么回答"(不影响用户)
金丝雀(canary)小比例真实用户先上(如 5%)常规变更
按用户分桶user_id 哈希分桶,指定比例命中新版本需要"同一用户体验一致"的场景

本 demo 用"按用户分桶"——确定性分桶保证同一用户每次都走同一版本。

分桶:为什么用哈希不用取模

错误:bucket = user_id % 100
  → 用户 ID 规律性强(连续 ID 落相邻桶),分布不均

正确:bucket = md5(user_id) % 100
  → 哈希打散,均匀落入 0-99;同一用户永远同桶(确定性)

实测验证(scripts/rollout.py simulate):10000 用户 @10% 放量 → 命中 10.47%,桶分布每 10 段各 ~10%,完美均匀。

放量节奏与观测

阶段放量观察什么
试点5-10%错误率、P95 延迟、评测分数
扩大25-50%用户反馈、成本变化、质量指标
全量100%灰度完成,回滚预案待命

关键:每步观察 ≥1 个完整发布周期——质量退化是概率性的,样本不够看不到。

灰度 vs A/B

灰度A/B
目的降低风险(别炸)对比效果(谁更好)
决策放量还是回滚保留哪个版本

先灰度保平安,再 A/B 找最优。

三、动手:分桶 + 放量 + 演练

git clone https://github.com/ChenYingbo/ai-prod-demo.git && cd ai-prod-demo
source .venv/bin/activate

# 1. 验证分桶均匀性(10000 用户 @10%)
python scripts/rollout.py simulate --n 10000 --rollout 10
# 真实输出:canary 命中 1047 (10.47%) ✓ 分布与配置一致(均匀)
# 桶分布(0-99 每 10 段): {0:1000,1:1000,...,9:1000}

# 2. 查看放量配置
python scripts/rollout.py status

# 3. 开启灰度(10% 用户走新提示词 v1.1.0)
CANARY_ROLLOUT=10 uvicorn app.main:app --port 8000
curl -X POST http://localhost:8000/api/chat \
  -H "Content-Type: application/json" -d '{"question":"什么是 RAG?","user_id":"user-001"}'
# 日志:event=canary_decision feature=chat-system user_id=user-001 bucket=xx variant=stable/canary rollout=10

灰度演练(必做)

  1. CANARY_ROLLOUT=10 启动,用 user-001~user-020 各发一请求
  2. 统计日志里 canary/stable 比例(应 ≈1:9)
  3. 把 CANARY_ROLLOUT=0 重启 → 全部 stable(灰度可关 = 回滚手段之一)
  4. 观察:同一 user 在 10% 和 20% 放量下,命中是"单调"的(10% 时 canary 的用户,20% 时仍是 canary)

四、真实踩坑(含压测翻车现场)

  1. 桶不均匀:用 user_id % 100 直接取模——连续 ID 全落相邻桶;必须 md5 打散
  2. 灰度与缓存冲突:结果被缓存后灰度切换不生效——缓存 key 要考虑版本(本系列 C02 的 key 含 system 全文,天然隔离)
  3. 只看成功率不看质量:只盯错误率,质量悄悄退化——质量要靠评测样本
  4. 一放到底:10% 验证 5 分钟就冲 100%——样本量不够
  5. 放量比例写死:改比例要发版——CANARY_ROLLOUT 走配置/env
  6. 灰度没有回滚预案:回滚 = 放量归零(+ 降级链兜底)
  7. (实测)第二次压测全命中缓存:我们做压测复现时,第二次用相同问题模板 → 全部命中第一次的缓存,QPS 从真实 1.5 飙到 415——差点把缓存性能当成模型吞吐。压测/灰度测试一定要换题或清缓存

五、小结

  • 三种模式:影子 / 金丝雀 / 按用户分桶
  • 哈希分桶:确定性 + 均匀性(md5 % 100,不是直接取模)
  • 放量节奏:10→25→50→100,每步过观察门
  • 回滚 = 放量归零——灰度可关,就是回滚手段

明天(R03):《A/B 实验》——把"哪个版本更好"从感觉变成统计结论(含真实 z 检验:85% vs 78% 居然不显著)。 收藏 + 关注,每天一篇,30 天把 AI 应用送上生产。