使用 Argo Rollouts 實現漸進式發佈

Argo Rollouts 是一個 Kubernetes Operator 實現,它爲 Kubernetes 提供更加高級的部署能力,如藍綠、金絲雀、金絲雀分析、實驗和漸進式交付功能,爲雲原生應用和服務實現自動化、基於 GitOps 的逐步交付。

支持如下特性:

實現原理

與 Deployment 對象類似,Argo Rollouts 控制器將管理 ReplicaSets 的創建、縮放和刪除,這些 ReplicaSet 由 Rollout 資源中的 spec.template 定義,使用與 Deployment 對象相同的 pod 模板。

spec.template 變更時,這會向 Argo Rollouts 控制器發出信號,表示將引入新的 ReplicaSet,控制器將使用 spec.strategy 字段內的策略來確定從舊 ReplicaSet 到新 ReplicaSet 的 rollout 將如何進行,一旦這個新的 ReplicaSet 被放大(可以選擇通過一個 Analysis),控制器會將其標記爲穩定

如果在 spec.template 從穩定的 ReplicaSet 過渡到新的 ReplicaSet 的過程中發生了另一次變更(即在發佈過程中更改了應用程序版本),那麼之前的新 ReplicaSet 將縮小,並且控制器將嘗試發佈反映更新 spec.template 字段的 ReplicasSet。

相關概念

在繼續之前我們先來了解一些基本的概念。

Rollout(滾動)

Rollout 是一個 Kubernetes 的 CRD 資源,相當於 Kubernetes Deployment 對象,在需要更高級的部署或漸進式交付功能的情況下,它旨在取代 Deployment 對象,Rollout 提供了 Kubernetes Deployment 所不能提供的功能。

漸進式交付

漸進式交付是以受控和漸進的方式發佈產品更新的過程,從而降低發佈的風險,通常將自動化和指標分析結合起來以驅動更新的自動升級或回滾。

漸進式交付通常被描述爲持續交付的演變,將 CI/CD 中的速度優勢擴展到部署過程。通過將新版本限制在一部分用戶,觀察和分析正確的行爲,然後逐漸增加更多的流量,同時不斷驗證其正確性。

部署策略

雖然業界使用了一致的術語來描述各種部署策略,但這些策略的實現往往因工具而異,爲了明確 Argo Rollouts 的行爲方式,以下是 Argo Rollouts 提供的各種部署策略實施的描述。

上圖顯示了一個有兩個階段的金絲雀(10% 和 33% 的流量進入新版本),通過使用 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。

舊版和新版的 ReplicaSets

這些是標準的 Kubernetes ReplicaSet 資源的實例,Argo Rollouts 給它們添加了一些額外的元數據,以便跟蹤屬於應用程序的不同版本。

還要注意的是,參加 Rollout 的 ReplicaSet 完全由控制器自動管理,你不應該用外部工具來篡改它們。

Ingress/Service

用戶的流量進入集羣后,被重定向到合適的版本,Argo Rollouts 使用標準的 Kubernetes Service 資源,但有一些額外的元數據。

Argo Rollouts 在網絡配置上非常靈活,首先,可以在 Rollout 期間使用不同的服務,這些服務僅適用於新版本、僅適用於舊版本或兩者都適用。特別是對於 Canary 部署,Argo Rollouts 支持多種服務網格和 Ingress 解決方案,用於按特定百分比拆分流量,而不是基於 Pod 數量進行簡單的配置。

Analysis 與 AnalysisRun

Analysis 是一種自定義 Kubernetes 資源,它將 Rollout 連接到指標提供程序,併爲某些指標定義特定閾值,這些閾值將決定 Rollout 是否成功。對於每個 Analysis,你可以定義一個或多個指標查詢及其預期結果,如果指標查詢正常,則 Rollout 將繼續操作;如果指標顯示失敗,則自動回滾;如果指標無法提供成功 / 失敗的答案,則暫停發佈。

Analysis 只是關於要查詢哪些指標的模板。附加到 Rollout 的實際結果是 AnalysisRun 自定義資源,你可以在特定 Rollout 上或在集羣上全局定義 Analysis 以供多個 Rollout 共享。

