K8s 运维系列 | 第 28 天:灰度发布——金丝雀、蓝绿与滚动发布实践

在 Kubernetes 的生产运维中,一次坏版本的全量推送往往意味着几十甚至几百个 Pod 同时进入异常状态,故障半径被瞬间放大。灰度发布(也常被称为渐进式交付)就是为了解决这个问题:把新版本的流量以可控的、分阶段的方式放出去,让真实用户先承担验证责任,再把风险逐步收敛。本文以 K8s 为底座,深入讲解金丝雀发布、蓝绿发布、滚动发布三种主流灰度策略的原理、实现与权衡,结合 Ingress、Service、Istio 以及 Argo Rollouts 等工具,给出可直接落地的发布方案。

K8s 运维 第28天

一、核心概念:三种灰度策略的差异

灰度发布不是一种具体工具,而是「先放一部分流量验证,再逐步放量」的一种发布策略。在 K8s 生态中,围绕这一思路形成了三种典型策略:滚动发布、蓝绿发布、金丝雀发布。三者的取舍点主要在于流量切换的粒度、回滚速度和资源占用。

1.1 滚动发布(Rolling Update)

滚动发布是 K8s Deployment 的原生能力,通过 maxSurgemaxUnavailable 控制新旧 Pod 的替换节奏,实现无缝升级。其优势是不需要额外基础设施,缺点是灰度粒度较粗——只能以 Pod 数为单位,无法精确到流量百分比。

1.2 蓝绿发布(Blue/Green)

蓝绿发布同时维护两套完整环境:Blue 承载生产流量,Green 是新版本预演环境。当 Green 通过验证后,将 Service 的 selector 从 Blue 切换到 Green,实现秒级回滚。它的核心代价是资源翻倍——新旧版本同时在线。

1.3 金丝雀发布(Canary Release)

金丝雀发布把新版本当作「金丝雀」投放,先接 5% 流量观察指标,正常后再加到 20%、50%、100%。它兼顾资源占用与流量精度,需要 Ingress 或 Service Mesh 的流量分割能力支撑,是当下云原生发布的主流形态。

二、实战步骤一:原生 Deployment 滚动发布

我们先从最简单的滚动发布入手,理解 K8s 原生的流量切换机制。

2.1 基础 YAML

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 6
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: web
        image: registry.local/web:1.2.0
        ports:
        - containerPort: 8080

2.2 触发滚动更新

# 查看当前版本
kubectl rollout status deploy/web

# 推送新版本,触发滚动
kubectl set image deploy/web web=registry.local/web:1.3.0

# 观察 Pod 替换过程
kubectl get pods -l app=web -w

# 如果新版本有问题,秒级回滚
kubectl rollout undo deploy/web
kubectl rollout history deploy/web

2.3 滚动发布的局限

滚动发布只能让「一部分 Pod 是新版本」,但流量是随机分配的,无法保证新 Pod 只接 10% 的请求。在高 QPS 场景下,新版本的故障仍可能影响接近全量用户。

三、实战步骤二:蓝绿发布

蓝绿发布的精髓在于「服务入口的切换」而非 Pod 本身。通过维护两个 Deployment 和两个 Service,用 Selector 的变更完成流量切换。

3.1 两套环境与入口 Service

# Blue 环境(稳定版)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-blue
spec:
  replicas: 4
  template:
    metadata:
      labels:
        app: web
        version: blue
    spec:
      containers:
      - name: web
        image: registry.local/web:1.2.0

# Green 环境(候选版)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-green
spec:
  replicas: 4
  template:
    metadata:
      labels:
        app: web
        version: green
    spec:
      containers:
      - name: web
        image: registry.local/web:1.3.0

3.2 通过切换 Selector 完成发布

# 初始流量指向 blue
kubectl label svc/web version=blue

# Green 验证通过后,切换 selector
kubectl set selector svc/web version=green

# 如果 Green 出问题,立即切回 Blue
kubectl set selector svc/web version=blue

3.3 蓝绿发布的代价

蓝绿发布要求新旧版本常驻,双倍的资源占用意味着成本翻倍;此外切换是「全量切」,无法实现百分比灰度。适合对回滚速度要求极高、可以接受双倍资源的场景,例如核心支付链路。

四、实战步骤三:基于 Ingress 的金丝雀发布

金丝雀发布的关键是「按流量比例」拆分新旧版本。Ingress Controller 的 Rewrite/Canary 注解是这一场景最常用的方案。

4.1 双 Deployment + 双 Ingress

