Kruise Rollout: 讓所有應用負載都能使用漸進式交付

阿里巴巴雲原生發表於2022-04-26

作者:趙明山(立衡)

前言

OpenKruise [1]  是阿里雲開源的雲原生應用自動化管理套件,也是當前託管在 Cloud Native Computing Foundation (CNCF) 下的 Sandbox 專案。它來自阿里巴巴多年來容器化、雲原生的技術沉澱,是阿里內部生產環境大規模應用的基於 Kubernetes 之上的標準擴充套件元件,也是緊貼上游社群標準、適應網際網路規模化場景的技術理念與最佳實踐。在原有的工作負載、sidecar 管理等領域外,Kruise 目前正在漸進式交付領域進行嘗試。

什麼是漸進式交付?

“漸進式交付” 一詞最早起源於大型、複雜的工業化專案,它試圖將複雜的專案進行分階段拆解,通過持續進行小型閉環迭代降低交付成本和時間。隨著 Kubernetes 及雲原生理念被普及之後,尤其是在持續部署流水線出現後,漸進式交付為網際網路應用提供了基礎設施和實現方法。

在產品的迭代過程中,可以將漸進式交付的具體行為附著在流水線中,將整條交付流水線看作產品迭代的一個過程和一次漸進式交付週期。漸進式交付在實踐中是以 A/B 測試、金絲雀/灰度釋出等技術手段落地的。以 淘寶商品推薦 為例,其每次釋出重大功能,都會經歷一次典型的漸進式交付過程,從而通過漸進式交付提高交付的穩定性和效率

 title=

為什麼要 Kruise Rollout

Kubernetes 只提供了應用交付的 Deployment 控制器,以及針對流量的 Ingress、Service 抽象,但是如何將上述實現組合成開箱即用的漸進式交付方案,Kubernetes 並沒有出標準的定義。Argo-rollout 與 Flagger 是社群目前比較流行的漸進式交付方案,但是它們在一些能力和理念上跟我們的設想不太一樣。首先,它們僅支援 Deployment, 不支援 Statefulset、Daemonset,更不用說自定義的 operator 了;其次,它們不是“非侵入式的漸進式釋出方式” ,例如:Argo-rollout 不能支援社群 K8S Native Deployment、Flagger 對業務建立的 Deployment 進行了拷貝導致 Name 改變而與 Gitops 或自建 Paas 存在一些相容性的問題。

另外,百花齊放是雲原生的一大特點。阿里雲容器團隊負責整個容器平臺雲原生架構的演進,在應用漸進式交付領域也有強烈的需求,因此在參考社群方案以及考慮阿里內部場景的基礎上,我們在設計 Rollout 過程中有以下幾個目標:

  1. 無侵入性:對原生的 Workload 控制器以及使用者定義的 Application Yaml 定義不進行任何修改,保證原生資源的乾淨、一致
  2. 可擴充套件性:通過可擴充套件的方式,支援 K8S Native Workload、自定義 Workload 以及 Nginx、Isito 等多種 Traffic 排程方式
  3. 易用性:對使用者而言開箱即用,能夠非常方便的與社群 Gitops 或自建 Paas 結合使用

Kruise Rollout:旁路的漸進式交付能力

Kruise Rollout [2] 是 Kruise 針對漸進式交付抽象的定義模型,完整的 Rollout 定義:滿足配合應用流量和實際部署例項的金絲雀釋出、藍綠髮布、A/B Testing 釋出,以及釋出過程能夠基於 Prometheus Metrics 指標自動化分批與暫停,並能提供旁路的無感對接、相容已有的多種工作負載(Deployment、CloneSet、DaemonSet),架構如下:

 title=

流量排程(金絲雀、A/B Test、藍綠)與分批發布

金絲雀與分批發布是漸進式交付實踐中最常用的釋出方式,如下所示:

  1. workloadRef 旁路式的選擇需要 Rollout 的 Workload(Deployment、CloneSet、DaemonSet)。
  2. canary.Steps 定義了整個 Rollout 過程一共分為五批,其中第一批只灰度一個新版本 Pod,並且 routing 5% 流量到新版本 Pod,並且需要人工確認是否繼續釋出。
  3. 第二批發布 40% 新版本 Pod,以及 Routing 40% 流量到新版本 Pod,且釋出完成後 sleep 10m,自動釋出後面批次。
  4. trafficRoutings 定義了業務 Ingress 控制器為 Nginx,此處設計為可擴充套件實現,除 Nginx 之外還可以支援 Istio、Alb 等其它流量控制器。
