自动化金丝雀发布是挺好的,但是其实每个阶段金丝雀的流量比例都不同,通过延长发布时间来保护生产环境,降低发生生产事故的概率。

但实际上有个致命问题:无法自动判断金丝雀环境是否出错。

这可能会导致一种情况,当金丝雀环境在接受生产流量之后,它产生了大量的请求错误,在缺少人工介入的情况下,发布仍然按照计划进行,最终导致了生产环境的故障。

为了解决这个问题,我们希望渐进式交付变得更加智能,一个好的工程实践方式是:通过指标分析来自动判断金丝雀发布的质量,如果符合预期,就继续金丝雀步骤;如果不符合预期,则进行回滚。这样,也就能够避免将金丝雀环境的故障带到生产环境中了,这种分析方法也叫做金丝雀分析

将 Argo Rollout 和 Prometheus 结合,实现自动渐进式交付。

概述

整体架构和流程图如下:

image-20250420004225808

相比较金丝雀发布,自动渐进式交付增加了 Prometheus、Analysis Template 和 AnalysisRun 对象。其中,Analysis Template 定义用于分析的模板,AnalysisRun 是分析模板的实例化,Prometheus 是用来存储指标的数据库。

设计的自动渐进式交付流程会按照下面这张流程图来进行。

image-20250420005132291

自动渐进式交付开始时,首先会先将金丝雀环境的流量比例设置为 20% 并持续两分钟,然后将金丝雀环境的流量比例设置为 40% 并持续两分钟,然后再以此类推到 60%、80%,直到将金丝雀环境提升为生产环境为止。

从第二个阶段开始,自动金丝雀分析开始运行,在持续运行的过程中,如果金丝雀分析失败,那么金丝雀环境将进行自动回滚。这样就达到了自动渐进式交付的目的。

实践

