Argo Rollouts 基于 Analysis 的漸進(jìn)式發(fā)布
前面我們介紹了使用手動(dòng)的方式來(lái)控制 Argo Rollouts 進(jìn)行應(yīng)用交付,此外我們還可以利用 Argo Rollouts 提供的分析(Analysis)來(lái)執(zhí)行自動(dòng)交付。Argo Rollouts 提供了幾種執(zhí)行分析(Analysis)的方法來(lái)推動(dòng)漸進(jìn)式交付,首先需要了解幾個(gè) CRD 資源:
- Rollout:Rollout 是 Deployment 資源的直接替代品,它提供額外的 blueGreen 和 canary 更新策略,這些策略可以在更新期間創(chuàng)建 AnalysisRuns 和 Experiments,可以推進(jìn)更新,或中止更新。
- AnalysisTemplate:AnalysisTemplate 是一個(gè)模板,它定義了如何執(zhí)行金絲雀分析,例如它應(yīng)該執(zhí)行的指標(biāo)、頻率以及被視為成功或失敗的值,AnalysisTemplate 可以用輸入值進(jìn)行參數(shù)化。
- ClusterAnalysisTemplate:ClusterAnalysisTemplate 和 AnalysisTemplate 類似,但它是全局范圍內(nèi)的,它可以被整個(gè)集群的任何 Rollout 使用。
- AnalysisRun:AnalysisRun 是 AnalysisTemplate 的實(shí)例化。AnalysisRun 就像 Job 一樣,它們最終會(huì)完成,完成的運(yùn)行被認(rèn)為是成功的、失敗的或不確定的,運(yùn)行的結(jié)果分別影響 Rollout 的更新是否繼續(xù)、中止或暫停。
后臺(tái)分析
金絲雀正在執(zhí)行其部署步驟時(shí),分析可以在后臺(tái)運(yùn)行。
以下示例是每 10 分鐘逐漸將 Canary 權(quán)重增加 20%,直到達(dá)到 100%。在后臺(tái),基于名為 success-rate 的 AnalysisTemplate 啟動(dòng) AnalysisRun,success-rate 模板查詢 Prometheus 服務(wù)器,以 5 分鐘間隔/樣本測(cè)量 HTTP 成功率,它沒有結(jié)束時(shí)間,一直持續(xù)到停止或失敗。如果測(cè)量到的指標(biāo)小于 95%,并且有三個(gè)這樣的測(cè)量值,則分析被視為失敗。失敗的分析會(huì)導(dǎo)致 Rollout 中止,將 Canary 權(quán)重設(shè)置回零,并且 Rollout 將被視為降級(jí)。否則,如果 rollout 完成其所有 Canary 步驟,則認(rèn)為 rollout 是成功的,并且控制器將停止運(yùn)行分析。
如下所示的 Rollout 資源對(duì)象:
- 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}
上面我們引用了一個(gè) 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]
- ))
內(nèi)聯(lián)分析
分析也可以作為內(nèi)嵌“分析”步驟來(lái)執(zhí)行,當(dāng)分析以 "內(nèi)聯(lián) "方式進(jìn)行時(shí),在到達(dá)該步驟時(shí)啟動(dòng) AnalysisRun,并在運(yùn)行完成之前阻止其推進(jìn)。分析運(yùn)行的成功或失敗決定了部署是繼續(xù)進(jìn)行下一步,還是完全中止部署。
如下所示的示例中我們將 Canary 權(quán)重設(shè)置為 20%,暫停 5 分鐘,然后運(yùn)行分析。如果分析成功,則繼續(xù)發(fā)布,否則中止。
- 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
上面的對(duì)象中我們將 analysis 作為一個(gè)步驟內(nèi)聯(lián)到了 Rollout 步驟中,當(dāng)20%流量暫停5分鐘后,開始執(zhí)行 success-rate 這個(gè)分析模板。
這里 AnalysisTemplate 與上面的后臺(tái)分析例子相同,但由于沒有指定間隔時(shí)間,分析將執(zhí)行一次測(cè)量就完成了。
- 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]
- ))
此外我們可以通過(guò)指定 count 和 interval 字段,可以在一個(gè)較長(zhǎng)的時(shí)間段內(nèi)進(jìn)行多次測(cè)量。
- metrics:
- - name: success-rate
- successCondition: result[0] >= 0.95
- interval: 60s
- count: 5
- provider:
- prometheus:
- address: http://prometheus.example.com:9090
- query: ...
多個(gè)模板的分析
Rollout 在構(gòu)建 AnalysisRun 時(shí)可以引用多個(gè) AnalysisTemplate。這樣我們就可以從多個(gè) AnalysisTemplate 中來(lái)組成分析,如果引用了多個(gè)模板,那么控制器將把這些模板合并在一起,控制器會(huì)結(jié)合所有模板的指標(biāo)和 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]
- ))
當(dāng)執(zhí)行的分析的時(shí)候,控制器會(huì)將上面的 success-rate 和 error-rate 兩個(gè)模板合并到一個(gè) AnalysisRun 對(duì)象中去。
需要注意的是如果出現(xiàn)以下情況,控制器在合并模板時(shí)將出錯(cuò):
- 模板中的多個(gè)指標(biāo)具有相同的名稱
- 兩個(gè)同名的參數(shù)都有值
分析模板參數(shù)
AnalysisTemplates 可以聲明一組參數(shù),這些參數(shù)可以由 Rollouts 傳遞。然后,這些參數(shù)可以像在 metrics 配置中一樣使用,并在 AnalysisRun 創(chuàng)建時(shí)被實(shí)例化,參數(shù)占位符被定義為 {{ args.
- 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}"
在創(chuàng)建 AnalysisRun 時(shí),Rollout 中定義的參數(shù)與 AnalysisTemplate 的參數(shù)會(huì)合并,如下所示:
- 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
此外分析參數(shù)也支持 valueFrom,用于讀取 meta 數(shù)據(jù)并將其作為參數(shù)傳遞給 AnalysisTemplate,如下例子是引用元數(shù)據(jù)中的 env 和 region 標(biāo)簽,并將它們傳遞給 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']
藍(lán)綠預(yù)發(fā)布分析
使用 BlueGreen 策略的 Rollout 可以在使用預(yù)發(fā)布將流量切換到新版本之前啟動(dòng)一個(gè) AnalysisRun。分析運(yùn)行的成功或失敗決定 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 會(huì)創(chuàng)建一個(gè)預(yù)發(fā)布的 AnalysisRun,Rollout 不會(huì)將流量切換到新版本,而是會(huì)等到分析運(yùn)行成功完成。
注意:如果指定了 autoPromotionSeconds 字段,并且 Rollout 已經(jīng)等待了 auto promotion seconds 的時(shí)間,Rollout 會(huì)標(biāo)記 AnalysisRun 成功,并自動(dòng)將流量切換到新版本。如果 AnalysisRun 在此之前完成,Rollout 將不會(huì)創(chuàng)建另一個(gè) AnalysisRun,并等待 autoPromotionSeconds 的剩余時(shí)間。
藍(lán)綠發(fā)布后分析
使用 BlueGreen 策略的 Rollout 還可以在流量切換到新版本后使用發(fā)布后分析。如果發(fā)布后分析失敗或出錯(cuò),Rollout 則進(jìn)入中止?fàn)顟B(tài),并將流量切換回之前的穩(wěn)定 Replicaset,當(dāng)后分析成功時(shí),Rollout 被認(rèn)為是完全發(fā)布狀態(tài),新的 ReplicaSet 將被標(biāo)記為穩(wěn)定,然后舊的 ReplicaSet 將根據(jù) scaleDownDelaySeconds(默認(rèn)為30秒)進(jìn)行縮減。
- 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 可以用來(lái)配置分析運(yùn)行失敗,下面的例子是每隔5分鐘持續(xù)輪詢 Prometheus 服務(wù)器來(lái)獲得錯(cuò)誤總數(shù),如果遇到10個(gè)或更多的錯(cuò)誤,則認(rèn)為分析運(yùn)行失敗。
- 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]
- ))
無(wú)結(jié)果的運(yùn)行
分析運(yùn)行j結(jié)果也可以被認(rèn)為是不確定的,這表明運(yùn)行既不成功,也不失敗。無(wú)結(jié)果的運(yùn)行會(huì)導(dǎo)致發(fā)布在當(dāng)前步驟上暫停。這時(shí)需要人工干預(yù),以恢復(fù)運(yùn)行,或中止運(yùn)行。當(dāng)一個(gè)指標(biāo)沒有定義成功或失敗的條件時(shí),分析運(yùn)行可能成為無(wú)結(jié)果的一個(gè)例子。
- metrics:
- - name: my-query
- provider:
- prometheus:
- address: http://prometheus.example.com:9090
- query: ...
此外當(dāng)同時(shí)指定了成功和失敗的條件,但測(cè)量值沒有滿足任何一個(gè)條件時(shí),也可能發(fā)生不確定的分析運(yùn)行。
- metrics:
- - name: success-rate
- successCondition: result[0] >= 0.90
- failureCondition: result[0] < 0.50
- provider:
- prometheus:
- address: http://prometheus.example.com:9090
- query: ...
不確定的分析運(yùn)行的一個(gè)場(chǎng)景是使 Argo Rollouts 能夠自動(dòng)執(zhí)行分析運(yùn)行,并收集測(cè)量結(jié)果,但仍然允許我們來(lái)判斷決定測(cè)量值是否可以接受,并決定繼續(xù)或中止。
延遲分析運(yùn)行
如果分析運(yùn)行不需要立即開始(即給指標(biāo)提供者時(shí)間來(lái)收集金絲雀版本的指標(biāo)),分析運(yùn)行可以延遲特定的指標(biāo)分析。每個(gè)指標(biāo)可以被配置為有不同的延遲,除了特定指標(biāo)的延遲之外,具有后臺(tái)分析的發(fā)布可以延遲創(chuàng)建分析運(yùn)行,直到達(dá)到某個(gè)步驟為止
如下所示延遲一個(gè)指定的分析指標(biāo):
- 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: ...
延遲開始后臺(tái)分析運(yùn)行,直到步驟3(設(shè)定重量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}
引用 Secret
AnalysisTemplate 和 AnalysisRun 可以在 .spec.args 中引用 Secret 對(duì)象,這允許用戶安全地將認(rèn)證信息傳遞給指標(biāo)提供方,如登錄憑證或 API 令牌。
需要注意一個(gè) AnalysisRun 只能引用它所運(yùn)行的同一命名空間的 Secret。
如下所示的例子中,一個(gè) AnalysisTemplate 引用了一個(gè) API 令牌,并把它傳遞給一個(gè)Web 指標(biāo)提供者。
- apiVersion: argoproj.io/v1alpha1
- kind: AnalysisTemplate
- spec:
- args:
- - name: api-token
- valueFrom:
- secretKeyRef:
- name: token-secret
- key: apiToken
- metrics:
- - name: webmetric
- provider:
- web:
- headers:
- - key: Authorization
- value: "Bearer {{ args.api-token }}"
【編輯推薦】