apiVersion: rollouts.kruise.io/v1alpha1
kind: Rollout
spec:
  strategy:
    objectRef:
      workloadRef:
        apiVersion: apps/v1
        # Deployment, CloneSet, AdDaemonSet etc.
        kind: Deployment 
        name: echoserver
    canary:
      steps:
        # routing 5% traffics to the new version
      - weight: 5
        # Manual confirmation, release the back steps
        pause: {}
        # optional, The first step of released replicas. If not set, the default is to use 'weight', as shown above is 5%.
        replicas: 1
      - weight: 40
        # sleep 600s, release the back steps
        pause: {duration: 600}
      - weight: 60
        pause: {duration: 600}
      - weight: 80
        pause: {duration: 600}
        # 最後一批無需配置
      trafficRoutings:
        # echoserver service name
      - service: echoserver
        # nginx ingress
        type: nginx
        # echoserver ingress name
        ingress:
          name: echoserver

基於 Metrics 指標自動化分批與暫停

Rollout 過程中能夠自動的分析業務 Prometheus Metrics 指標,然後與 steps 結合起來,來決定 Rollout 是否需要繼續或者暫停。如下所示,在釋出完每個批次之後分析過去五分鐘業務的 http 狀態碼,如果 http 200 的比例小於 99.5 將暫停此 Rollout 過程。

apiVersion: rollouts.kruise.io/v1alpha1
kind: Rollout
spec:
  strategy:
    objectRef:
      ...
    canary:
      steps:
      - weight: 5
        ...
      # metrics分析  
      analysis:
        templates:
        - templateName: success-rate
          startingStep: 2 # delay starting analysis run until setWeight: 40%
          args:
          - name: service-name
            value: guestbook-svc.default.svc.cluster.local

# metrics analysis模版
apiVersion: rollouts.kruise.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]
          ))

金絲雀釋出實踐

  1. 假設使用者有基於 Kubernetes 部署 echoServer 服務如下,並且通過 nginx ingress 對外服務:

 title=

  1. 定義 Kruise Rollout 金絲雀釋出(1 個新版本 Pod,以及 5% 流量),並 apply -f 到 Kubernetes 叢集
apiVersion: rollouts.kruise.io/v1alpha1
kind: Rollout
metadata:
  name: rollouts-demo
spec:
  objectRef:
    ...
  strategy:
    canary:
      steps:
      - weight: 5
        pause: {}
        replicas: 1
      trafficRoutings:
        ...
  1. 升級 echoserver 映象版本(Version 1.10.2 -> 1.10.3),並 kubectl -f 到 Kuberernetes 叢集中
apiVersion: apps/v1
kind: Deployment
metadata:
  name: echoserver
...
spec:
  ...
  containers:
  - name: echoserver
    image: cilium/echoserver:1.10.3

Kruise Rollout 監聽到上述行為後,將會自動開始金絲雀釋出過程。如下所示,自動生成 canary Deployment、service 以及 Ingress,並且配置 5% 流量到新版本 Pods:

 title=

  1. 金絲雀一段時間後,業務研發同學確認新版本無異常後,可以通過命令 kubectl-kruise rollout approve rollout/rollouts-demo -n default 釋出剩餘所有的 Pods。Rollout 會精確的控制後續過程,當釋出完成後,會回收所有的 canary 資源,恢復到使用者部署的狀態。

 title=

  1. 如果在金絲雀過程中發現新版本異常,可以將業務映象調整為之前版本(1.10.2),然後 kubectl apply -f 到 Kubernetes 叢集當中。Kruise Rollout 監聽到該行為,會回收所有的 canary 資源,達到快速回滾的效果。
apiVersion: apps/v1
kind: Deployment
metadata:
  name: echoserver
...
spec:
  ...
  containers:
  - name: echoserver
    image: cilium/echoserver:1.10.2

 title=

總結

隨著 Kubernetes 上面部署的應用日益增多,如何做到業務快速迭代與應用穩定性之間的平衡,是平臺建設方必須要解決的問題。Kruise Rollout 是 OpenKruise 在漸進式交付領域的新探索,旨在解決應用交付領域的流量排程以及分批部署問題。Kruise Rollout 目前已經正式釋出 v0.1.0 版本,並且與社群 OAM KubeVela 專案進行了整合,vela 使用者可以通過 Addons 快速部署與使用 Rollout 能力。此外,也希望社群使用者能夠加入進來,我們一起在應用交付領域做更多的擴充套件。

 title=

相關連結

[1] OpenKruise:

https://github.com/openkruise/kruise

[2] Kruise Rollout:

https://github.com/openkruise/rollouts/blob/master/docs/getting_started/introduction.md

??戳此處,檢視 OpenKruise 專案官方主頁與文件!

相關文章