做者 | 趙明山(立衡)
來源 | 阿里巴巴雲原生公衆號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 容器的通用化需求,並將其沉澱到 OpenKruise 上面,最終抽象爲 SidecarSet 做爲管理和升級種類繁多 sidecar 容器的利器。api
SidecarSet 是 OpenKruise 中針對 sidecar 抽象出來的概念,負責注入和升級 Kubernetes 集羣中的 sidecar 容器,是 OpenKruise 的核心 workload 之一。它提供了很是豐富的功能,用戶使用 SidecarSet 能夠很是方便實現 sidecar 容器的管理。主要特性以下:安全
配置單獨管理:爲每個 sidecar 容器配置單獨的 SidecarSet 配置,方便管理。架構
自動注入:在新建、擴容、重建 pod 的場景中,實現 sidecar 容器的自動注入。
注意:針對 Pod 中包含多個容器的模式,其中對外提供主要業務邏輯能力的容器稱之爲 主容器,其它一些如日誌採集、安全、代理等輔助能力的容器稱之爲 Sidecar 容器。例如:一個對外提供 web 能力的 pod,nginx 容器提供主要的 web server 能力即爲主容器,logtail 容器負責採集、上報 nginx 日誌即爲 Sidecar 容器。本文中的 SidecarSet 資源抽象也是爲解決 Sidecar 容器的一些問題。
應用日誌可讓你瞭解應用內部的運行情況,日誌對調試問題和監控集羣活動很是有用。應用容器化後,最簡單且最普遍採用的日誌記錄方式就是寫入標準輸出和標準錯誤。
可是,在當前分佈式系統、大規模集羣的時代下,上述方案還不足以達到生產環境的標準。首先,對於分佈式系統而言,日誌都是分散在單個容器裏面,沒有一個統一彙總的地方。其次,若是發生容器崩潰、Pod 被驅逐等場景,會出現日誌丟失的狀況。所以,須要一種更加可靠,獨立於容器生命週期的日誌解決方案。
Sidecar logging architectures 是將 logging agent 放到一個獨立的 sidecar 容器中,經過共享日誌目錄的方式,實現容器日誌的採集,而後存儲到日誌平臺的後端存儲。
阿里巴巴以及螞蟻集團內部一樣也是基於這種架構實現了容器的日誌採集,下面我將介 紹OpenKruise SidecarSet 如何助力 Sidecar 日誌架構在 Kubernetes 集羣中的大規模落地實踐。
OpenKruise SidecarSet 基於 Kubernetes AdmissionWebhook 機制實現了 sidecar 容器的自動注入,所以只要將 sidecar 配置到 SidecarSet 中,無論用戶用 CloneSet、Deployment、StatefulSet 等任何方式部署,擴出來的 Pod 中都會注入定義好的 sidecar 容器。
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 選擇器
數據卷共享
注意:Kubernetes 社區對於已經建立的 Pod 不容許修改 container 數量,因此上述注入能力只能發生在 Pod 建立階段,對於已經建立的 Pod 須要經過重建的方式來注入。
SidecarSet 不只實現 sidecar 容器的注入,並且複用了 OpenKruise 中原地升級的特性,實現了在不重啓 Pod 和主容器的前提下單獨升級 sidecar 容器的能力。因爲這種升級方式基本上能作到業務方無感知的程度,因此 sidecar 容器的升級已再也不是上下交困的難題,從而極大解放了 sidecar 的全部者,提高了 sidecar 版本迭代的速度。
注意:Kubernetes 社區對於已經建立的 Pod 只容許修改 container.image 字段,所以對於 sidecar 容器的修改包含除 container.image 的其它字段,則須要經過 Pod 重建的方式,不能直接原地升級。
爲了知足一些複雜的 sidecar 升級場景,SidecarSet 除了原地升級之外,還提供了很是豐富的灰度發佈策略。
灰度發佈應該算是平常發佈中最多見的一種手段,它可以比較平滑的完成 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 個發佈完成。
對於有金絲雀發佈需求的業務,能夠經過 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 配置去掉,就會繼續經過最大不可用來滾動發佈。
SidecarSet 對於 pod 的升級順序,默認按照以下規則:
對升級的 pod 集合,保證屢次升級的順序一致。
除了上述默認發佈順序以外,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 的智能打散方式。
阿里巴巴以及螞蟻集團內部已經大規模的使用 SidecarSet 來管理 sidecar 容器,下面我將經過日誌採集 Logtail sidecar 來做爲一個示例。
# 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: {}
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: {}
$ 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
$ kubectl get sidecarset logtail-sidecarset -o yaml | grep -A4 status status: matchedPods: 1 observedGeneration: 1 readyPods: 1 updatedPods: 1
$ 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
$ 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!