灰度发布与渐进式交付
灰度发布与渐进式交付
概述
kubectl apply -f deployment.yaml 是赌博——你相信新版本没问题,但生产环境总有惊喜。渐进式交付(Progressive Delivery)的核心思想是:新版本不会一次性推到所有用户,而是逐步扩大流量比例,每一步都由指标验证决定是继续还是回滚。K8s 原生 Deployment 的 RollingUpdate 只关心 Pod 是否 Ready(进程存活),不关心业务指标是否正常(错误率、延迟)。
一句话:RollingUpdate 保证部署过程不中断服务,Argo Rollouts + Flagger 保证部署后不引入故障。前者是部署工具,后者是发布决策引擎。
Argo Rollouts —— 替代 Deployment 的渐进式交付控制器
核心概念
Argo Rollouts 是 Argo 家族的发布控制器,用 Rollout CRD 替代 Deployment 管理 Pod 生命周期——但额外支持 Blue-Green 和 Canary 两种渐进式策略。
Deployment:
RollingUpdate → 逐步替换 Pod,一次一批,等 Ready → 完成
Rollout (Canary):
Step 1: 创建 1 个新版本 Pod(canary)
Step 2: 等 5 分钟,观察 Prometheus 指标
Step 3: 如果指标正常 → 扩大到 25% 流量
Step 4: 再等 10 分钟
Step 5: 如果仍正常 → 100% 流量 → 删除旧 Pod
任意一步指标异常 → 自动回滚
Rollout CRD v1.7 的完整结构:
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: health-ack
namespace: health
spec:
replicas: 5
selector:
matchLabels:
app: health-ack
# 保持和 Deployment 一样的 Pod 模板(原地迁移只需要改 apiVersion + kind)
template:
metadata:
labels:
app: health-ack
spec:
containers:
- name: app
image: registry.example.com/health-ack:v2.3.1
ports:
- containerPort: 8080
# Blue-Green 策略
strategy:
blueGreen:
activeService: health-ack-active # 生产流量指向的 Service
previewService: health-ack-preview # 新版本预览 Service
autoPromotionEnabled: false # true=自动切换、false=手动确认
prePromotionAnalysis: # 切换前做分析
templates:
- templateName: smoke-test # 冒烟测试 AnalysisTemplate
postPromotionAnalysis: # 切换后验证
templates:
- templateName: canary-metrics
# Canary 策略(分步灰度)
# strategy:
# canary:
# canaryService: health-ack-canary # canary 流量的 Service
# stableService: health-ack-stable # stable 流量的 Service
# steps:
# - setWeight: 10 # Step 1: 10% 流量到 canary
# - pause: { duration: 5m } # 等 5 分钟
# - analysis: # 分析指标
# templates:
# - templateName: canary-metrics
# - setWeight: 25 # Step 2: 25%
# - pause: { duration: 10m }
# - setWeight: 50 # Step 3: 50%
# - pause: { duration: 15m }
# - setWeight: 100 # Step 4: 100%(promote)
Blue-Green vs Canary 决策
| 维度 | Blue-Green | Canary |
|---|---|---|
| 切换方式 | 一次性切换 100% 流量 | 逐步增加流量比例 |
| 回滚速度 | 秒级(切换 Service selector) | 逐步减量 |
| 双倍资源需求 | ✅ 需要 2× Pod | ✅ 只需要少量 canary Pod |
| 数据库 Schema 变更 | ❌ 不能同时有新旧两版写同一 DB | ✅ 可以(小流量先写,观察) |
| 适用 | 关键服务、需要秒级回滚 | 通用,推荐 |
| 你的场景 | api-health 的健康检查 endpoint(无状态、无 DB 写) | health-ack 的 checkout(有 DB 写) |
AnalysisTemplate —— 让发布决策由数据驱动
AnalysisTemplate 定义了在灰度过程中需要验证的指标,以及如何判断成功/失败:
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: canary-metrics
namespace: health
spec:
metrics:
# 指标 1:HTTP 错误率(5xx/总请求 < 1%)
- name: error-rate
interval: 30s # 每 30 秒取样一次
count: 10 # 取 10 个样本
failureLimit: 3 # 允许最多 3 次失败
provider:
prometheus:
address: http://prometheus.monitoring:9090
query: |
sum(rate(http_requests_total{
namespace="health",
app="health-ack",
status=~"5.."
}[1m]))
/
sum(rate(http_requests_total{
namespace="health",
app="health-ack"
}[1m])) > 0.01
# 指标 2:P99 延迟(< 500ms)
- name: latency-p99
interval: 30s
count: 10
failureLimit: 2
provider:
prometheus:
address: http://prometheus.monitoring:9090
query: |
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket{
namespace="health",
app="health-ack"
}[1m])) by (le)
) > 0.5
# 指标 3:新版本 Pod 重启次数(> 0 即失败)
- name: pod-restarts
interval: 60s
count: 5
failureLimit: 1
successCondition: result == 0 # 自定义成功条件
provider:
prometheus:
address: http://prometheus.monitoring:9090
query: |
sum(increase(kube_pod_container_status_restarts_total{
namespace="health",
pod=~"health-ack-.*",
container="app"
}[1m]))
# 指标 4:Webhook 回调(外部分析系统)
- name: external-validation
provider:
web:
url: "https://qa-tool.internal/validate?app=health-ack&version=v2.3.1"
timeoutSeconds: 30
jsonPath: "{$.passed}"
B/G + Analysis 完整工作流
1. 运维触发 Rollout 更新
→ kubectl argo rollouts set image health-ack *=registry.example.com/health-ack:v2.3.1
→ 或 ArgoCD 自动检测 Git 变更
2. Rollout Controller:
→ 创建新 ReplicaSet(可以指定新 replicas=1 作预览)
→ 新 Pod 加入 previewService(不影响生产流量)
→ 触发 prePromotionAnalysis → 跑 AnalysisTemplate
3. AnalysisTemplate (冒烟测试 + 指标验证):
→ 如果所有指标通过 → autoPromotion(如果开启)或等待手动 promote
→ 如果任一指标失败 → AnalysisRun 标记为 Failed → 不 promote
4. promote:
→ activeService selector 从旧 ReplicaSet 切到新 ReplicaSet
→ 100% 流量瞬间切换到新版本
→ 触发 postPromotionAnalysis → 继续监控 5 分钟
→ 如果通过 → 删除旧 ReplicaSet
→ 如果失败 → 秒级切回(Service selector 切回旧 RS)
Flagger —— Service Mesh 原生的渐进式交付
Flagger 是 Weaveworks 开发的项目,专为 Istio / Linkerd / Contour / NGINX 设计的金丝雀发布工具。与 Argo Rollouts 的互补关系:Rollouts 管 Deployment 的生命周期和副本管理,Flagger 管 Service Mesh 的流量分割和指标验证。
Flagger + Istio 完整示例
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: health-ack
namespace: health
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment # 注意:Flagger 管的是 Deployment,不是 Rollout
name: health-ack
service:
port: 8080
gateways:
- istio-system/ingress-gateway
hosts:
- "api.health.example.com"
analysis:
interval: 30s # 每 30s 检查一次指标
threshold: 5 # 连续 5 次阈值检查通过 → 成功
maxWeight: 50 # 金丝雀最大流量比例(50%)
stepWeight: 10 # 每次增加 10%
stepWeights: [5, 10, 20, 50] # 自定义每步目标比例
# 第一步就验证的指标
metrics:
- name: request-success-rate
thresholdRange:
min: 99 # 成功率 ≥ 99%
interval: 1m
- name: request-duration
thresholdRange:
max: 500 # P99 < 500ms
interval: 1m
# Webhook:金丝雀开始/结束/回滚时回调
webhooks:
- name: load-test
type: pre-rollout
url: http://flagger-loadtester.health/
timeout: 5m
metadata:
type: cmd
cmd: "hey -z 1m -c 10 http://health-ack-canary.health:8080/api/health"
- name: notification
type: post-rollout
url: http://notification-service.health/notify
- name: rollback-notification
type: rollback
url: http://notification-service.health/notify
Flagger 自动创建和管理的 Istio 资源(你无需手动配):
# Flagger 自动创建:
kubectl get virtualservices -n health
# health-ack ← 自动创建(canary ↔ stable 权重分割)
kubectl get destinationrules -n health
# health-ack ← 自动创建(canary/sttps subsets 定义)
Argo Rollouts vs Flagger
| 维度 | Argo Rollouts | Flagger |
|---|---|---|
| 管理什么 | Pod 副本(替代 Deployment) | Service Mesh 流量分割 |
| 流量分割方式 | 多 Service 切换 | Istio/Linkerd VirtualService 权重 |
| 分析 | AnalysisTemplate(PromQL/Webhook/Datadog) | metrics(PromQL)+ webhooks |
| 与 ArgoCD 集成 | ✅ 原生(同家族) | ✅ 通过注解触发 |
| 复杂度 | 中等(需替换 Deployment 为 Rollout) | 较高(需 Service Mesh) |
| 适用场景 | 通用、无需 Service Mesh | 已有 Istio/Linkerd 的集群 |
| 你的场景 | 没有 Service Mesh 时的首选 | 迁移到 Istio 后的升级选择 |
生产实践:xirang-ocr 灰度发布实例
从你之前的 GPU 灰度发布问题中提练出一个标准模式:
发布前检查(Checklist)
# 1. 对比旧版本和新版本的 resource spec(防止原地 CPU/Memory resize 异常)
diff <(kubectl get deployment xirang-ocr -o json | jq '.spec.template.spec.containers[0].resources') \
<(cat new-deployment.yaml | yq '.spec.template.spec.containers[0].resources')
# 2. 确认 GPU 节点可用性
kubectl get nodes -l accelerator=nvidia-tesla-t4 -o json | jq '.items[] | {name: .metadata.name, allocatable: .status.allocatable."nvidia.com/gpu"}'
# 3. 确认 Istio sidecar 注入(确保 canary Pod 在 Service Mesh 内)
kubectl get namespace health -o json | jq '.metadata.labels["istio-injection"]'
Rollout 配置(GPU 场景)
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: xirang-ocr
spec:
replicas: 2
selector:
matchLabels:
app: xirang-ocr
template:
metadata:
labels:
app: xirang-ocr
spec:
nodeSelector:
accelerator: nvidia-tesla-t4
tolerations:
- key: "nvidia.com/gpu"
operator: "Exists"
effect: "NoSchedule"
containers:
- name: ocr
image: registry.example.com/xirang-ocr:v2.1.0
resources:
limits:
nvidia.com/gpu: 1
requests:
nvidia.com/gpu: 1
strategy:
canary:
steps:
- setWeight: 10
- pause: { duration: 5m }
- analysis:
templates:
- templateName: gpu-metrics # GPU 专项验证
- setWeight: 50
- pause: { duration: 10m }
- setWeight: 100
---
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: gpu-metrics
spec:
metrics:
- name: gpu-utilization
interval: 30s
count: 5
failureLimit: 2
provider:
prometheus:
address: http://prometheus.monitoring:9090
query: |
avg(DCGM_FI_DEV_GPU_UTIL{
pod=~"xirang-ocr-.*"
}) < 10 # GPU 利用率 < 10% = 模型没加载成功
- name: gpu-memory
failureLimit: 2
provider:
prometheus:
query: |
avg(DCGM_FI_DEV_FB_USED{
pod=~"xirang-ocr-.*"
}) < 1000000 # 显存使用 < 1MB = 模型未加载
回滚流程
# 1. 立即中止正在进行的 Rollout
kubectl argo rollouts abort xirang-ocr -n ocr
# 2. 回滚到上一个稳定版本
kubectl argo rollouts undo xirang-ocr -n ocr
# 3. 验证回滚后状态
kubectl argo rollouts status xirang-ocr -n ocr --watch
# 等 "Rollout 'xirang-ocr' is Healthy."
# 4. 排查失败原因
kubectl describe analysisrun -n ocr -l rollout=pright-ocr
# 查看哪个指标触发了失败
关联知识
- ArgoCD GitOps 实战 — ArgoCD 管理 Argo Rollouts CRD(Git → ArgoCD → Rollout)
- Istio 服务网格详解 — Flagger 的流量分割依赖 Istio VirtualService
- K8s 可观测性栈 — Prometheus 指标是 AnalysisTemplate 的决策依据
- ../linux/CPU 隔离与中断亲和性 — GPU 工作在隔离 CPU 上的 Pod,不抢占训练任务 CPU
参考资源
- Argo Rollouts:https://argoproj.github.io/argo-rollouts/
- Flagger:https://docs.flagger.app/
- AnalysisTemplate:https://argoproj.github.io/argo-rollouts/features/analysis/
学习时间
| 阶段 | 时间 | 备注 |
|---|---|---|
| 渐进式交付 | 2026-07-06 | Blue-Green/Canary、AnalysisTemplate、Flagger、GPU 发布案例、回滚流程 |
状态: 🌱 学习中 下次复习日期: 2026-07-13