OpenKruise v0.8.0 核心能力解讀:管理 Sidecar 容器的利器

頭圖.png

做者 | 趙明山(立衡)
來源 | 阿里巴巴雲原生公衆號nginx

前言

OpenKruise 是阿里雲開源的雲原生應用自動化管理套件,也是當前託管在 Cloud Native Computing Foundation (CNCF) 下的 Sandbox 項目。它來自阿里巴巴多年來容器化、雲原生的技術沉澱,是阿里內部生產環境大規模應用的基於 Kubernetes 之上的標準擴展組件,也是緊貼上游社區標準、適應互聯網規模化場景的技術理念與最佳實踐。git

OpenKruise 在 2021 年 3 月 4 日發佈了最新的 v0.8.0版本(ChangeLog),其中加強了 SidecarSet 的能力,特別是對日誌管理類 Sidecar 有了更加完善的支持。github

背景

Sidecar 是雲原生中一種很是重要的容器設計模式,它將輔助能力從主容器中剝離出來成爲單獨的 sidecar 容器。在微服務架構中,一般也使用 sidecar 模式將微服務中的配置管理、服務發現、路由、熔斷等通用能力從主程序中剝離出來,從而極大下降了微服務架構中的複雜性。隨着 Service Mesh 的逐步風靡,sidecar 模式也日益深刻人心,在阿里巴巴集團內部也大量使用 sidecar 模式來管理諸如運維、安全、消息中間件等通用組件。web

在 Kubernetes 集羣中,Pod 不只能夠實現主容器與 sidecar 容器的構建,同時提供了許多功能強大的 workload(例如:deployment、statefulset)來對 Pod 進行管理、升級。可是隨着 Kubernetes 集羣上的業務日益增多,sidecar 容器的種類與規模也隨之日益龐大,對線上 sidecar 容器的管理和升級成爲了愈發繁雜的工做:docker

  • 業務 Pod 裏面包含了運維、安全、代理等多個 sidecar 容器,業務線同窗不只要完成自身主容器的配置,並且還須要熟悉這些 sidecar 容器的配置,這不只增長了業務同窗的工做量,同時也無形增長了 sidecar 容器配置的風險。後端

  • sidecar 容器的升級須要連同業務主容器一塊兒重啓(deployment、statefulset 等 workload 基於 Pod 銷燬、重建的模式,來實現 Pod 的滾動升級),推進和升級支撐着線上數百款業務的 sidecar 容器,必然存在着極大的業務阻力。設計模式

  • 做爲 sidecar 容器的提供者對線上諸多各類配置以及版本的 sidecar 容器沒有直接有效的升級手段,這對 sidecar 容器的管理意味着極大的潛在風險。

阿里巴巴集團內部擁有着百萬級的容器數量連同上面承載的上千個業務,所以,sidecar 容器的管理與升級也就成爲了亟待完善的主題。所以,咱們總結了內部許多 sidecar 容器的通用化需求,並將其沉澱到 OpenKruise 上面,最終抽象爲 SidecarSet 做爲管理和升級種類繁多 sidecar 容器的利器。api

OpenKruise SidecarSet

SidecarSet 是 OpenKruise 中針對 sidecar 抽象出來的概念,負責注入和升級 Kubernetes 集羣中的 sidecar 容器,是 OpenKruise 的核心 workload 之一。它提供了很是豐富的功能,用戶使用 SidecarSet 能夠很是方便實現 sidecar 容器的管理。主要特性以下:安全

  • 配置單獨管理:爲每個 sidecar 容器配置單獨的 SidecarSet 配置,方便管理。架構

  • 自動注入:在新建、擴容、重建 pod 的場景中,實現 sidecar 容器的自動注入。

  • 原地升級:支持不重建 pod 的方式完成 sidecar 容器的原地升級,不影響業務主容器,幷包含豐富的灰度發佈策略

注意:針對 Pod 中包含多個容器的模式,其中對外提供主要業務邏輯能力的容器稱之爲 主容器,其它一些如日誌採集、安全、代理等輔助能力的容器稱之爲 Sidecar 容器。例如:一個對外提供 web 能力的 pod,nginx 容器提供主要的 web server 能力即爲主容器,logtail 容器負責採集、上報 nginx 日誌即爲 Sidecar 容器。本文中的 SidecarSet 資源抽象也是爲解決 Sidecar 容器的一些問題。

1. Sidecar logging architectures