接下来,我们进入到渐进式交付的实战环节,实战过程大致分成下面几个步骤。

  1. 创建生产环境,包括 Rollout 对象、Service 和 Ingress
  2. [创建用于自动金丝雀分析的 AnalysisTemplate 模板](#创建 Analysis Template)。
  3. 安装 Prometheus 并配置 Ingress-Nginx。
  4. 修改镜像版本,启动渐进式交付。

创建生产环境

首先,我们需要创建用于模拟生产环境的 Rollout 对象、Service 和 Ingress。将下面的内容保存为 apd-rollout-with-analysis.yaml 文件。

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: canary-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: canary-demo
  strategy:
    canary:
      analysis:
        templates:
          - templateName: success-rate
        startingStep: 2
        args:
          - name: ingress
            value: canary-demo
      canaryService: canary-demo-canary
      stableService: canary-demo
      trafficRouting:
        nginx:
          stableIngress: canary-demo
      steps:
        - setWeight: 20
        - pause:
            duration: 2m
        - setWeight: 40
        - pause:
            duration: 2m
        - setWeight: 60
        - pause:
            duration: 2m
        - setWeight: 80
        - pause:
            duration: 2m
  template:
    metadata:
      labels:
        app: canary-demo
    spec:
      containers:
        - image: argoproj/rollouts-demo:blue
          imagePullPolicy: Always
          name: canary-demo
          ports:
            - containerPort: 8080
              name: http
              protocol: TCP
          resources:
            requests:
              cpu: 5m
              memory: 32Mi
 

在 canary 字段下面增加了 analysis 字段,它的作用是指定金丝雀分析的模板,模板内容我们会在稍后创建。另外,这里同样使用了 argoproj/rollouts-demo:blue 镜像来模拟生产环境。

然后,使用 kubectl apply 命令将它应用到集群内。

$ kubectl apply -f rollout-with-analysis.yaml
rollout.argoproj.io/canary-demo created

接下来,我们还需要创建 Service 对象。在这里,我们可以一并创建生产环境和金丝雀环境所需要用到的 Service ,将下面的内容保存为 apd-canary-demo-service.yaml。

然后,使用 kubectl apply 命令将它应用到集群内。

  quartz git:(main*) kb apply -f content/CloudNative/ArgoCD/apd-canary-demo-service.yaml  
kbservice/canary-demo unchanged
service/canary-demo-canary unchanged

最后,再创建 Ingress 对象。将下面的内容保存为 apd-canary-demo-ingress.yaml 文件。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: canary-demo
  labels:
    app: canary-demo
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
    - host: progressive-auto-in-k8s.cheverjohn.me
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: canary-demo
                port:
                  name: http
  tls:
    - hosts:
        - progressive-auto-in-k8s.cheverjohn.me
      secretName: progressive-auto-in-k8s-tls
  ingressClassName: "nginx"
 

在这个 Ingress 对象中,指定了 https://progressive-auto-in-k8s.cheverjohn.me 作为访问域名。

然后,使用 kubectl apply 命令将它应用到集群内。

  quartz git:(main*) kb apply -f content/CloudNative/ArgoCD/apd-canary-demo-ingress.yaml 
kbingress.networking.k8s.io/canary-demo configured

创建 Analysis Template

由于我们在 Rollout 对象中指定了名为 success-rate 的金丝雀分析模板,所以我们还需要创建它。将下面的内容保存为 apd-analysis-success.yaml 文件。

apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: success-rate
spec:
  args:
    - name: ingress
  metrics:
    - name: success-rate
      interval: 10s
      failureLimit: 3
      successCondition: result[0] > 0.90
      provider:
        prometheus:
          address: http://kps-prometheus:9090
          query: >+
            sum(rate(nginx_ingress_controller_requests{ingress="{{args.ingress}}",status!~"[4-5].*"}[60s]))/sum(rate(nginx_ingress_controller_requests{ingress="{{args.ingress}}"}[60s]))
 

这里我简单介绍一下 AnalysisTemplate 对象字段的含义。首先 spec.args 字段定义了参数,该参数会在后续的 query 语句中使用,它的值是从 Rollout 对象的 canary.analysis.args 字段传递进来的。

这里需要额外注意的是, prometheus 的 address 应该配置正确。不然我一开始就会遇到报错。

image-20250420024755213

spec.metrics 字段定义了自动分析的相关配置。其中,interval 字段为频率,每 10 秒钟执行一次分析。failureLimit 字段代表“连续 3 次失败则金丝雀分析失败”,此时要执行回滚动作。successCondition 字段代表判断条件,这里的 result[0] 是一个表达式,代表的含义是当查询语句的返回值大于 0.90 时,说明本次金丝雀分析成功了。

最后,spec.metrics.provider 字段定义了分析数据来源于 Prometheus,还定义了 Prometheus Server 的连接地址,我们将在稍后部署 Prometheus。

query 字段是金丝雀分析的查询语句。这条查询语句的含义你可以简单地理解成:在 60 秒内 HTTP 状态码不为 4xx 和 5xx 的请求占所有请求的比例。换句话说,当 HTTP 请求成功的比例大于 0.90 时,代表一次金丝雀分析成功。

访问生产环境

接下来,为了访问生产环境,地址为 https://progressive-auto-in-k8s.cheverjohn.me

配置 Ingress-Nginx 和 ServiceMonitor

为了让 Prometheus 能够顺利地获取到 HTTP 请求指标,我们需要打开 Ingress-Nginx Metric 指标端口。并且有 ServiceMonitor。

这一块,因为我本来就是使用 helm upgrade —install 安装的 ingress-nginx。所以我更新了一下安装命令,如下:

helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx \
  --version 4.12.1 \
  -n ingress-nginx \
  --create-namespace \
  --set controller.replicaCount=1 \
  --set controller.resources.requests.cpu=100m \
  --set controller.resources.requests.memory=128Mi \
  --set controller.resources.limits.cpu=500m \
  --set controller.resources.limits.memory=512Mi \
  --set controller.metrics.enabled=true \
  --set controller.metrics.serviceMonitor.enabled=true \
  --set controller.metrics.serviceMonitor.namespace=ingress-nginx \
  --set controller.metrics.serviceMonitor.additionalLabels.release=kps \
  --set-string controller.podAnnotations."prometheus\.io/scrape"="true" \
  --set-string controller.podAnnotations."prometheus\.io/port"="10254" \
  --set global.imageRegistry=harbor.cheverjohn.me/quay.io \
  --set grafana.enabled=false \
  --set prometheus.enabled=false

当把 ServiceMonitor 应用到集群后,Prometheus 会按照标签来匹配 Ingress-Nginx Pod,并且会每 10s 主动拉取一次指标数据,并保存到 Prometheus 时序数据库中。

接下来,我们验证 Prometheus 是否已经成功获取到了 Ingress-Nginx 指标,这将决定自动金丝雀分析是否能成功获取到数据。

我们可以进入 Prometheus 控制台验证是否成功获取了 Ingress-Nginx 指标。首先,使用 kubectl port-forward 命令将 Prometheus 转发到本地。

$ kubectl port-forward service/prometheus-kube-prometheus-prometheus 9090:9090 -n prometheus
Forwarding from 127.0.0.1:9090 -> 9090
Forwarding from [::1]:9090 -> 9090 

验证 Ingress-Nginx 指标

验证 Prometheus 是否已经成功获取到了 ingress-nginx 指标,如下:

image-20250420021530148

这将决定自动金丝雀分析是否能成功获取到数据。

我的 ingress-nginx 数据如下

image-20250420021628349

自动渐近式交付实验

现在,所有的准备工作都已经完成了,接下来我们进行自动渐进式交付实验。

让我们重新回忆一下我在前面提到的这张流程图。

image-20250420005132291

在实验过程过程中,我会按照这张流程图分别进行两个实验。

  1. 自动渐进式交付成功(图中①号链路)。
  2. 自动渐进式交付失败(图中②号链路)。

自动渐进式交付成功

接下来,我们进行自动渐进式交付成功的实验。

要开始实验,只要更新 Rollout 对象的镜像版本即可。之前编辑 Rollout 对象并通过 kubectl apply 的方法来更新镜像版本。

这里我们使用另一种更新镜像的方法,通过 Argo Rollout kubectl 插件来更新镜像。

$ kubectl argo rollouts set image canary-demo canary-demo=argoproj/rollouts-demo:blue
rollout "canary-demo" image updated

image-20250420024842201

image-20250420024916848

自动渐进式交付失败

在上面的实验中,由于应用返回的 HTTP 状态码都是 200 ,所以金丝雀分析自然是会成功的。

接下来,我们来尝试进行自动渐进式交付失败的实验。

经过了自动渐进式交付成功的实验之后,当前生产环境中的镜像为 argoproj/rollouts-demo:green,我们继续使用 Argo Rollout kubectl 插件来更新镜像,并将镜像版本修改为 yellow 版本。

$ kubectl argo rollouts set image canary-demo canary-demo=argoproj/rollouts-demo:yellow
rollout "canary-demo" image updated

接下来,重新返回 https://progressive-auto-in-k8s.cheverjohn.me/ 打开应用,等待一段时间后,你会看到请求开始出现黄色方块,如下图所示。

image-20250420031834908

image-20250420033223299

image-20250420033253872