K8s 发布策略:蓝绿部署与金丝雀发布实战

一、发布策略全景对比

策略 速度 风险 适用场景
Recreate 最快 极高(停机) 开发/测试环境
RollingUpdate 中(影响扩散慢) 默认选择
Blue-Green 秒级切换 低(独立环境) 无损切换、快速回滚
Canary 慢(渐进) 最低(影响范围可控) 高风险变更、A/B测试

RollingUpdate 是 K8s 默认策略,问题在于:新旧版本 Pod 同时存在,流量无法精确控制,一旦出问题影响范围取决于滚动进度。

二、蓝绿部署:秒级切换的无损发布

核心思想

维护两套完整生产环境(Blue 当前版本 / Green 新版本),切换时只需修改 Service 的 selector,流量立即 100% 切换。回滚同样秒级。

实战配置

# Blue Deployment(当前线上版本)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service-blue
  namespace: production
spec:
  replicas: 5
  selector:
    matchLabels:
      app: order-service
      version: blue
  template:
    metadata:
      labels:
        app: order-service
        version: blue
    spec:
      containers:
      - name: order-service
        image: registry.example.com/order-service:v1.2
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
        resources:
          requests: { cpu: "200m", memory: "256Mi" }
          limits: { cpu: "500m", memory: "512Mi" }
---
# Green Deployment(新版本,部署后无流量)
# 结构同上,version: green, image: v1.3
---
# Service(流量入口,通过 selector 控制流向)
apiVersion: v1
kind: Service
metadata:
  name: order-service
  namespace: production
spec:
  selector:
    app: order-service
    version: blue    # ← 只需修改此处即可切换流量
  ports:
  - port: 80
    targetPort: 8080

切换操作步骤

# 1. 部署 Green 版本(不影响线上)
kubectl apply -f order-service-green.yaml

# 2. 等待 Green 就绪
kubectl rollout status deployment/order-service-green -n production

# 3. 验证 Green(内部直连测试)
kubectl port-forward deployment/order-service-green 8080:8080 -n production

# 4. 执行流量切换(核心操作,秒级完成)
kubectl patch service order-service -n production \
  -p '{"spec":{"selector":{"version":"green"}}}'

# 5. 观察 5-10 分钟,如需回滚:
kubectl patch service order-service -n production \
  -p '{"spec":{"selector":{"version":"blue"}}}'

# 6. 确认无误后下线 Blue(节省资源)
kubectl scale deployment/order-service-blue --replicas=0 -n production

适用场景与局限

  • ✅ 零停机切换、版本差异大需完整验证、SLA 回滚 < 1 分钟
  • ⚠️ 资源消耗翻倍、数据库 Schema 变更需兼容性处理、无法对真实流量做渐进式验证

三、金丝雀发布:精细流量控制的渐进式上线

方案一:K8s 原生(粗粒度)

通过调整 Deployment 副本比例控制流量,依赖随机调度,不够精确:

# stable: 9 replicas → ~90% 流量
# canary: 1 replica  → ~10% 流量
# 二者共享同一个 Service selector: app=order-service

方案二:Nginx Ingress Annotation(推荐)

通过 Annotation 实现基于权重/Header/Cookie 的精细流量控制:

# 稳定版 Ingress(正常配置)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: order-service-stable
spec:
  ingressClassName: nginx
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: order-service-stable
            port: { number: 80 }
---
# 金丝雀 Ingress(核心配置)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: order-service-canary
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    # 方式一:基于权重(10% 流量进入金丝雀)
    nginx.ingress.kubernetes.io/canary-weight: "10"
    # 方式二:基于 Header(特定用户强制走金丝雀)
    # nginx.ingress.kubernetes.io/canary-by-header: "X-Canary"
    # nginx.ingress.kubernetes.io/canary-by-header-value: "always"
    # 方式三:基于 Cookie(灰度用户标记)
    # nginx.ingress.kubernetes.io/canary-by-cookie: "canary_user"
spec:
  ingressClassName: nginx
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: order-service-canary
            port: { number: 80 }

渐进式流量调整脚本(配合 Prometheus 自动回滚):

#!/bin/bash
# canary-progress.sh:渐进式提升金丝雀流量比例
NAMESPACE="production"
INGRESS="order-service-canary"

for weight in 10 25 50 75 100; do
  echo ">>> 设置金丝雀流量比例: ${weight}%"
  kubectl annotate ingress ${INGRESS} -n ${NAMESPACE} \
    nginx.ingress.kubernetes.io/canary-weight="${weight}" --overwrite

  echo ">>> 等待 5 分钟,观察监控指标..."
  sleep 300

  # 检查错误率(需配合 Prometheus)
  ERROR_RATE=$(curl -s "http://prometheus:9090/api/v1/query" \
    --data-urlencode 'query=rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m])' \
    | jq '.data.result[0].value[1]' | tr -d '"')

  if (( $(echo "$ERROR_RATE > 0.01" | bc -l) )); then
    echo "❌ 错误率超标,自动回滚至 0%!"
    kubectl annotate ingress ${INGRESS} -n ${NAMESPACE} \
      nginx.ingress.kubernetes.io/canary-weight="0" --overwrite
    exit 1
  fi
  echo "✅ 指标正常,继续推进..."