應用日誌可讓你瞭解應用內部的運行情況,日誌對調試問題和監控集羣活動很是有用。應用容器化後,最簡單且最普遍採用的日誌記錄方式就是寫入標準輸出和標準錯誤。

可是,在當前分佈式系統、大規模集羣的時代下,上述方案還不足以達到生產環境的標準。首先,對於分佈式系統而言,日誌都是分散在單個容器裏面,沒有一個統一彙總的地方。其次,若是發生容器崩潰、Pod 被驅逐等場景,會出現日誌丟失的狀況。所以,須要一種更加可靠,獨立於容器生命週期的日誌解決方案。

Sidecar logging architectures 是將 logging agent 放到一個獨立的 sidecar 容器中,經過共享日誌目錄的方式,實現容器日誌的採集,而後存儲到日誌平臺的後端存儲。
                               
1.png

阿里巴巴以及螞蟻集團內部一樣也是基於這種架構實現了容器的日誌採集,下面我將介 紹OpenKruise SidecarSet 如何助力 Sidecar 日誌架構在 Kubernetes 集羣中的大規模落地實踐

2. 自動注入

OpenKruise SidecarSet 基於 Kubernetes AdmissionWebhook 機制實現了 sidecar 容器的自動注入,所以只要將 sidecar 配置到 SidecarSet 中,無論用戶用 CloneSet、Deployment、StatefulSet 等任何方式部署,擴出來的 Pod 中都會注入定義好的 sidecar 容器。 

2.png

Sidecar 容器的全部者只須要配置自身的 SidecarSet,就能夠在業務無感知的狀況下完成 sidecar 容器的注入,這種方式極大的下降了 sidecar 容器使用的門檻,也方便了 sidecar 全部者的管理工做。爲了知足 sidecar 注入的多種場景,SidecarSet 除 containers 以外還擴展了以下字段:

# sidecarset.yaml
apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: test-sidecarset
spec:
  # 經過selector選擇pod
  selector:
    matchLabels:
      app: web-server
  # 指定 namespace 生效    
  namespace: ns-1    
  # container definition
  containers:
  - name: logtail
    image: logtail:1.0.0
    # 共享指定卷
    volumeMounts:
    - name: web-log
      mountPath: /var/log/web 
    # 共享全部卷
    shareVolumePolicy: disabled
    # 環境變量共享
    transferEnv:
    - sourceContainerName: web-server
      # TZ表明時區,例如:web-server容器中存在環境變量 TZ=Asia/Shanghai
        envName: TZ 
  volumes:
  - name: web-log
    emptyDir: {}
  • Pod 選擇器

    • 支持 selector 來選擇要注入的 Pod,如示例中將選擇 labels[app] = web-server 的 pod,將 logtail 容器注入進去,也能夠在全部的 pod 中添加一個 labels[inject/logtail] = true 的方式,來實現全局性的 sidecar 注入。
    • namespace:sidecarSet 默認是全局生效的,若是隻想對某一個 namespace 生效,則配置該參數。
  • 數據卷共享

    • 共享指定卷:經過 volumeMounts 和 volumes 能夠完成與主容器的特定卷的共享,如示例中經過共享 web-log volume 來達到日誌採集的效果。
    • 共享全部卷:經過 shareVolumePolicy = enabled | disabled 來控制是否掛載 pod 主容器的全部卷卷,經常使用於日誌收集等 sidecar,配置爲 enabled 後會把應用容器中全部掛載點注入 sidecar 同一路經下(sidecar 中自己就有聲明的數據卷和掛載點除外)。
  • 環境變量共享:能夠經過 transferEnv 從其它容器中獲取環境變量,會把名爲 sourceContainerName 容器中名爲 envName 的環境變量拷貝到本 sidecar 容器,如示例中日誌 sidecar 容器共享了主容器的時區 TZ,這在海外環境中尤爲常見。

注意:Kubernetes 社區對於已經建立的 Pod 不容許修改 container 數量,因此上述注入能力只能發生在 Pod 建立階段,對於已經建立的 Pod 須要經過重建的方式來注入。

3. 原地升級

SidecarSet 不只實現 sidecar 容器的注入,並且複用了 OpenKruise 中原地升級的特性,實現了在不重啓 Pod 和主容器的前提下單獨升級 sidecar 容器的能力。因爲這種升級方式基本上能作到業務方無感知的程度,因此 sidecar 容器的升級已再也不是上下交困的難題,從而極大解放了 sidecar 的全部者,提高了 sidecar 版本迭代的速度。             

3.png

