Application Canary Deployment

灰度发布是一种渐进式发布策略,通过逐步将新版本应用引入到少部分用户或流量中。此增量式的发布方式使团队能够监控系统行为、收集指标并确保稳定性,然后再进行全面部署。该方法显著降低了风险,尤其是在生产环境中。

Argo Rollouts 是一个 Kubernetes 原生的渐进式交付控制器,支持高级部署策略。它扩展了 Kubernetes 的能力,提供了灰度发布、蓝绿部署、分析运行、实验和自动回滚等功能。它可与可观测性栈集成,实现基于指标的健康检查,并通过 CLI 和监控面板对应用交付进行控制。

关键概念:

  • Rollout:Kubernetes 中的自定义资源定义(CRD),替代标准的 Deployment 资源,实现蓝绿、灰度等高级部署控制。
  • Canary Steps:一系列逐步调整流量的操作,例如先将 25% 流量导向新版本,再调整到 50%。
  • Pause Steps:引入等待间隔,用于手动或自动验证后再继续下一个灰度步骤。

灰度发布的优势

  • 风险缓解:通过先将变更部署到少量服务器,可以发现并解决问题,减少对用户的影响。
  • 渐进式发布:允许逐步暴露新功能,有助于有效监控性能和用户反馈。
  • 实时反馈:灰度发布能即时反映新版本在真实环境中的性能和稳定性。
  • 灵活性:可根据性能指标调整发布流程,实现动态发布,可暂停或回滚。
  • 成本效益:与蓝绿部署不同,灰度发布无需独立环境,更节省资源。

使用 Argo Rollouts 进行灰度发布

Argo Rollouts 支持灰度发布策略来滚动更新 Deployment,并通过 Gateway API 插件控制流量。在 ACP 中,可以使用 ALB 作为 Gateway API Provider 来实现 Argo Rollouts 的流量控制。

目录

前提条件

  1. 集群中已安装 Argo Rollouts 及 Gateway API 插件。
  2. 安装 Argo Rollouts 的 kubectl 插件(安装地址见 here)。
  3. 已有一个项目用于创建命名空间。
  4. 集群中已部署 ALB 并分配给该项目。
  5. 集群中已有用于部署应用的命名空间。

操作步骤

创建 Deployment

首先定义应用的“稳定”版本,即当前用户访问的版本。创建一个 Kubernetes Deployment,设置合适的副本数、容器镜像版本(例如 hello:1.23.1)及标签,如 app=web

使用以下 YAML:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: hello:1.23.1
          ports:
            - containerPort: 80

YAML 字段说明:

  • apiVersion:用于创建资源的 Kubernetes API 版本。
  • kind:资源类型,这里是 Deployment。
  • metadata.name:Deployment 的名称。
  • spec.replicas:期望的 Pod 副本数。
  • spec.selector.matchLabels:Deployment 用于选择管理的 Pod 标签。
  • template.metadata.labels:Pod 的标签,Service 用于选择 Pod。
  • spec.containers:Pod 中运行的容器列表。
  • containers.name:容器名称。
  • containers.image:容器镜像。
  • containers.ports.containerPort:容器暴露的端口。

使用 kubectl 应用配置:

kubectl apply -f deployment.yaml

这将搭建生产环境。

也可以使用 Helm Chart 创建 Deployment 和 Service。

创建稳定服务

创建一个 Kubernetes Service,用于暴露稳定版本的 Deployment。该服务根据标签选择 Pod,初始选择器为 app=web

apiVersion: v1
kind: Service
metadata:
  name: web-stable
spec:
  selector:
    app: web
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80

YAML 字段说明:

  • apiVersion:用于创建 Service 的 Kubernetes API 版本。
  • kind:资源类型,这里是 Service。
  • metadata.name:Service 名称。
  • spec.selector:根据标签选择 Pod。
  • ports.protocol:使用的协议(TCP)。
  • ports.port:Service 暴露的端口。
  • ports.targetPort:容器接收流量的端口。

使用命令应用:

kubectl apply -f web-stable-service.yaml

该服务允许外部访问稳定版本。

创建灰度服务

创建一个 Kubernetes Service,用于暴露灰度版本的 Deployment。该服务根据标签选择 Pod,初始选择器为 app=web

apiVersion: v1
kind: Service
metadata:
  name: web-canary
spec:
  selector:
    app: web
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80

YAML 字段说明:

  • apiVersion:用于创建 Service 的 Kubernetes API 版本。
  • kind:资源类型,这里是 Service。
  • metadata.name:Service 名称。
  • spec.selector:根据标签选择 Pod。
  • ports.protocol:使用的协议(TCP)。
  • ports.port:Service 暴露的端口。
  • ports.targetPort:容器接收流量的端口。

使用命令应用:

kubectl apply -f web-canary-service.yaml

该服务允许外部访问灰度版本。

