深色模式
灰度发布与流量镜像
摘要:本文面向负责发布流程的 SRE / 平台工程师。覆盖两类低风险发布手段——加权金丝雀(canary)与流量镜像(shadow / mirroring),给出完整 YAML,并重点讨论镜像带来的状态变更、资源翻倍等生产红线。覆盖版本 Istio 1.31。
适用版本与前提
- Istio:1.31(apiVersion
networking.istio.io/v1beta1稳定;1.2x 同 group,功能一致) - 前提:目标服务已纳入网格(Sidecar 已注入或 ambient 命名空间),并已定义对应
DestinationRule子集。
背景:两种“低风险”发布
| 手段 | 本质 | 是否影响真实响应 | 主要用途 |
|---|---|---|---|
| 加权金丝雀 | 把 X% 真实用户流量切到新版本 | 是(切到的用户拿到真实响应) | 渐进放量、按指标决策 |
| 流量镜像 | 复制一份真实请求到新版本,响应被丢弃 | 否(原调用方只收到主版本的响应) | 用真实流量验证新版本,零用户风险 |
二者可组合:先用镜像在不影响用户的前提下压测新版,确认稳定后再用加权金丝雀放量。
架构:灰度与镜像的流量拓扑
生产实践一:加权金丝雀
最经典做法:先 90/10,观察指标,逐步 100%。
yaml
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: checkout
namespace: shop
spec:
host: checkout
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: checkout
namespace: shop
spec:
hosts:
- checkout
http:
- route:
- destination:
host: checkout
subset: v1
weight: 90
- destination:
host: checkout
subset: v2
weight: 101
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
放量时只需修改 weight 并 kubectl apply,无需重建 Pod。回滚即把权重改回 100/0。
灰度决策依据
不要凭“感觉”放量。对接 Prometheus 的 istio_requests_total(错误率、延迟分位)与 istio_request_duration_milliseconds,设定阈值(如 5xx < 0.5%、p99 < 300ms)后再加权重。可进一步用 Flagger/Argo Rollouts 自动化这一步。
生产实践二:流量镜像(Shadow)
镜像把一份真实请求异步复制给 canary,canary 的响应被丢弃,因此原调用方无感知。常用于验证新版本能否扛住真实流量与负载模式。
yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: checkout
namespace: shop
spec:
hosts:
- checkout
http:
- route:
- destination:
host: checkout
subset: v1
weight: 100
mirror:
host: checkout
subset: v2
mirrorPercentage:
value: 10.0 # 镜像 10% 生产流量到 v2;省略则 100%1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
镜像行为细节
- 镜像请求会带上原始
Host/Authority头并追加-shadow后缀(如checkout→checkout-shadow),便于在 canary 的日志/指标中区分影子流量。 - 镜像是 fire-and-forget:canary 的响应被丢弃,不影响主路径延迟与用户。
- 镜像请求会真正执行到 canary,只是不回答原调用方。
失败模式与生产红线
红线 1:镜像触发了状态变更
镜像请求真的跑到 canary 并执行业务逻辑。如果 canary 执行了写操作(下单、扣款、发消息),而响应被丢弃,可能造成重复下单、重复扣款。
生产危险
绝不可把镜像流量指向会写共享状态(数据库、消息队列、支付)的版本,除非:
- canary 使用隔离的影子依赖(影子库 / mock 下游);
- 或写路径天然幂等且指向沙箱环境;
- 或显式剥离敏感 Header 并只读。 支付类服务镜像前必须与业务方确认,必要时用 mock 服务替代真实下游。
红线 2:资源翻倍与超时
镜像使请求量翻倍,canary 若无独立容量评估,可能拖垮自身甚至反压(共享资源时)。镜像请求也应设激进超时,避免镜像慢响应耗尽队列。
红线 3:Header/凭证泄漏
镜像请求携带原始 Authorization、Cookie、X-Api-Key 等敏感 Header,落入影子环境有泄露风险。应在镜像前剥离敏感 Header,或在隔离网络内处理。
验证、回滚与清理
bash
# 验证主版本与镜像版本都收到请求(看 v2 访问日志)
kubectl logs deploy/checkout-v2 -n shop -c checkout | grep -i shadow
# 灰度回滚:改回 100/0 权重
kubectl apply -f vs-stable-100.yaml -n shop
# 关闭镜像:删除 mirror/mirrorPercentage 字段后 apply
kubectl apply -f vs-no-mirror.yaml -n shop
# 清理
kubectl delete virtualservice checkout -n shop
kubectl delete destinationrule checkout -n shop1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
故障排查
- 镜像没看到流量:确认
mirror.host是网格内可达的 Service,且 canary Pod 已注入 Sidecar;检查mirrorPercentage是否误设为 0。 - canary 报错但用户无感知:符合预期——镜像响应本就丢弃。应主动看 canary 指标与日志,而非等用户投诉。
- 金丝雀“不生效”:同 traffic.md 排障——host 拼写、subset 标签、revision 推送。
替代方案与权衡
- Argo Rollouts / Flagger:在 VS/DR 之上做自动化渐进交付与指标分析,减少人工放量失误。
- Gateway API:
HTTPRoute的RequestMirrorfilter 是 vendor-neutral 的镜像方式,未来优先。 - 与 Linkerd 对比:Linkerd 支持
TrafficSplit做按比例切流,但原生不支持流量镜像;需要镜像时应优先 Istio 或额外工具。
参考资料
- Istio 官方文档 - 流量镜像任务,访问日期:2026-10-08。
- ContainerSolutions - Shadow Deployment 策略(Istio 示例),访问日期:2026-10-08。
- OneUptime - 流量镜像细节与坑,访问日期:2026-10-08。
- K8s Guide - Istio 架构与流量管理,访问日期:2026-10-08。