注意:Kubernetes 社區對於已經建立的 Pod 只容許修改 container.image 字段,所以對於 sidecar 容器的修改包含除 container.image 的其它字段,則須要經過 Pod 重建的方式,不能直接原地升級。

爲了知足一些複雜的 sidecar 升級場景,SidecarSet 除了原地升級之外,還提供了很是豐富的灰度發佈策略。

4. 灰度發佈

灰度發佈應該算是平常發佈中最多見的一種手段,它可以比較平滑的完成 sidecar 容器的發佈,尤爲是在大規模集羣的場景下,強烈建議使用這種方式。下面是首批暫停後續基於最大不可用滾動發佈的例子,假設一個有 1000 個 pod 須要發佈:

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: sidecarset
spec:
  # ...
  updateStrategy:
    type: RollingUpdate
    partition: 980
    maxUnavailable: 10%

上述配置首先發布(1000 - 980)= 20 個 pod 以後就會暫停發佈,業務能夠觀察一段時間發現 sidecar 容器正常後,調整從新 update SidecarSet 配置:

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: sidecarset
spec:
  # ...
  updateStrategy:
    type: RollingUpdate
    maxUnavailable: 10%

這樣調整後,對於餘下的 980 個 pod,將會按照最大不可用的數量(10% * 1000 = 100)的順序進行發佈,直到全部的 pod 都發布完成。

Partition 的語義是保留舊版本 Pod 的數量或百分比,默認爲 0。這裏的 partition 不表示任何 order 序號。若是在發佈過程當中設置了 partition:

  • 若是是數字,控制器會將 (replicas - partition) 數量的 Pod 更新到最新版本。

  • 若是是百分比,控制器會將 (replicas * (100% - partition)) 數量的 Pod 更新到最新版本。

MaxUnavailable 是發佈過程當中保證的,同一時間下最大不可用的 Pod 數量,默認值爲 1。用戶能夠將其設置爲絕對值或百分比(百分比會被控制器按照 selected pod 作基數來計算出一個背後的絕對值)。

注意:maxUnavailable 和 partition 兩個值是沒有必然關聯。舉例:

  • 當 {matched pod}=100,partition=50,maxUnavailable=10,控制器會發布 50 個 Pod 到新版本,可是發佈窗口爲 10,即同一時間只會發佈 10 個 Pod,每發佈好一個 Pod 纔會再找一個發佈,直到 50 個發佈完成。

  • 當 {matched pod}=100,partition=80,maxUnavailable=30,控制器會發布 20 個 Pod 到新版本,由於知足 maxUnavailable 數量,因此這 20 個 Pod 會同時發佈。

5. 金絲雀發佈

對於有金絲雀發佈需求的業務,能夠經過 strategy.selector 來實現。方式:對於須要率先金絲雀灰度的 pod 打上固定的 labels[canary.release] = true,再經過 strategy.selector.matchLabels 來選中該 pod。

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: sidecarset
spec:
  # ...
  updateStrategy:
    type: RollingUpdate
    selector:
      matchLabels:
      - canary.release: true
    maxUnavailable: 10%

上述配置只會發佈打上金絲雀 labels 的容器,在完成金絲雀驗證以後,經過將 updateStrategy.selector 配置去掉,就會繼續經過最大不可用來滾動發佈。

6. 打散發布

SidecarSet 對於 pod 的升級順序,默認按照以下規則:

  • 對升級的 pod 集合,保證屢次升級的順序一致

  • 選擇優先順序是(越小優先級越高):unscheduled < scheduled, pending < unknown < running, not-ready < ready, newer pods < older pods。

除了上述默認發佈順序以外,scatter 打散策略容許用戶自定義將符合某些標籤的 Pod 打散到整個發佈過程當中。好比,對於像 logtail 這種全局性的 sidecar container,一個集羣當中極可能注入了幾十個業務 pod,所以可使用基於 應用名 的方式來打散 logtail 的方式進行發佈,實現不一樣應用間打散灰度發佈的效果,而且這種方式能夠同最大不可用一塊兒使用。

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: sidecarset
spec:
  # ...
  updateStrategy:
    type: RollingUpdate
    # 配置pod labels,假設全部的pod都包含labels[app_name]
    scatterStrategy:
    - key: app_name
      value: nginx
    - key: app_name
      value: web-server
    - key: app_name
      value: api-gateway
    maxUnavailable: 10%

注意:當前版本必需要列舉全部的應用名稱,咱們將在下個版本支持只配置 label key 的智能打散方式。

7. 實踐

