使用 Argo Rollouts 实现应用渐进式发布
架构
下面展示了由 Argo Rollouts 管理的 Deployment 的所有组件。
Rollout Controller
这是主控制器,用于监视集群的事件并在 Rollout 类型的资源发生更改时做出反应。控制器将读取 rollout 的所有详细信息,并使集群处于 rollout 定义中描述的相同状态。
请注意,Argo Rollouts 不会篡改或响应正常 Deployment 资源上发生的任何变更,这意味着你可以在一个使用其他方法部署应用的集群中安装 Argo Rollouts。
Rollout 资源
Rollout 资源是 Argo Rollouts 引入和管理的一种自定义 Kubernetes 资源,它与原生的 Kubernetes Deployment 资源基本兼容,但有额外的字段来控制更加高级的部署方法,如金丝雀和蓝/绿部署。
Argo Rollouts 控制器将只对 Rollout 资源中的变化做出反应,不会对正常的 Deployment 资源做任何事情,所以如果你想用 Argo Rollouts 管理你的 Deployment,你需要将你的 Deployment 迁移到 Rollouts。
AnalysisTemplate 与 AnalysisRun
Analysis
是一种自定义 Kubernetes 资源,它将 Rollout 连接到指标提供程序,并为某些指标定义特定阈值,这些阈值将决定 Rollout 是否成功。对于每个 Analysis,你可以定义一个或多个指标查询及其预期结果,如果指标查询正常,则 Rollout 将继续发布;如果指标显示失败,则自动回滚;如果指标无法提供成功/失败的结果,则暂停发布。
为了执行分析,Argo Rollouts 提供了两个自定义的 Kubernetes 资源:AnalysisTemplate
和 AnalysisRun
。
AnalysisTemplate
包含有关要查询哪些指标的说明。附加到 Rollout
的实际结果是 AnalysisRun
自定义资源,可以在特定 的 Rollout
上定义 AnalysisTemplate
,也可以在集群上定义全局的 AnalysisTemplate
,以供多个 Rollout
共享作为 ClusterAnalysisTemplate
,而 AnalysisRun
资源的范围仅限于特定的 rollout。
请注意,在 Rollout 中使用分析和指标是完全可选的,你可以通过 API 或 CLI 手动暂停和继续发布,也可以使用其他外部方法(例如冒烟测试)。你不需要仅使用 Argo Rollouts 的指标解决方案,你还可以在 Rollout 中混合自动(即基于分析)和手动步骤。
除了指标之外,你还可以通过运行 Kubernetes Job 或运行 webhook 来决定发布的成功与否。
Metric Providers
Argo Rollouts 包括多个流行指标提供程序的本机集成,您可以在分析资源中使用这些提供程序来自动升级或回滚部署。有关特定设置选项,请参阅每个提供商的文档。
Argo Rollouts 包括几个流行的指标提供者的集成,你可以在分析资源中使用,来自动升级或回滚发布。
安装
直接使用下面的命令安装 Argo Rollouts:
$ kubectl create namespace argo-rollouts
$ kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/download/v1.6.0/install.yaml
这里会创建一个名为 argo-rollouts
的命名空间,Argo Rollouts 控制器运行在上面。
$ kubectl get pods -n argo-rollouts
NAME READY STATUS RESTARTS AGE
argo-rollouts-b9bbbc884-5h5jz 1/1 Running 0 7m21s
$ kubectl get crd |grep argo
analysisruns.argoproj.io 2023-09-21T06:20:34Z
analysistemplates.argoproj.io 2023-09-21T06:20:34Z
clusteranalysistemplates.argoproj.io 2023-09-21T06:20:34Z
experiments.argoproj.io 2023-09-21T06:20:35Z
rollouts.argoproj.io 2023-09-21T06:20:35Z
此外,我们还可以安装一个 kubectl 插件,对于命令行管理和可视化发布非常方便。使用 curl 安装 Argo Rollouts kubectl 插件:
# https://ghproxy.com/https://github.com//argoproj/argo-rollouts/releases/download/v1.6.0/kubectl-argo-rollouts-linux-amd64
$ curl -LO https://github.com/argoproj/argo-rollouts/releases/download/v1.6.0/kubectl-argo-rollouts-linux-amd64
然后赋予 kubectl-argo-rollouts
二进制文件可执行权限:
$ chmod +x ./kubectl-argo-rollouts-linux-amd64
将该二进制文件移动到你的 PATH 路径下面去:
$ sudo mv ./kubectl-argo-rollouts-linux-amd64 /usr/local/bin/kubectl-argo-rollouts
执行下面的命令来验证插件是否安装成功:
$ kubectl argo rollouts version
kubectl-argo-rollouts: v1.6.0+7eae71e
BuildDate: 2023-09-06T18:36:42Z
GitCommit: 7eae71ed89f1a3769864435bddebe3ca05384df3
GitTreeState: clean
GoVersion: go1.20.7
Compiler: gc
Platform: linux/amd64
Dashboard
Argo Rollouts Kubectl 插件可以提供一个本地 Dashboard,来可视化你的 Rollouts。
要启动这个 Dashboard,需要在包含 Rollouts 资源对象的命名空间中运行 kubectl argo rollouts dashboard
命令,然后访问localhost:3100
即可。
点击 Rollout 可以进行详细页面,在详细页面可以看到 Rollout 的配置信息,还可以直接在 UI 界面上执行一些常用的操作,比如重启、重启、中断等。
Analysis 和渐进式交互
Argo Rollouts 提供了几种执行分析的方法来推动渐进式交付,首先需要了解几个 CRD 资源:
-
Rollout
:Rollout 是 Deployment 资源的直接替代品,它提供额外的blueGreen
和canary
更新策略,这些策略可以在更新期间创建AnalysisRuns
和Experiments
,可以推进更新,或中止更新。 -
Experiments
:Experiments CRD 允许用户临时运行一个或多个 ReplicaSet。除了运行临时 ReplicaSet 之外,Experiments CRD 还可以与 ReplicaSet 一起启动 AnalysisRuns。通常,这些 AnalysisRun 用于确认新的 ReplicaSet 是否按预期运行。 -
AnalysisTemplate
:AnalysisTemplate 是一个模板,它定义了如何执行金丝雀分析,例如它应该执行的指标、频率以及被视为成功或失败的值,AnalysisTemplate 可以用输入值进行参数化。 -
ClusterAnalysisTemplate
:ClusterAnalysisTemplate 和 AnalysisTemplate 类似,但它是全局范围内的,它可以被整个集群的任何 Rollout 使用。 -
AnalysisRun
:AnalysisRun 是 AnalysisTemplate 的实例化。AnalysisRun 就像 Job 一样,它们最终会完成,完成的运行被认为是成功的、失败的或不确定的,运行的结果分别影响 Rollout 的更新是否继续、中止或暂停。
后台分析
金丝雀正在执行其部署步骤时,分析可以在后台运行。
以下示例是每 10 分钟逐渐将 Canary 权重增加 20%,直到达到 100%。在后台,基于名为 success-rate
的 AnalysisTemplate
启动 AnalysisRun
,success-rate
模板查询 Prometheus 服务器,以 5 分钟间隔/样本测量 HTTP 成功率,它没有结束时间,一直持续到停止或失败。如果测量到的指标小于 95%,并且有三个这样的测量值,则分析被视为失败。失败的分析会导致 Rollout 中止,将 Canary 权重设置回零,并且 Rollout 将被视为降级。否则,如果 Rollout 完成其所有 Canary 步骤,则认为 rollout 是成功的,并且控制器将停止运行分析。
如下所示的 Rollout 资源对象:
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: guestbook
spec:
# ...
strategy:
canary:
analysis:
templates:
- templateName: success-rate
startingStep: 2 # 延迟开始分析,到第3步开始
args:
- name: service-name
value: guestbook-svc.default.svc.cluster.local
steps:
- setWeight: 20
- pause: { duration: 10m }
- setWeight: 40
- pause: { duration: 10m }
- setWeight: 60
- pause: { duration: 10m }
- setWeight: 80
- pause: { duration: 10m }
上面我们引用了一个 success-rate
的模板:
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: success-rate
spec:
args:
- name: service-name
metrics:
- name: success-rate
interval: 5m
# NOTE: prometheus queries return results in the form of a vector.
# So it is common to access the index 0 of the returned array to obtain the value
successCondition: result[0] >= 0.95
failureLimit: 3
provider:
prometheus:
address: http://prometheus.example.com:9090
query: |
sum(irate(
istio_requests_total{reporter="source",destination_service=~"{{args.service-name}}",response_code!~"5.*"}[5m]
)) /
sum(irate(
istio_requests_total{reporter="source",destination_service=~"{{args.service-name}}"}[5m]
))
内联分析
分析也可以作为内嵌分析步骤来执行,当分析以内联方式进行时,在到达该步骤时启动 AnalysisRun,并在运行完成之前阻止其推进。分析运行的成功或失败决定了部署是继续进行下一步,还是完全中止部署。
如下所示的示例中我们将 Canary 权重设置为 20%,暂停 5 分钟,然后运行分析。如果分析成功,则继续推出,否则中止。
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: guestbook
spec:
# ...
strategy:
canary:
steps:
- setWeight: 20
- pause: { duration: 5m }
- analysis:
templates:
- templateName: success-rate
args:
- name: service-name
value: guestbook-svc.default.svc.cluster.local
上面的对象中我们将 analysis 作为一个步骤内联到了 Rollout 步骤中,当 20%流量暂停 5 分钟后,开始执行 success-rate
这个分析模板。
这里 AnalysisTemplate 与上面的后台分析例子相同,但由于没有指定间隔时间,分析将执行一次测量就完成了。
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: success-rate
spec:
args:
- name: service-name
- name: prometheus-port
value: 9090
metrics:
- name: success-rate
successCondition: result[0] >= 0.95
provider:
prometheus:
address: "http://prometheus.example.com:{{args.prometheus-port}}"
query: |
sum(irate(
istio_requests_total{reporter="source",destination_service=~"{{args.service-name}}",response_code!~"5.*"}[5m]
)) /
sum(irate(
istio_requests_total{reporter="source",destination_service=~"{{args.service-name}}"}[5m]
))
此外我们可以通过指定 count 和 interval 字段,可以在一个较长的时间段内进行多次测量。
metrics:
- name: success-rate
successCondition: result[0] >= 0.95
interval: 60s
count: 5
provider:
prometheus:
address: http://prometheus.example.com:9090
query: ...
多个模板的分析
Rollout 在构建 AnalysisRun 时可以引用多个 AnalysisTemplate。这样我们就可以从多个 AnalysisTemplate 中来组成分析,如果引用了多个模板,那么控制器将把这些模板合并在一起,控制器会结合所有模板的指标和 args 字段。如下所示:
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: guestbook
spec:
# ...
strategy:
canary:
analysis:
templates:
- templateName: success-rate
- templateName: error-rate
args:
- name: service-name
value: guestbook-svc.default.svc.cluster.local
---
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: success-rate
spec:
args:
- name: service-name
metrics:
- name: success-rate
interval: 5m
successCondition: result[0] >= 0.95
failureLimit: 3
provider:
prometheus:
address: http://prometheus.example.com:9090
query: |
sum(irate(
istio_requests_total{reporter="source",destination_service=~"{{args.service-name}}",response_code!~"5.*"}[5m]
)) /
sum(irate(
istio_requests_total{reporter="source",destination_service=~"{{args.service-name}}"}[5m]
))---
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: error-rate
spec:
args:
- name: service-name
metrics:
- name: error-rate
interval: 5m
successCondition: result[0] <= 0.95
failureLimit: 3
provider:
prometheus:
address: http://prometheus.example.com:9090
query: |
sum(irate(
istio_requests_total{reporter="source",destination_service=~"{{args.service-name}}",response_code=~"5.*"}[5m]
)) /
sum(irate(
istio_requests_total{reporter="source",destination_service=~"{{args.service-name}}"}[5m]
))
当执行的分析的时候,控制器会将上面的 success-rate
和 error-rate
两个模板合并到一个 AnalysisRun
对象中去。
需要注意的是如果出现以下情况,控制器在合并模板时将出错:
- 模板中的多个指标具有相同的名称
- 两个同名的参数都有值
分析模板参数
AnalysisTemplates 可以声明一组参数,这些参数可以由 Rollouts 传递。然后,这些参数可以像在 metrics 配置中一样使用,并在 AnalysisRun 创建时被实例化,参数占位符被定义为 {{ args.<name> }}
,如下所示:
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: args-example
spec:
args:
# required
- name: service-name
- name: stable-hash
- name: latest-hash
# optional
- name: api-url
value: http://example/measure
# from secret
- name: api-token
valueFrom:
secretKeyRef:
name: token-secret
key: apiToken
metrics:
- name: webmetric
successCondition: result == 'true'
provider:
web:
# placeholders are resolved when an AnalysisRun is created
url: "{{ args.api-url }}?service={{ args.service-name }}"
headers:
- key: Authorization
value: "Bearer {{ args.api-token }}"
jsonPath: "{$.results.ok}"
在创建 AnalysisRun 时,Rollout 中定义的参数与 AnalysisTemplate 的参数会合并,如下所示:
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: guestbook
spec:
---
strategy:
canary:
analysis:
templates:
- templateName: args-example
args:
# required value
- name: service-name
value: guestbook-svc.default.svc.cluster.local
# override default value
- name: api-url
value: http://other-api
# pod template hash from the stable ReplicaSet
- name: stable-hash
valueFrom:
podTemplateHashValue: Stable
# pod template hash from the latest ReplicaSet
- name: latest-hash
valueFrom:
podTemplateHashValue: Latest
此外分析参数也支持 valueFrom
,用于读取 meta 数据并将其作为参数传递给 AnalysisTemplate,如下例子是引用元数据中的 env 和 region 标签,并将它们传递给 AnalysisTemplate。
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: guestbook
labels:
appType: demo-app
buildType: nginx-app
...
env: dev
region: us-west-2
spec:
...
strategy:
canary:
analysis:
templates:
- templateName: args-example
args:
...
- name: env
valueFrom:
fieldRef:
fieldPath: metadata.labels['env']
# region where this app is deployed
- name: region
valueFrom:
fieldRef:
fieldPath: metadata.labels['region']
蓝绿预发布分析
使用 BlueGreen 策略的 Rollout 可以在使用预发布将流量切换到新版本之前启动一个 AnalysisRun。分析运行的成功或失败决定 Rollout 是否切换流量,或完全中止 Rollout,如下所示:
kind: Rollout
metadata:
name: guestbook
spec:
---
strategy:
blueGreen:
activeService: active-svc
previewService: preview-svc
prePromotionAnalysis:
templates:
- templateName: smoke-tests
args:
- name: service-name
value: preview-svc.default.svc.cluster.local
上面我们的示例中一旦新的 ReplicaSet 完全可用,Rollout 会创建一个预发布的 AnalysisRun,Rollout 不会将流量切换到新版本,而是会等到分析运行成功完成。
注意:如果指定了 autoPromotionSeconds
字段,并且 Rollout 已经等待了 auto promotion seconds
的时间,Rollout 会标记 AnalysisRun 成功,并自动将流量切换到新版本。如果 AnalysisRun 在此之前完成,Rollout 将不会创建另一个 AnalysisRun,并等待 autoPromotionSeconds
的剩余时间。
蓝绿发布后分析
使用 BlueGreen 策略的 Rollout 还可以在流量切换到新版本后使用发布后分析。如果发布后分析失败或出错,Rollout 则进入中止状态,并将流量切换回之前的稳定 ReplicaSet,当后分析成功时,Rollout 被认为是完全发布状态,新的 ReplicaSet 将被标记为稳定,然后旧的 ReplicaSet 将根据 scaleDownDelaySeconds
(默认为 30 秒)进行缩减。
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: guestbook
spec:
---
strategy:
blueGreen:
activeService: active-svc
previewService: preview-svc
scaleDownDelaySeconds: 600 # 10 minutes
postPromotionAnalysis:
templates:
- templateName: smoke-tests
args:
- name: service-name
value: preview-svc.default.svc.cluster.local
失败条件
failureCondition
可以用来配置分析运行失败,下面的例子是每隔 5 分钟持续轮询 Prometheus 服务器来获得错误总数,如果遇到 10 个或更多的错误,则认为分析运行失败。
metrics:
- name: total-errors
interval: 5m
failureCondition: result[0] >= 10
failureLimit: 3
provider:
prometheus:
address: http://prometheus.example.com:9090
query: |
sum(irate(
istio_requests_total{reporter="source",destination_service=~"{{args.service-name}}",response_code~"5.*"}[5m]
))
无结果的运行
分析运行 j 结果也可以被认为是不确定的,这表明运行既不成功,也不失败。无结果的运行会导致发布在当前步骤上暂停。这时需要人工干预,以恢复运行,或中止运行。当一个指标没有定义成功或失败的条件时,分析运行可能成为无结果的一个例子。
metrics:
- name: my-query
provider:
prometheus:
address: http://prometheus.example.com:9090
query: ...
此外当同时指定了成功和失败的条件,但测量值没有满足任何一个条件时,也可能发生不确定的分析运行。
metrics:
- name: success-rate
successCondition: result[0] >= 0.90
failureCondition: result[0] < 0.50
provider:
prometheus:
address: http://prometheus.example.com:9090
query: ...
不确定的分析运行的一个场景是使 Argo Rollouts 能够自动执行分析运行,并收集测量结果,但仍然允许我们来判断决定测量值是否可以接受,并决定继续或中止。
延迟分析运行
如果分析运行不需要立即开始(即给指标提供者时间来收集金丝雀版本的指标),分析运行可以延迟特定的指标分析。每个指标可以被配置为有不同的延迟,除了特定指标的延迟之外,具有后台分析的发布可以延迟创建分析运行,直到达到某个步骤为止
如下所示延迟一个指定的分析指标:
metrics:
- name: success-rate
# Do not start this analysis until 5 minutes after the analysis run starts
initialDelay: 5m
successCondition: result[0] >= 0.90
provider:
prometheus:
address: http://prometheus.example.com:9090
query: ...
延迟开始后台分析运行,直到步骤 3(设定重量 40%)。
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: guestbook
spec:
strategy:
canary:
analysis:
templates:
- templateName: success-rate
startingStep: 2
steps:
- setWeight: 20
- pause: { duration: 10m }
- setWeight: 40
- pause: { duration: 10m }
Job Metrics
此外 Kubernetes Job 还可用于运行分析,使用 Job 时,如果 Job 完成且退出代码为零,则指标被视为成功,否则指标失败。
metrics:
- name: test
provider:
job:
metadata:
annotations:
foo: bar # annotations defined here will be copied to the Job object
labels:
foo: bar # labels defined here will be copied to the Job object
spec:
backoffLimit: 1
template:
spec:
containers:
- name: test
image: my-image:latest
command:
[my-test-script, my-service.default.svc.cluster.local]
restartPolicy: Never
Web Metrics
同样还可以针对某些外部服务执行 HTTP 请求来获取测量结果。下面示例向某个 URL 发出 HTTP GET 请求。 Webhook 响应必须返回 JSON 内容。jsonPath
表达式的结果将分配给可在 successCondition
和 failureCondition
表达式中引用的 result 变量。如果省略,将使用整个 body
作为结果变量。
metrics:
- name: webmetric
successCondition: result == true
provider:
web:
url: "http://my-server.com/api/v1/measurement?service={{ args.service-name }}"
timeoutSeconds: 20 # defaults to 10 seconds
headers:
- key: Authorization
value: "Bearer {{ args.api-token }}"
jsonPath: "{$.data.ok}"
比如下面的示例表示如果 data.ok
字段为真且 data.successPercent
大于 0.90
,测量将是成功的。
{ "data": { "ok": true, "successPercent": 0.95 } }
metrics:
- name: webmetric
successCondition: "result.ok && result.successPercent >= 0.90"
provider:
web:
url: "http://my-server.com/api/v1/measurement?service={{ args.service-name }}"
headers:
- key: Authorization
value: "Bearer {{ args.api-token }}"
jsonPath: "{$.data}"
当然关于 Argo Rollouts 的使用还有很多细节,可以参考官方文档:https://argoproj.github.io/argo-rollouts/以了解更多。
文章转载自公众号:k8s技术圈