請注意,在 Rollout 中使用 Analysis 和指標是完全可選的,你可以通過 API 或 CLI 手動暫停和促進發布或使用其他外部方法。你不需要僅使用 Argo Rollouts 的 Metrics 解決方案,你還可以在 Rollout 中混合自動(即基於 Analysis)和手動步驟。

除了指標之外,你還可以通過運行 Kubernetes Job 或運行 webhook 來決定發佈的成功與否。

Metric Providers

Argo Rollouts 包括幾個流行的指標提供者的原生集成,你可以在 Analysis 資源中使用,來自動提升或回滾發佈。

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

這裏會創建一個名爲 argo-rollouts 的命名空間,Argo Rollouts 控制器運行在下面。

$ kubectl get pods -n argo-rollouts
NAME                             READY   STATUS    RESTARTS   AGE
argo-rollouts-845b79ff9-crx9v    1/1     Running   0          58s

此外,我們還可以安裝一個 kubectl 插件,對於命令行管理和可視化發佈非常方便。使用 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 二進制文件可執行權限:

$ 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.2.2+22aff27
  BuildDate: 2022-07-26T17:24:43Z
  GitCommit: 22aff273bf95646e0cd02555fbe7d2da0f903316
  GitTreeState: clean
  GoVersion: go1.17.6
  Compiler: gc
  Platform: linux/amd64

使用

接下來我們通過幾個簡單的示例來說明 Rollout 的部署、升級、發佈和中斷等操作,以此來展示 Rollouts 的各種功能。

1. 部署 Rollout

首先我們部署一個 Rollout 資源和一個針對該資源的 Kubernetes Service 對象,這裏我們示例中的 Rollout 採用了金絲雀的更新策略,將 20% 的流量發送到金絲雀上,然後手動發佈,最後在升級的剩餘時間內逐漸自動增大流量,可以通過如下所示的 Rollout 來描述這個策略:

# basic-rollout.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: rollouts-demo
spec:
  replicas: 5 # 定義5個副本
  strategy: # 定義升級策略
    canary: # 金絲雀發佈
      steps: # 發佈的節奏
        - setWeight: 20
        - pause: {} # 會一直暫停
        - setWeight: 40
        - pause: { duration: 10 } # 暫停10s
        - setWeight: 60
        - pause: { duration: 10 }
        - setWeight: 80
        - pause: { duration: 10 }
  revisionHistoryLimit: 2 # 下面部分其實是和 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

還包括一個如下所示的 Service 資源對象:

# 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

直接創建上面的兩個資源對象:

$ kubectl apply -f basic-rollout.yaml
$ kubectl apply -f basic-service.yaml

任何 Rollout 的初始創建都會立即將副本擴展到 100%(跳過任何金絲雀升級步驟、分析等...),因爲還沒有發生升級。

$ 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 以及相關資源對象,並展示實時狀態變化,要在部署過程中觀察 Rollout,可以通過運行插件的 get rollout --watch 命令,比如:

$ kubectl argo rollouts get rollout rollouts-demo --watch

2. 更新 Rollout

上面已經部署完成,接下來就需要執行更新了,和 Deployment 類似,對 Pod 模板字段的任何變更都會導致新的版本(即 ReplicaSet)被部署,更新 Rollout 通常是修改容器鏡像的版本,然後執行 kubectl apply ,爲了方便,rollouts 插件還單獨提供了一個 set image 的命令,比如這裏我們運行以下所示命令,用 yellow 版本的容器更新上面的 Rollout:

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

在 rollout 更新期間,控制器將通過 Rollout 更新策略中定義的步驟進行。這個示例的 rollout 爲金絲雀設置了 20% 的流量權重,並一直暫停 rollout,直到用戶取消或促進發布。在更新鏡像後,再次觀察 rollout,直到它達到暫停狀態。

$ kubectl argo rollouts get rollout rollouts-demo --watch

當 demo rollout 到達第二步時,我們可以從插件中看到,Rollout 處於暫停狀態,現在有 5 個副本中的 1 個運行新版本的 pod,其餘 4 個仍然運行舊版本,這相當於 setWeight: 20 步驟所定義的 20% 的金絲雀權重。

3. Promote Rollout

經過上面的更新後,Rollout 現在處於暫停狀態,當一個 Rollout 到達一個沒有持續時間的暫停步驟時,它將一直保持在暫停狀態,直到它被恢復 / 提升。要手動將 Rollout 切換到下一個步驟,請運行插件的 promotion 命令。