阿里巴巴以及螞蟻集團內部已經大規模的使用 SidecarSet 來管理 sidecar 容器,下面我將經過日誌採集 Logtail sidecar 來做爲一個示例。

  1. 基於 sidecarSet.yaml 配置文件建立 SidecarSet 資源。
# sidecarset.yaml
apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: logtail-sidecarset
spec:
  selector:
    matchLabels:
      app: nginx
  updateStrategy:
    type: RollingUpdate
    maxUnavailable: 10%
  containers:
  - name: logtail
    image: log-service/logtail:0.16.16
    # when recevie sigterm, logtail will delay 10 seconds and then stop
    command:
    - sh
    - -c
    - /usr/local/ilogtail/run_logtail.sh 10
    livenessProbe:
      exec:
        command:
        - /etc/init.d/ilogtaild
        - status
    resources:
      limits:
        memory: 512Mi
      requests:
        cpu: 10m
        memory: 30Mi
    ##### share this volume    
    volumeMounts:
    - name: nginx-log
        mountPath: /var/log/nginx
    transferEnv:
    - sourceContainerName: nginx
        envName: TZ 
  volumes:
  - name: nginx-log
    emptyDir: {}
  1. 基於 pod.yaml 建立 Pod。
apiVersion: v1
kind: Pod
metadata:
  labels:
    # matches the SidecarSet's selector
    app: nginx 
  name: test-pod
spec:
  containers:
  - name: nginx
    image: log-service/docker-log-test:latest
    command: ["/bin/mock_log"]
    args: ["--log-type=nginx", "--stdout=false", "--stderr=true", "--path=/var/log/nginx/access.log", "--total-count=1000000000", "--logs-per-sec=100"]
    volumeMounts:
    - name: nginx-log
        mountPath: /var/log/nginx
    envs:
    - name: TZ
      value: Asia/Shanghai
  volumes:
  - name: nginx-log
    emptyDir: {}
  1. 建立這個 Pod,你會發現其中被注入了 logtail 容器:
$ kubectl get pod
NAME       READY   STATUS    RESTARTS   AGE
test-pod   2/2     Running   0          118s

$ kubectl get pods test-pod -o yaml |grep 'logtail:0.16.16'
    image: log-service/logtail:0.16.16
  1. 此時,SidecarSet status 被更新爲:
$ kubectl get sidecarset logtail-sidecarset -o yaml | grep -A4 status
status:
  matchedPods: 1
  observedGeneration: 1
  readyPods: 1
  updatedPods: 1
  1. 更新 sidecarSet 中 sidecar container 的 image logtail:0.16.18。
$ kubectl edit sidecarsets logtail-sidecarset

# sidecarset.yaml
apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: logtail-sidecarset
spec:
  containers:
    - name: logtail
      image: log-service/logtail:0.16.18
  1. 此時,發現 pod 中的 logtail 容器已經被更新爲了 logtail:0.16.18 版本,而且 pod 以及其它的容器沒有重啓。
$ kubectl get pods |grep test-pod
test-pod                            2/2     Running   1          7m34s

$ kubectl get pods test-pod -o yaml |grep 'image: logtail:0.16.18'
    image: log-service/logtail:0.16.18

$ kubectl describe pods test-pod
Events:
  Type    Reason     Age                 From               Message
  ----    ------     ----                ----               -------
  Normal  Killing    5m47s               kubelet            Container logtail definition changed, will be restarted
  Normal  Pulling    5m17s               kubelet            Pulling image "log-service/logtail:0.16.18"
  Normal  Created    5m5s (x2 over 12m)  kubelet            Created container logtail
  Normal  Started    5m5s (x2 over 12m)  kubelet            Started container logtail
  Normal  Pulled     5m5s                kubelet            Successfully pulled image "log-service/logtail:0.16.18"

總結

本次 OpenKruise v0.8.0 版本的升級,SidecarSet 特性主要是完善了日誌管理類 Sidecar 場景的能力,後續咱們在持續深耕 SidecarSet 穩定性、性能的同時,也將覆蓋更多的場景,好比下一個版本將會增長針對 Service Mesh 場景的支持。同時,咱們也歡迎更多的同窗參與到 OpenKruise 社區來,共同建設一個場景更加豐富、完善的 K8s 應用管理、交付擴展能力,可以面向更加規模化、複雜化、極致性能的場景。

若是你們對 OpenKruise 項目感興趣,有任何但願交流的話題,歡迎你們訪問 OpenKruise 官網GitHub

相關文章
相關標籤/搜索