done
echo "🎉 金丝雀发布完成,全量切换!"

方案三:Argo Rollouts(企业级推荐)

专为渐进式发布设计的 CRD,支持自动分析、Metric 门控、与 ArgoCD 深度集成:

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: order-service
  namespace: production
spec:
  replicas: 10
  strategy:
    canary:
      steps:
      - setWeight: 10           # Step 1: 切 10% 流量
      - pause: {duration: 5m}   # 暂停5分钟,自动分析
      - setWeight: 25
      - pause: {duration: 10m}
      - setWeight: 50
      - pause: {}               # 无时限暂停,等待人工确认
      - setWeight: 75
      - pause: {duration: 5m}
      - setWeight: 100
      analysis:
        templates:
        - templateName: success-rate
        startingStep: 2
        args:
        - name: service-name
          value: order-service-canary
  selector:
    matchLabels:
      app: order-service
  template:
    metadata:
      labels:
        app: order-service
    spec:
      containers:
      - name: order-service
        image: registry.example.com/order-service:v1.3
---
# 分析模板:基于 Prometheus 指标自动判断是否继续
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: success-rate
spec:
  args:
  - name: service-name
  metrics:
  - name: success-rate
    interval: 2m
    successCondition: result[0] >= 0.99  # 成功率低于 99% 则自动回滚
    failureLimit: 3
    provider:
      prometheus:
        address: http://prometheus.monitoring:9090
        query: |
          sum(rate(http_requests_total{service="{{args.service-name}}",status!~"5.."}[5m]))
          /
          sum(rate(http_requests_total{service="{{args.service-name}}"}[5m]))

Argo Rollouts 常用操作

# 安装
kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts \
  -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml

# 查看 Rollout 状态
kubectl argo rollouts get rollout order-service -n production --watch

# 手动推进到下一步(暂停状态时)
kubectl argo rollouts promote order-service -n production

# 中止发布并回滚
kubectl argo rollouts abort order-service -n production

四、生产关键注意事项

1. 数据库兼容性是最大的坑

新旧版本同时运行期间访问同一数据库,必须保证 Schema 兼容:

-- ❌ 错误:直接删除列(旧版本 Pod 会报错)
ALTER TABLE orders DROP COLUMN old_field;

-- ✅ 正确:三步 expand/contract 模式
-- Step 1(发布前):新版本兼容旧列,同时写入新列
-- Step 2(清零旧版本后):新版本只读新列
-- Step 3(下次发布):删除旧列

推荐工具:Flyway、Liquibase 管理 Schema 变更。

2. 健康检查 + 优雅停机

readinessProbe:
  httpGet:
    path: /actuator/health/readiness
    port: 8080
  initialDelaySeconds: 15
  periodSeconds: 5
lifecycle:
  preStop:
    exec:
      command: ["/bin/sh", "-c", "sleep 15"]  # 蓝绿切换后让旧 Pod 处理完进行中的请求
terminationGracePeriodSeconds: 60

3. Prometheus 金丝雀告警规则

groups:
- name: canary-release
  rules:
  - alert: CanaryHighErrorRate
    expr: |
      sum(rate(http_requests_total{service="order-service-canary", status=~"5.."}[5m]))
      /
      sum(rate(http_requests_total{service="order-service-canary"}[5m])) > 0.01
    for: 2m
    labels:
      severity: critical
    annotations:
      summary: "金丝雀版本错误率超标,建议回滚"
  - alert: CanaryHighLatency
    expr: |
      histogram_quantile(0.99,
        sum(rate(http_request_duration_seconds_bucket{service="order-service-canary"}[5m])) by (le)
      ) > 0.5
    for: 3m
    labels:
      severity: warning

4. 有状态服务不建议蓝绿部署

StatefulSet(Redis、Kafka 等)的 PVC 不能直接切换,推荐滚动更新或专门的 Operator 管理。

5. 金丝雀发布前置检查清单

# 1. 确认 readinessProbe 配置正确
# 2. 确认资源限制已设置
# 3. 确认 PDB 存在(保障滚动期间可用性)
kubectl get pdb -n production
# 4. 确认监控面板已就绪
# 5. 确认回滚命令已准备(回滚不应超过1分钟)

五、蓝绿 vs 金丝雀选型决策树

维度 蓝绿部署 金丝雀发布
回滚速度 ⚡ 秒级 🐢 分钟级
资源消耗 💸 高(2x) 💰 低
流量控制精度 ❌ 非此即彼 ✅ 1%-100% 任意
配置复杂度 低(原生 Service) 中-高(Ingress/Rollouts)
最适合场景 无状态、对回滚时间敏感 高风险变更、A/B 测试
是否需要 < 1 分钟回滚?
├── 是 → 蓝绿部署(接受 2x 资源消耗)
└── 否
    ├── 变更风险高 / 需要 A/B 测试?
    │   ├── 是 → 金丝雀发布(Nginx Ingress 或 Argo Rollouts)
    │   └── 否 → RollingUpdate(资源受限场景)
    └── 有自动化需求 / 大型团队?
        └── Argo Rollouts(企业级首选)