使用 Argo Rollouts 實(shí)現(xiàn)漸進(jìn)式發(fā)布
支持如下特性:
- 藍(lán)綠更新策略
- 金絲雀更新策略
- 更加細(xì)粒度、加權(quán)流量拆分
- 自動(dòng)回滾
- 手動(dòng)判斷
- 可定制的指標(biāo)查詢和業(yè)務(wù) KPI 分析
- Ingress 控制器集成:NGINX,ALB
- 服務(wù)網(wǎng)格集成:Istio,Linkerd,SMI
- Metrics 指標(biāo)集成:Prometheus、Wavefront、Kayenta、Web、Kubernetes Jobs、Datadog、New Relic
實(shí)現(xiàn)原理
與 Deployment 對(duì)象類似,Argo Rollouts 控制器將管理 ReplicaSets 的創(chuàng)建、縮放和刪除,這些 ReplicaSet 由 Rollout 資源中的 spec.template 定義,使用與 Deployment 對(duì)象相同的 pod 模板。
當(dāng) spec.template? 變更時(shí),這會(huì)向 Argo Rollouts 控制器發(fā)出信號(hào),表示將引入新的 ReplicaSet,控制器將使用 spec.strategy? 字段內(nèi)的策略來(lái)確定從舊 ReplicaSet 到新 ReplicaSet 的 rollout 將如何進(jìn)行,一旦這個(gè)新的 ReplicaSet 被放大(可以選擇通過(guò)一個(gè) Analysis),控制器會(huì)將其標(biāo)記為穩(wěn)定。
如果在 spec.template? 從穩(wěn)定的 ReplicaSet 過(guò)渡到新的 ReplicaSet 的過(guò)程中發(fā)生了另一次變更(即在發(fā)布過(guò)程中更改了應(yīng)用程序版本),那么之前的新 ReplicaSet 將縮小,并且控制器將嘗試發(fā)布反映更新 spec.template 字段的 ReplicasSet。
相關(guān)概念
在繼續(xù)之前我們先來(lái)了解一些基本的概念。
Rollout(滾動(dòng))
Rollout 是一個(gè) Kubernetes 的 CRD 資源,相當(dāng)于 Kubernetes Deployment 對(duì)象,在需要更高級(jí)的部署或漸進(jìn)式交付功能的情況下,它旨在取代 Deployment 對(duì)象,Rollout 提供了 Kubernetes Deployment 所不能提供的功能。
- 藍(lán)綠部署
- 金絲雀部署
- 與 Ingress 控制器和服務(wù)網(wǎng)格整合,實(shí)現(xiàn)高級(jí)流量路由
- 與用于藍(lán)綠和金絲雀分析的指標(biāo)提供者集成
- 根據(jù)成功或失敗的指標(biāo),自動(dòng)發(fā)布或回滾
漸進(jìn)式交付
漸進(jìn)式交付是以受控和漸進(jìn)的方式發(fā)布產(chǎn)品更新的過(guò)程,從而降低發(fā)布的風(fēng)險(xiǎn),通常將自動(dòng)化和指標(biāo)分析結(jié)合起來(lái)以驅(qū)動(dòng)更新的自動(dòng)升級(jí)或回滾。
漸進(jìn)式交付通常被描述為持續(xù)交付的演變,將 CI/CD 中的速度優(yōu)勢(shì)擴(kuò)展到部署過(guò)程。通過(guò)將新版本限制在一部分用戶,觀察和分析正確的行為,然后逐漸增加更多的流量,同時(shí)不斷驗(yàn)證其正確性。
部署策略
雖然業(yè)界使用了一致的術(shù)語(yǔ)來(lái)描述各種部署策略,但這些策略的實(shí)現(xiàn)往往因工具而異,為了明確 Argo Rollouts 的行為方式,以下是 Argo Rollouts 提供的各種部署策略實(shí)施的描述。
- RollingUpdate(滾動(dòng)更新): 慢慢地用新版本替換舊版本,隨著新版本的出現(xiàn),舊版本會(huì)慢慢縮減,以保持應(yīng)用程序的總數(shù)量。這是 Deployment 對(duì)象的默認(rèn)策略。
- Recreate(重新創(chuàng)建): Recreate 會(huì)在啟動(dòng)新版本之前刪除舊版本的應(yīng)用程序,這可確保應(yīng)用程序的兩個(gè)版本永遠(yuǎn)不會(huì)同時(shí)運(yùn)行,但在部署期間會(huì)出現(xiàn)停機(jī)時(shí)間。
- Blue-Green(藍(lán)綠): 藍(lán)綠發(fā)布(有時(shí)稱為紅黑)指同時(shí)部署了新舊兩個(gè)版本的應(yīng)用程序,在此期間,只有舊版本的應(yīng)用程序會(huì)收到生產(chǎn)流量,這允許開(kāi)發(fā)人員在將實(shí)時(shí)流量切換到新版本之前針對(duì)新版本進(jìn)行測(cè)試。
- Canary(金絲雀): 金絲雀發(fā)布指將一部分用戶暴露在新版本的應(yīng)用程序中,而將其余流量提供給舊版本,一旦新版本被驗(yàn)證是正確的,新版本可以逐漸取代舊版本。Ingress 控制器和服務(wù)網(wǎng)格,如 NGINX Ingress 和 Istio,可以使金絲雀的流量拆分模式比原生的更復(fù)雜(例如,實(shí)現(xiàn)非常細(xì)粒度的流量分割,或基于 HTTP 頭的分割)。
上圖顯示了一個(gè)有兩個(gè)階段的金絲雀(10%和 33%的流量進(jìn)入新版本),通過(guò)使用 Argo Rollouts,我們可以根據(jù)實(shí)際的使用情況定義確切的階段數(shù)和流量百分比。
架構(gòu)
下面展示了由 Argo Rollouts 管理的 Deployment 的所有組件。
Rollout Controller
這是主控制器,用于監(jiān)視集群的事件并在 Rollout 類型的資源發(fā)生更改時(shí)做出反應(yīng)??刂破鲗⒆x取 rollout 的所有詳細(xì)信息,并使集群處于 rollout 定義中描述的相同狀態(tài)。
請(qǐng)注意,Argo Rollouts 不會(huì)篡改或響應(yīng)正常 Deployment 資源上發(fā)生的任何變更,這意味著你可以在一個(gè)使用其他方法部署應(yīng)用的集群中安裝 Argo Rollouts。
Rollout 資源
Rollout 資源是 Argo Rollouts 引入和管理的一種自定義 Kubernetes 資源,它與原生的 Kubernetes Deployment 資源基本兼容,但有額外的字段來(lái)控制更加高級(jí)的部署方法,如金絲雀和藍(lán)/綠部署。
Argo Rollouts 控制器將只對(duì) Rollout 資源中的變化做出反應(yīng),不會(huì)對(duì)正常的 Deployment 資源做任何事情,所以如果你想用 Argo Rollouts 管理你的 Deployment,你需要將你的 Deployment 遷移到 Rollouts。
舊版和新版的 ReplicaSets
這些是標(biāo)準(zhǔn)的 Kubernetes ReplicaSet 資源的實(shí)例,Argo Rollouts 給它們添加了一些額外的元數(shù)據(jù),以便跟蹤屬于應(yīng)用程序的不同版本。
還要注意的是,參加 Rollout 的 ReplicaSet 完全由控制器自動(dòng)管理,你不應(yīng)該用外部工具來(lái)篡改它們。
Ingress/Service
用戶的流量進(jìn)入集群后,被重定向到合適的版本,Argo Rollouts 使用標(biāo)準(zhǔn)的 Kubernetes Service 資源,但有一些額外的元數(shù)據(jù)。
Argo Rollouts 在網(wǎng)絡(luò)配置上非常靈活,首先,可以在 Rollout 期間使用不同的服務(wù),這些服務(wù)僅適用于新版本、僅適用于舊版本或兩者都適用。特別是對(duì)于 Canary 部署,Argo Rollouts 支持多種服務(wù)網(wǎng)格和 Ingress 解決方案,用于按特定百分比拆分流量,而不是基于 Pod 數(shù)量進(jìn)行簡(jiǎn)單的配置。
Analysis 與 AnalysisRun
Analysis 是一種自定義 Kubernetes 資源,它將 Rollout 連接到指標(biāo)提供程序,并為某些指標(biāo)定義特定閾值,這些閾值將決定 Rollout 是否成功。對(duì)于每個(gè) Analysis,你可以定義一個(gè)或多個(gè)指標(biāo)查詢及其預(yù)期結(jié)果,如果指標(biāo)查詢正常,則 Rollout 將繼續(xù)操作;如果指標(biāo)顯示失敗,則自動(dòng)回滾;如果指標(biāo)無(wú)法提供成功/失敗的答案,則暫停發(fā)布。
Analysis 只是關(guān)于要查詢哪些指標(biāo)的模板。附加到 Rollout 的實(shí)際結(jié)果是 AnalysisRun 自定義資源,你可以在特定 Rollout 上或在集群上全局定義 Analysis 以供多個(gè) Rollout 共享。
請(qǐng)注意,在 Rollout 中使用 Analysis 和指標(biāo)是完全可選的,你可以通過(guò) API 或 CLI 手動(dòng)暫停和促進(jìn)發(fā)布或使用其他外部方法。你不需要僅使用 Argo Rollouts 的 Metrics 解決方案,你還可以在 Rollout 中混合自動(dòng)(即基于 Analysis)和手動(dòng)步驟。
除了指標(biāo)之外,你還可以通過(guò)運(yùn)行 Kubernetes Job 或運(yùn)行 webhook 來(lái)決定發(fā)布的成功與否。
Metric Providers
Argo Rollouts 包括幾個(gè)流行的指標(biāo)提供者的原生集成,你可以在 Analysis 資源中使用,來(lái)自動(dòng)提升或回滾發(fā)布。
CLI 和 UI
還可以使用 Argo Rollouts CLI 或集成 UI 查看和管理 Rollout,兩者都是可選的。
安裝
直接使用下面的命令安裝 Argo Rollouts:
$ kubectl create namespace argo-rollouts
$ kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/download/v1.2.2/install.yaml
這里會(huì)創(chuàng)建一個(gè)名為 argo-rollouts 的命名空間,Argo Rollouts 控制器運(yùn)行在下面。
$ kubectl get pods -n argo-rollouts
NAME READY STATUS RESTARTS AGE
argo-rollouts-845b79ff9-crx9v 1/1 Running 0 58s
此外,我們還可以安裝一個(gè) kubectl 插件,對(duì)于命令行管理和可視化發(fā)布非常方便。使用 curl 安裝 Argo Rollouts kubectl 插件:
# https://github.91chi.fun/https://github.com//argoproj/argo-rollouts/releases/download/v1.2.2/kubectl-argo-rollouts-linux-amd64
$ curl -LO https://github.com/argoproj/argo-rollouts/releases/download/v1.2.2/kubectl-argo-rollouts-linux-amd64
然后賦予 kubectl-argo-rollouts 二進(jìn)制文件可執(zhí)行權(quán)限:
$ chmod +x ./kubectl-argo-rollouts-linux-amd64
將該二進(jìn)制文件移動(dòng)到你的 PATH 路徑下面去:
$ sudo mv ./kubectl-argo-rollouts-linux-amd64 /usr/local/bin/kubectl-argo-rollouts
執(zhí)行下面的命令來(lái)驗(yàn)證插件是否安裝成功:
$ kubectl argo rollouts version
kubectl-argo-rollouts: v1.2.2+22aff27
BuildDate: 2022-07-26T17:24:43Z
GitCommit: 22aff273bf95646e0cd02555fbe7d2da0f903316
GitTreeState: clean
GoVersion: go1.17.6
Compiler: gc
Platform: linux/amd64
使用
接下來(lái)我們通過(guò)幾個(gè)簡(jiǎn)單的示例來(lái)說(shuō)明 Rollout 的部署、升級(jí)、發(fā)布和中斷等操作,以此來(lái)展示 Rollouts 的各種功能。
1. 部署 Rollout
首先我們部署一個(gè) Rollout 資源和一個(gè)針對(duì)該資源的 Kubernetes Service 對(duì)象,這里我們示例中的 Rollout 采用了金絲雀的更新策略,將 20% 的流量發(fā)送到金絲雀上,然后手動(dòng)發(fā)布,最后在升級(jí)的剩余時(shí)間內(nèi)逐漸自動(dòng)增大流量,可以通過(guò)如下所示的 Rollout 來(lái)描述這個(gè)策略:
# basic-rollout.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: rollouts-demo
spec:
replicas: 5 # 定義5個(gè)副本
strategy: # 定義升級(jí)策略
canary: # 金絲雀發(fā)布
steps: # 發(fā)布的節(jié)奏
- setWeight: 20
- pause: {} # 會(huì)一直暫停
- setWeight: 40
- pause: { duration: 10 } # 暫停10s
- setWeight: 60
- pause: { duration: 10 }
- setWeight: 80
- pause: { duration: 10 }
revisionHistoryLimit: 2 # 下面部分其實(shí)是和 Deployment 兼容的
selector:
matchLabels:
app: rollouts-demo
template:
metadata:
labels:
app: rollouts-demo
spec:
containers:
- name: rollouts-demo
image: argoproj/rollouts-demo:blue
ports:
- name: http
containerPort: 8080
protocol: TCP
resources:
requests:
memory: 32Mi
cpu: 5m
還包括一個(gè)如下所示的 Service 資源對(duì)象:
# basic-service.yaml
apiVersion: v1
kind: Service
metadata:
name: rollouts-demo
spec:
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
selector:
app: rollouts-demo
直接創(chuàng)建上面的兩個(gè)資源對(duì)象:
$ kubectl apply -f basic-rollout.yaml
$ kubectl apply -f basic-service.yaml
任何 Rollout 的初始創(chuàng)建都會(huì)立即將副本擴(kuò)展到 100%(跳過(guò)任何金絲雀升級(jí)步驟、分析等...),因?yàn)檫€沒(méi)有發(fā)生升級(jí)。
$ kubectl get pods -l app=rollouts-demo
NAME READY STATUS RESTARTS AGE
rollouts-demo-687d76d795-6ppnh 1/1 Running 0 53s
rollouts-demo-687d76d795-8swrk 1/1 Running 0 53s
rollouts-demo-687d76d795-fnt2w 1/1 Running 0 53s
rollouts-demo-687d76d795-mtvtw 1/1 Running 0 53s
rollouts-demo-687d76d795-sh56l 1/1 Running 0 53s
Argo Rollouts 的 kubectl 插件允許我們可視化 Rollout 以及相關(guān)資源對(duì)象,并展示實(shí)時(shí)狀態(tài)變化,要在部署過(guò)程中觀察 Rollout,可以通過(guò)運(yùn)行插件的 get rollout --watch 命令,比如:
$ kubectl argo rollouts get rollout rollouts-demo --watch
2. 更新 Rollout
上面已經(jīng)部署完成,接下來(lái)就需要執(zhí)行更新了,和 Deployment 類似,對(duì) Pod 模板字段的任何變更都會(huì)導(dǎo)致新的版本(即 ReplicaSet)被部署,更新 Rollout 通常是修改容器鏡像的版本,然后執(zhí)行 kubectl apply? ,為了方便,rollouts 插件還單獨(dú)提供了一個(gè) set image? 的命令,比如這里我們運(yùn)行以下所示命令,用 yellow 版本的容器更新上面的 Rollout:
$ kubectl argo rollouts set image rollouts-demo \
rollouts-demo=argoproj/rollouts-demo:yellow
rollout "rollouts-demo" image updated
在 rollout 更新期間,控制器將通過(guò) Rollout 更新策略中定義的步驟進(jìn)行。這個(gè)示例的 rollout 為金絲雀設(shè)置了 20% 的流量權(quán)重,并一直暫停 rollout,直到用戶取消或促進(jìn)發(fā)布。在更新鏡像后,再次觀察 rollout,直到它達(dá)到暫停狀態(tài)。
$ kubectl argo rollouts get rollout rollouts-demo --watch
當(dāng) demo rollout 到達(dá)第二步時(shí),我們可以從插件中看到,Rollout 處于暫停狀態(tài),現(xiàn)在有 5 個(gè)副本中的 1 個(gè)運(yùn)行新版本的 pod,其余 4 個(gè)仍然運(yùn)行舊版本,這相當(dāng)于 setWeight: 20 步驟所定義的 20%的金絲雀權(quán)重。
3. Promote Rollout
經(jīng)過(guò)上面的更新后,Rollout 現(xiàn)在處于暫停狀態(tài),當(dāng)一個(gè) Rollout 到達(dá)一個(gè)沒(méi)有持續(xù)時(shí)間的暫停步驟時(shí),它將一直保持在暫停狀態(tài),直到它被恢復(fù)/提升。要手動(dòng)將 Rollout 切換到下一個(gè)步驟,請(qǐng)運(yùn)行插件的 promotion 命令。
$ kubectl argo rollouts promote rollouts-demo
rollout 'rollouts-demo' promoted
切換后 Rollout 將繼續(xù)執(zhí)行剩余的步驟。在我們的例子中,剩余的步驟是完全自動(dòng)化的,所以 Rollout 最終會(huì)完成步驟,直到它已經(jīng)完全過(guò)渡到新版本。再次觀察 Rollout,直到它完成所有步驟。
$ kubectl argo rollouts get rollout rollouts-demo --watch
promote 命令還支持用 --full 標(biāo)志跳過(guò)所有剩余步驟和分析。
可以看到 stable? 版本已經(jīng)切換到 revision:2? 這個(gè) ReplicaSet 了。在更新過(guò)程中,無(wú)論何時(shí),無(wú)論是通過(guò)失敗的金絲雀分析自動(dòng)中止,還是由用戶手動(dòng)中止,Rollout 都會(huì)退回到 stable 版本。
4. 中斷 Rollout
接下來(lái)我們來(lái)了解如何在更新過(guò)程中手動(dòng)中止 Rollout,首先,使用 set image? 命令部署一個(gè)新的 red 版本的容器,并等待 rollout 再次達(dá)到暫停的步驟。
$ kubectl argo rollouts set image rollouts-demo \
rollouts-demo=argoproj/rollouts-demo:red
rollout "rollouts-demo" image updated
這一次我們將中止更新,而不是將滾動(dòng)切換到下一步,這樣它就回到了 stable? 版本,該插件同樣提供了一個(gè) abort 命令,可以在更新過(guò)程中的任何時(shí)候手動(dòng)中止 Rollout。
$ kubectl argo rollouts abort rollouts-demo
當(dāng)中止?jié)L動(dòng)時(shí),它將擴(kuò)大 ReplicaSet 的 stable? 版本(在本例中是 yellow? 版本),并縮小任何其他版本。盡管 ReplicaSet 的穩(wěn)定版本可能正在運(yùn)行,并且是健康的,但整個(gè) Rollout 仍然被認(rèn)為是退化的,因?yàn)槠谕陌姹荆╮ed 版本)不是實(shí)際運(yùn)行的版本。
為了使 Rollout 再次被認(rèn)為是健康的而不是有問(wèn)題的版本,有必要將所需的狀態(tài)改回以前的穩(wěn)定版本。在我們的例子中,我們可以簡(jiǎn)單地使用之前的 yellow? 鏡像重新運(yùn)行 set image 命令即可。
$ kubectl argo rollouts set image rollouts-demo \
rollouts-demo=argoproj/rollouts-demo:yellow
運(yùn)行這個(gè)命令后,可以看到 Rollout 立即變成了 health 狀態(tài),而且沒(méi)有任何關(guān)于創(chuàng)建新 ReplicaSets 的動(dòng)態(tài)。
當(dāng) Rollout 還沒(méi)有達(dá)到預(yù)期狀態(tài)(例如,它被中止了,或者正在更新中),而穩(wěn)定版本的資源清單被重新應(yīng)用,Rollout 檢測(cè)到這是一個(gè)回滾,而不是一個(gè)更新,并將通過(guò)跳過(guò)分析和步驟快速部署穩(wěn)定的 ReplicaSet。
上面例子中的 Rollout 沒(méi)有使用 Ingress 控制器或服務(wù)網(wǎng)格來(lái)控制流量。相反,它使用正常的 Kubernetes Service 來(lái)實(shí)現(xiàn)近似的金絲雀權(quán)重,基于新舊副本數(shù)量的比例來(lái)實(shí)現(xiàn)。所以,這個(gè) Rollout 有一個(gè)限制,即它只能實(shí)現(xiàn) 20% 的最小加權(quán),通過(guò)擴(kuò)展 5 個(gè) pod 中的一個(gè)來(lái)運(yùn)行新版本。為了實(shí)現(xiàn)更細(xì)粒度的金絲雀,這就需要一個(gè) Ingress 控制器或服務(wù)網(wǎng)格了。
Dashboard
Argo Rollouts Kubectl 插件可以提供一個(gè)本地 Dashboard,來(lái)可視化你的 Rollouts。
要啟動(dòng)這個(gè) Dashboard,需要在包含 Rollouts 資源對(duì)象的命名空間中運(yùn)行 kubectl argo rollouts dashboard? 命令,然后訪問(wèn)localhost:3100 即可。
點(diǎn)擊 Rollout 可以進(jìn)行詳細(xì)頁(yè)面,在詳細(xì)頁(yè)面可以看到 Rollout 的配置信息,還可以直接在 UI 界面上執(zhí)行一些常用的操作,比如重啟、重啟、中斷等。
Analysis 和漸進(jìn)式交互
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 成功率,它沒(méi)有結(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 # 延遲開(kāi)始分析,到第3步開(kāi)始
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ù)推出,否則中止。
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 分鐘后,開(kāi)始執(zhí)行 success-rate 這個(gè)分析模板。
這里 AnalysisTemplate 與上面的后臺(tái)分析例子相同,但由于沒(mé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.<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}"
在創(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)沒(méi)有定義成功或失敗的條件時(shí),分析運(yùn)行可能成為無(wú)結(jié)果的一個(gè)例子。
metrics:
- name: my-query
provider:
prometheus:
address: http://prometheus.example.com:9090
query: ...
此外當(dāng)同時(shí)指定了成功和失敗的條件,但測(cè)量值沒(méi)有滿足任何一個(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)行不需要立即開(kāi)始(即給指標(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: ...
延遲開(kāi)始后臺(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 }
此外 OpenKurise 項(xiàng)目最近也推出了類似的漸進(jìn)式發(fā)布工具 Kruise Rollouts,感興趣的可以前往 https://github.com/openkruise/rollouts 了解更多相關(guān)信息。