创建 Gateway

使用 example.com 作为访问域名,创建 Gateway 以该域名暴露服务:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: default
spec:
  gatewayClassName: exclusive-gateway
  listeners:
  - allowedRoutes:
      namespaces:
        from: All
    name: gateway-metric
    port: 11782
    protocol: TCP
  - allowedRoutes:
      namespaces:
        from: All
    hostname: example.com
    name: web
    port: 80
    protocol: HTTP

使用命令:

kubectl apply -f gateway.yaml

Gateway 会分配一个外部 IP 地址,可通过 Gateway 资源的 status.addresses 中类型为 IPAddress 的字段获取该 IP。

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: default
...
status:
  addresses:
  - type: IPAddress
    value: 192.168.134.30

DNS 配置

在 DNS 服务器中配置域名,将域名解析到 Gateway 的 IP 地址。使用命令验证 DNS 解析:

nslookup example.com
Server:         192.168.16.19
Address:        192.168.16.19#53

Non-authoritative answer:
Name:   example.com
Address: 192.168.134.30

应返回 Gateway 的地址。

创建 HTTPRoute

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: web
spec:
  hostnames:
  - example.com
  parentRefs:
  - group: gateway.networking.k8s.io
    kind: Gateway
    name: default
    namespace: default
    sectionName: web
  rules:
  - backendRefs:
    - group: ""
      kind: Service
      name: web-canary
      namespace: default
      port: 80
      weight: 0
    - group: ""
      kind: Service
      name: web-stable
      namespace: default
      port: 80
      weight: 100
    matches:
    - path:
        type: PathPrefix
        value: /

使用命令:

kubectl apply -f httproute.yaml

访问稳定服务

集群外部通过域名访问服务:

curl http://example.com

或者在浏览器中访问 http://example.com

创建 Rollout

接下来,使用 Argo Rollouts 创建采用 Canary 策略的 Rollout 资源。

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: rollout-canary
spec:
  minReadySeconds: 30
  replicas: 2
  revisionHistoryLimit: 3
  selector:
    matchLabels:
      app: web
  strategy:
    canary:
      canaryService: web-canary
      maxSurge: 25%
      maxUnavailable: 0
      stableService: web-stable
      steps:
      - setWeight: 50
      - pause: {}
      - setWeight: 100
      trafficRouting:
        plugins:
          argoproj-labs/gatewayAPI:
            httpRoute: web
            namespace: default
  workloadRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
    scaleDown: onsuccess

YAML 字段说明:

  • spec.selector:Pod 标签选择器。现有 ReplicaSet 中被选择的 Pod 会受此 Rollout 影响,必须与 Pod 模板标签匹配。
  • workloadRef:指定工作负载引用及缩容策略。
  • scaleDown:指定迁移到 Rollout 后是否缩减工作负载(Deployment)。可选值:
    • "never":不缩减 Deployment。
    • "onsuccess":Rollout 健康后缩减 Deployment。
    • "progressively":Rollout 扩容时逐步缩减 Deployment,失败时恢复 Deployment。
  • strategy:部署策略,支持 BlueGreenCanary
  • canary:灰度策略定义。
    • canaryService:控制器更新以选择灰度 Pod 的服务,流量路由必需。
    • stableService:控制器更新以选择稳定 Pod 的服务,流量路由必需。
    • steps:定义灰度更新的步骤序列,首次部署时跳过。
      • setWeight:设置灰度 ReplicaSet 的流量比例。
      • pause:暂停发布,支持单位 s、m、h,{} 表示无限期暂停。
      • plugin:执行配置的插件,此处配置了 gatewayAPI 插件。

使用命令应用:

kubectl apply -f rollout.yaml

这将为部署设置灰度发布策略。初始设置权重为 50,等待推广。50% 流量转发至灰度服务。推广后权重设为 100,100% 流量转发至灰度服务,最终灰度服务成为稳定服务。

验证 Rollout

创建 Rollout 后,Argo Rollouts 会创建一个与 Deployment 模板相同的新 ReplicaSet。当新 ReplicaSet 的 Pod 健康时,Deployment 会缩容至 0。

使用以下命令确认 Pod 正常运行:

kubectl argo rollouts get rollout rollout-canary
Name:            rollout-canary
Namespace:       default
Status: Healthy
Strategy:        Canary
Step:          9/9
SetWeight:     100
ActualWeight:  100
Images:          hello:1.23.1 (stable)
Replicas:
Desired:       2
Current:       2
Updated:       2
Ready:         2
Available:     2

NAME                                      KIND        STATUS     AGE  INFO
 rollout-canary                            Rollout Healthy  32s
└──# revision:1
  └──⧉ rollout-canary-5c9d79697b           ReplicaSet Healthy  32s  stable
    ├──□ rollout-canary-5c9d79697b-fh78d  Pod Running  32s  ready:1/1
    └──□ rollout-canary-5c9d79697b-rrbtj  Pod Running  32s  ready:1/1