# 稳定版本
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-stable
spec:
  replicas: 5
  template:
    metadata:
      labels:
        app: web
        version: stable
    spec:
      containers:
      - name: web
        image: registry.local/web:1.2.0
# 金丝雀版本,独立 Service 与 Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-canary
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"
    nginx.ingress.kubernetes.io/canary-by-header: "X-Canary"
spec:
  ingressClassName: nginx
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-canary
            port:
              number: 8080

4.2 渐进式放量

# 阶段一:10% 流量
kubectl annotate ingress web-canary 
  nginx.ingress.kubernetes.io/canary-weight=10 --overwrite

# 观察 Prometheus 指标 30 分钟,无异常后
kubectl annotate ingress web-canary 
  nginx.ingress.kubernetes.io/canary-weight=50 --overwrite

# 稳定后全量切换
kubectl annotate ingress web-canary 
  nginx.ingress.kubernetes.io/canary-weight=100 --overwrite
kubectl delete ingress web-canary  # 收尾清理

# 灰度回滚:直接降到 0
kubectl annotate ingress web-canary 
  nginx.ingress.kubernetes.io/canary-weight=0 --overwrite

4.3 基于用户身份的定向灰度

除了按百分比,还可以按请求头定向灰度,适合内部员工先体验新版本:

# 内部员工请求携带 X-Canary: true 走金丝雀
curl -H "X-Canary: true" https://api.example.com/health

# 通过 cookie 灰度已登录的高价值用户
# nginx.ingress.kubernetes.io/canary-by-cookie: "internal-user"

五、进阶方案:Istio + Argo Rollouts 自动化灰度

手工改注解容易出错,生产环境推荐使用自动化工具。

5.1 Istio VirtualService 流量分割

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: web-vs
spec:
  hosts:
  - api.example.com
  http:
  - route:
    - destination:
        host: web
        subset: stable
      weight: 90
    - destination:
        host: web
        subset: canary
      weight: 10
  - match:
    - headers:
        x-canary:
          exact: "true"
    route:
    - destination:
        host: web
        subset: canary

5.2 Argo Rollouts 自动渐进发布

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: web
spec:
  replicas: 10
  strategy:
    canary:
      canaryService: web-canary
      stableService: web-stable
      analysis:
        templates:
        - templateName: success-rate-check
        args:
        - name: service
          value: web-canary
      steps:
      - setWeight: 10
      - pause: {duration: 10m}
      - setWeight: 30
      - pause: {duration: 15m}
      - setWeight: 60
      - pause: {duration: 30m}
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: web
        image: registry.local/web:1.3.0

Argo Rollouts 会监听 Prometheus 的成功率、错误率、延迟指标,一旦超过阈值自动暂停或回滚,把「人工判断」变成「数据驱动」。

六、常见问题(FAQ)

Q1:金丝雀与滚动发布怎么选?
如果只是简单升级、无重大改动,用滚动发布即可;如果涉及数据结构变更、协议兼容、外部依赖,必须走金丝雀。

Q2:蓝绿发布资源翻倍能否优化?
可以接受时选蓝绿;不可接受时,将新版本容量设为 1-2 副本,仅在验证阶段保留,验证通过后再扩缩容,能显著降低成本。

Q3:如何确保灰度阶段指标可信?
金丝雀阶段流量基数小,单看绝对值没有意义。应关注成功率、P99 延迟、错误率等相对指标,并和稳定版本做对比分析。

Q4:数据库变更能否灰度?
数据库结构变更需遵循「先兼容再切换」原则:先加字段、写双写、读新字段、下线老字段,每一步都可以单独灰度。

七、总结

灰度发布是生产 K8s 集群的「安全护栏」。滚动发布简单原生,蓝绿发布秒级回滚但成本翻倍,金丝雀发布精度最高、是云原生发布的主流方案。三种策略并非互斥,实际生产中常常组合使用:核心服务用蓝绿,业务服务用金丝雀,配置类变更用滚动。无论选择哪种,都要把灰度阶段的关键指标接入监控告警,让数据驱动发布决策。建议从滚动发布起步,逐步引入 Ingress Canary 注解,再升级到 Istio 或 Argo Rollouts,形成完整的渐进式交付体系。

下期预告

第 29 天我们将进入「生产实践——K8s 上运行有状态服务」,讲解如何在 Kubernetes 中稳定运行 MySQL、Redis、Kafka 等数据库与中间件,涵盖 StatefulSet、PVC、备份恢复与容量规划等关键主题。

微信二维码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容