$ kubectl argo rollouts promote rollouts-demo
rollout 'rollouts-demo' promoted

切換後 Rollout 將繼續執行剩餘的步驟。在我們的例子中,剩餘的步驟是完全自動化的,所以 Rollout 最終會完成步驟,直到它已經完全過渡到新版本。再次觀察 Rollout,直到它完成所有步驟。

$ kubectl argo rollouts get rollout rollouts-demo --watch

promote 命令還支持用 --full 標誌跳過所有剩餘步驟和分析。

可以看到 stable 版本已經切換到 revision:2 這個 ReplicaSet 了。在更新過程中,無論何時,無論是通過失敗的金絲雀分析自動中止,還是由用戶手動中止,Rollout 都會退回到 stable 版本。

4. 中斷 Rollout

接下來我們來了解如何在更新過程中手動中止 Rollout,首先,使用 set image 命令部署一個新的 red 版本的容器,並等待 rollout 再次達到暫停的步驟。

$ kubectl argo rollouts set image rollouts-demo \
  rollouts-demo=argoproj/rollouts-demo:red
rollout "rollouts-demo" image updated

這一次我們將中止更新,而不是將滾動切換到下一步,這樣它就回到了 stable 版本,該插件同樣提供了一個 abort 命令,可以在更新過程中的任何時候手動中止 Rollout。

$ kubectl argo rollouts abort rollouts-demo

當中止滾動時,它將擴大 ReplicaSet 的 stable 版本(在本例中是 yellow 版本),並縮小任何其他版本。儘管 ReplicaSet 的穩定版本可能正在運行,並且是健康的,但整個 Rollout 仍然被認爲是退化的,因爲期望的版本(red 版本)不是實際運行的版本。

爲了使 Rollout 再次被認爲是健康的而不是有問題的版本,有必要將所需的狀態改回以前的穩定版本。在我們的例子中,我們可以簡單地使用之前的 yellow 鏡像重新運行 set image 命令即可。

$ kubectl argo rollouts set image rollouts-demo \
  rollouts-demo=argoproj/rollouts-demo:yellow

運行這個命令後,可以看到 Rollout 立即變成了 health 狀態,而且沒有任何關於創建新 ReplicaSets 的動態。

當 Rollout 還沒有達到預期狀態(例如,它被中止了,或者正在更新中),而穩定版本的資源清單被重新應用,Rollout 檢測到這是一個回滾,而不是一個更新,並將通過跳過分析和步驟快速部署穩定的 ReplicaSet。

上面例子中的 Rollout 沒有使用 Ingress 控制器或服務網格來控制流量。相反,它使用正常的 Kubernetes Service 來實現近似的金絲雀權重,基於新舊副本數量的比例來實現。所以,這個 Rollout 有一個限制,即它只能實現 20% 的最小加權,通過擴展 5 個 pod 中的一個來運行新版本。爲了實現更細粒度的金絲雀,這就需要一個 Ingress 控制器或服務網格了。

Dashboard

Argo Rollouts Kubectl 插件可以提供一個本地 Dashboard,來可視化你的 Rollouts。

要啓動這個 Dashboard,需要在包含 Rollouts 資源對象的命名空間中運行 kubectl argo rollouts dashboard 命令,然後訪問localhost:3100 即可。

點擊 Rollout 可以進行詳細頁面,在詳細頁面可以看到 Rollout 的配置信息,還可以直接在 UI 界面上執行一些常用的操作,比如重啓、重啓、中斷等。

Analysis 和漸進式交互

Argo Rollouts 提供了幾種執行分析 (Analysis) 的方法來推動漸進式交付,首先需要了解幾個 CRD 資源:

後臺分析

金絲雀正在執行其部署步驟時,分析可以在後臺運行。

以下示例是每 10 分鐘逐漸將 Canary 權重增加 20%,直到達到 100%。在後臺,基於名爲 success-rateAnalysisTemplate 啓動 AnalysisRunsuccess-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-rateerror-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 }

此外 OpenKurise 項目最近也推出了類似的漸進式發佈工具 Kruise Rollouts,感興趣的可以前往 https://github.com/openkruise/rollouts 瞭解更多相關信息。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/BP9MfGAgPiOYx_EpNOWqjQ