准备灰度部署

接下来,准备新版本应用作为绿色部署。更新 Deployment web 的镜像版本(例如 hello:1.23.2)。使用命令:

kubectl patch deployment web -p '{"spec":{"template":{"spec":{"containers":[{"name":"web","image":"hello:1.23.2"}]}}}}'

这将为测试设置新版本应用。

Rollout 会创建新的 ReplicaSet 管理灰度 Pod,50% 流量转发至灰度 Pod。使用以下命令验证:

kubectl argo rollouts get rollout rollout-canary
Name:            rollout-canary
Namespace:       default
Status: Paused
Message:         CanaryPauseStep
Strategy:        Canary
Step:          1/3
SetWeight:     50
ActualWeight:  50
Images:          hello:1.23.1 (stable)
                hello:1.23.2 (canary)
Replicas:
Desired:       2
Current:       3
Updated:       1
Ready:         3
Available:     3

NAME                                      KIND        STATUS     AGE  INFO
 rollout-canary                            Rollout Paused   95s
├──# revision:2
  └──⧉ rollout-canary-5898765588           ReplicaSet Healthy  46s  canary
     └──□ rollout-canary-5898765588-ls5jk  Pod Running  45s  ready:1/1
└──# revision:1
  └──⧉ rollout-canary-5c9d79697b           ReplicaSet Healthy  95s  stable
    ├──□ rollout-canary-5c9d79697b-fk269  Pod Running  94s  ready:1/1
    └──□ rollout-canary-5c9d79697b-wkmcn  Pod Running  94s  ready:1/1

当前运行 3 个 Pod,包含稳定和灰度版本。权重为 50,50% 流量转发至灰度服务。发布流程暂停,等待推广。

如果使用 Helm Chart 部署应用,可用 Helm 工具升级到灰度版本。

访问 http://example.com,50% 流量将转发至灰度服务,响应应有所不同。

推广 Rollout

当灰度版本测试通过后,可推广 Rollout,将所有流量切换至灰度 Pod。使用命令:

kubectl argo rollouts promote rollout-canary

验证 Rollout 是否完成:

kubectl argo rollouts get rollout rollout-canary
Name:            rollout-canary
Namespace:       default
Status: Healthy
Strategy:        Canary
Step:          3/3
SetWeight:     100
ActualWeight:  100
Images:          hello:1.23.2 (stable)
Replicas:
Desired:       2
Current:       2
Updated:       2
Ready:         2
Available:     2

NAME                                      KIND        STATUS         AGE    INFO
 rollout-canary                            Rollout Healthy      8m42s
├──# revision:2
  └──⧉ rollout-canary-5898765588           ReplicaSet Healthy      7m53s  stable
     ├──□ rollout-canary-5898765588-ls5jk  Pod Running      7m52s  ready:1/1
     └──□ rollout-canary-5898765588-dkfwg  Pod Running      68s    ready:1/1
└──# revision:1
  └──⧉ rollout-canary-5c9d79697b           ReplicaSet ScaledDown   8m42s
    ├──□ rollout-canary-5c9d79697b-fk269  Pod Terminating  8m41s  ready:1/1
    └──□ rollout-canary-5c9d79697b-wkmcn  Pod Terminating  8m41s  ready:1/1

若稳定版本镜像更新为 hello:1.23.2,且 revision 1 的 ReplicaSet 缩容为 0,表示发布完成。

访问 http://example.com,100% 流量将转发至灰度服务。

中止 Rollout(可选)

若在发布过程中发现灰度版本存在问题,可中止发布,将所有流量切回稳定服务。使用命令:

kubectl argo rollouts abort rollout-canary

验证结果:

kubectl argo rollouts get rollout rollout-canary
Name:            rollout-demo
Namespace:       default
Status: Degraded
Message:         RolloutAborted: Rollout aborted update to revision 3
Strategy:        Canary
Step:          0/3
SetWeight:     0
ActualWeight:  0
Images:          hello:1.23.1 (stable)
Replicas:
Desired:       2
Current:       2
Updated:       0
Ready:         2
Available:     2

NAME                                      KIND        STATUS        AGE  INFO
 rollout-canary                            Rollout Degraded    18m
├──# revision:3
  └──⧉ rollout-canary-5c9d79697b           ReplicaSet ScaledDown  18m  canary,delay:passed
└──# revision:2
  └──⧉ rollout-canary-5898765588           ReplicaSet Healthy     17m  stable
    ├──□ rollout-canary-5898765588-ls5jk  Pod Running     17m  ready:1/1
    └──□ rollout-canary-5898765588-dkfwg  Pod Running     10m  ready:1/1

访问 http://example.com,100% 流量将转发至稳定服务。