Argo Rollouts 基於 Analysis 的漸進式發佈

前面我們介紹了使用手動的方式來控制 Argo Rollouts 進行應用交付,此外我們還可以利用 Argo Rollouts 提供的分析 (Analysis) 來執行自動交付。Argo Rollouts 提供了幾種執行分析 (Analysis) 的方法來推動漸進式交付,首先需要了解幾個 CRD 資源:

後臺分析

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

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

引用 Secret

AnalysisTemplate 和 AnalysisRun 可以在 .spec.args 中引用 Secret 對象,這允許用戶安全地將認證信息傳遞給指標提供方,如登錄憑證或 API 令牌。

需要注意一個 AnalysisRun 只能引用它所運行的同一命名空間的 Secret。

如下所示的例子中,一個 AnalysisTemplate 引用了一個 API 令牌,並把它傳遞給一個 Web 指標提供者。

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 }}"
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/13HOX1j0DcjdH8v8-IVUKA