应用灰度发布

灰度发布是一种渐进式发布策略,它会将新的应用版本逐步引入给一小部分用户或流量。这种增量式发布方式可让团队在全面部署之前监控系统行为、收集指标并确保稳定性。该方法可以显著降低风险,尤其是在生产环境中。

Argo Rollouts 是一个 Kubernetes 原生的渐进式交付控制器,可帮助实现高级部署策略。它通过提供 Canary、Blue-Green Deployments、Analysis Runs、Experimentation 和 Automated Rollbacks 等功能扩展了 Kubernetes 的能力。它可以与可观测性栈集成,进行基于指标的健康检查,并提供基于 CLI 和 监控面板的应用交付控制。

关键概念:

  • Rollout:Kubernetes 中的自定义资源定义(CRD),用于替代标准 Deployment 资源,从而支持 blue-green、canary deployment 等高级部署控制。
  • Canary Steps:一系列逐步切换流量的操作,例如先将 25% 的流量,再将 50% 的流量导向新版本。
  • Pause Steps:在进入下一步 canary step 之前引入等待间隔,以便进行手动或自动验证。

灰度发布的优势

  • 降低风险:先将变更发布到一小部分服务器上,可以在全面发布前发现并解决问题,从而将对用户的影响降到最低。
  • 增量式发布:这种方式支持逐步暴露新功能,有助于你有效监控性能和用户反馈。
  • 实时反馈:灰度发布能在真实环境下立即提供新版本性能和稳定性的洞察。
  • 灵活性:你可以根据性能指标调整部署过程,从而实现可动态控制的发布,并可按需暂停或回滚。
  • 成本效益:与 blue/green deployments 不同,灰度发布不需要单独的环境,因此资源利用率更高。

使用 Argo Rollouts 的灰度发布

Argo Rollouts 支持使用 canary deployment strategy 来发布 deployment,并通过 Gateway API Plugin 控制流量。在 ACP 中,你可以使用 ALB 作为 Gateway API Provider 来实现 Argo Rollouts 的流量控制。

前提条件

  1. 集群中已安装带有 Gateway API 插件的 Argo Rollouts。
  2. Argo Rollouts kubectl 插件(从这里安装)。
  3. 一个用于创建 namespace 的项目。
  4. 集群中已部署 ALB,并已分配给该项目。
  5. 集群中用于部署应用的 namespace。

操作步骤

创建 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 进行选择。
  • spec.containers:每个 pod 中运行的容器。
  • containers.name:容器名称。
  • containers.image:要运行的容器镜像。
  • containers.ports.containerPort:容器暴露的端口。

使用 kubectl 应用配置:

kubectl apply -f deployment.yaml

这就完成了生产环境的配置。

另外,你也可以使用 Helm Chart 来创建 deployments 和 services。

创建稳定版 Service

创建一个 Kubernetes Service 来暴露稳定版 deployment。该 Service 会基于匹配标签,将流量转发到稳定版的 pod。初始时,Service selector 会选择带有 app=web 标签的 pod。

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

这样即可对外访问稳定版 deployment。

创建 Canary Service

创建一个 Kubernetes Service 来暴露 canary deployment。该 Service 会基于匹配标签,将流量转发到 canary 版本的 pod。初始时,Service selector 会选择带有 app=web 标签的 pod。

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

这样即可对外访问 canary deployment。

创建 Gateway

使用 example.com 作为访问该 Service 的域名,创建 Gateway 以通过该域名暴露 Service:

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

访问稳定版 Service

在集群外部,使用以下命令通过域名访问 Service:

curl http://example.com

你也可以在浏览器中访问 http://example.com

创建 Rollout

接下来,使用 Canary strategy 创建 Argo Rollouts 的 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 会受到该 rollout 的影响。它必须与 pod template 的标签匹配。

  • workloadRef:指定 workload 引用以及要应用的 scale down 策略。

  • scaleDown:指定在迁移到 Rollout 后,是否缩减 workload(Deployment)。可选值如下:

    • "never":Deployment 不会缩减。
    • "onsuccess":Rollout 变为健康状态后,Deployment 会被缩减。
    • "progressively":随着 Rollout 逐步扩容,Deployment 会逐步缩减。如果 Rollout 失败,Deployment 会重新扩容。
  • strategy:Rollout strategy,支持 BlueGreenCanary strategy。

  • canaryCanary rollout strategy 的定义。

    • canaryService:控制器会更新该 Service 以选择 canary pod。流量路由时必需。
    • stableService:控制器会更新该 Service 以选择 stable pod。流量路由时必需。
    • steps:定义在 canary 更新期间执行的一系列步骤。首次部署 Rollout 时会跳过这些步骤。
      • setWeight:设置 canary ReplicaSet 的比例。
      • pause:无限期或指定时长暂停 Rollout。支持的单位:s、m、h。{} 表示无限期暂停。
      • plugin:执行已配置的插件,这里配置的是 gatewayAPI 插件。

使用以下命令应用:

kubectl apply -f rollout.yaml

这样就为该 deployment 配置了使用 Canary strategy 的 rollouts。它会先将权重设为 50,并等待你进行促升。50% 的流量会转发到 canary service。完成 rollout 的促升后,权重将设为 100,100% 的流量会转发到 canary service。最后,canary service 会成为 stable service。

验证 Rollout

创建 Rollout 后,Argo Rollouts 会根据 deployment 的相同 template 创建一个新的 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

准备 Canary Deployment

接下来,将应用的新版本作为 green deployment 准备好。使用新镜像版本更新 deployment web(例如 hello:1.23.2)。使用以下命令:

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

这样就为测试准备好了新的应用版本。

Rollouts 会创建一个新的 ReplicaSet 来管理 canary pod,50% 的流量将转发到 canary 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 在运行,分别是 stable 和 canary 版本。此时权重为 50,50% 的流量会转发到 canary service。rollout 流程已暂停,等待促升。

如果你使用 Helm Chart 来部署应用,请使用 helm 工具将应用升级到 canary 版本。

访问 http://example.com 时,50% 的流量会转发到 canary service。你应该会从该 URL 收到不同的响应。

促升 Rollout

当 canary 版本测试通过后,你可以促升 rollout,将所有流量切换到 canary 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

如果稳定版 Images 已更新为 hello:1.23.2,并且 revision 1 的 ReplicaSet 已缩减为 0,则表示 rollout 已完成。

访问 http://example.com 时,100% 的流量会转发到 canary service。

中止 Rollout(可选)

如果你在 rollout 过程中发现 canary 版本存在问题,可以中止该过程,将所有流量切换回 stable service。使用以下命令:

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% 的流量会转发到 stable service。