做者 | 王思宇(酒祝)
來源 | 阿里巴巴雲原生公衆號git
OpenKruise 是阿里雲開源的雲原生應用自動化管理套件,也是當前託管在 Cloud Native Computing Foundation (CNCF) 下的 Sandbox 項目。它來自阿里巴巴多年來容器化、雲原生的技術沉澱,是阿里內部生產環境大規模應用的基於 Kubernetes 之上的標準擴展組件,也是緊貼上游社區標準、適應互聯網規模化場景的技術理念與最佳實踐。github
OpenKruise 在 2021.3.4 發佈了最新的 v0.8.0 版本(ChangeLog),其中一個主要變更是新增了 鏡像預熱 能力。本文由《經過 OpenKruise 實現大規模集羣 鏡像預熱&部署發佈加速實踐》分享整理爲文字,爲你們介紹咱們所提供的鏡像預熱能力的需求來源、實現方式以及使用場景。web
「鏡像」 也算是 Docker 爲容器領域帶來的一大創新。在 Docker 以前,雖然 Linux 已經提供了 cgroup 隔離,儘管阿里巴巴從 2011 年已經逐漸基於 LXC 開始容器化,但都缺少鏡像這種對運行環境的封裝。不過呢,儘管鏡像爲咱們帶來了諸多好處,但不能否認在實際場景中咱們也面臨各類各樣拉鏡像帶來的問題,其中最多見的一點就是拉鏡像的耗時。api
咱們過去聽到過不少用戶對容器化的期待和認識,好比 「極致彈性」、「秒級擴容」、「高效發佈」 等等,但結合 Kubernetes 中一個標準的 Pod 建立過程來看,和用戶的指望仍是有必定差距的(假設 Pod 中包含 sidecar、app 兩個容器):網絡
正常來講,對於小規模集羣中調度、分配/掛載遠程盤、分配網絡等操做耗時較小,對於大規模集羣須要作必定優化,但都還在可控的範圍內。然而對於拉鏡像的耗時,在大規模彈性的集羣中則尤其棘手,即便採用了 P2P 等技術手段來優化,對於一個較大的業務鏡像仍是可能須要較長的時間來拉取,與用戶所指望的擴容、發佈速度不符。架構
而咱們若是能作到將 sidecar 容器的鏡像、以及業務容器的基礎鏡像提早在節點上拉取好,則 Pod 建立過程能夠大幅縮短,其中拉鏡像的耗時甚至能優化 70% 以上:app
而 Kubernetes 自身是沒有提供任何面向鏡像的操做能力的,圍繞 Kubernetes 的生態來看,目前也沒有比較成熟的規模化鏡像預熱產品。這是咱們在 OpenKruise 中提供鏡像預熱的緣由,而且這套鏡像預熱能力已經在阿里巴巴內部的雲原生環境大面積落地,在後文的實踐中也會簡單介紹咱們的基本用法。less
OpenKruise 實現鏡像預熱的原理,要先從它的運行架構看起:ide
從 v0.8.0 開始,安裝了 Kruise 以後,有兩個在 kruise-system 命名空間下的組件:kruise-manager 與 kruise-daemon。前者是一個由 Deployment 部署的中心化組件,一個 kruise-manager 容器(進程)中包含了多個 controller 和 webhook;後者則由 DaemonSet 部署到集羣中的節點上,經過與 CRI 交互來繞過 Kubelet 完成一些擴展能力(好比拉取鏡像、重啓容器等)。優化
所以,Kruise 會爲每一個節點(Node)建立一個同名對應的自定義資源:NodeImage,而每一個節點的 NodeImage 裏寫明瞭在這個節點上須要預熱哪些鏡像,所以這個節點上的 kruise-daemon 只要按照 NodeImage 來執行鏡像的拉取任務便可:
如上圖所示,咱們在 NodeImage 中能指定要拉取的鏡像名、tag、拉取的策略,好比單次拉取的超時、失敗重試次數、任務的 deadline、TTL 時間等等。
有了 NodeImage,咱們也就擁有了最基本的鏡像預熱能力了,不過還不能徹底知足大規模場景的預熱需求。在一個有 5k 個節點的集羣中,要用戶去一個個更新 NodeImage 資源來作預熱顯然是不夠友好的。所以,Kruise 還提供了一個更高抽象的自定義資源 ImagePullJob:
如上圖所示,在 ImagePullJob 中用戶能夠指定一個鏡像要在哪些範圍的節點上批量作預熱,以及這個 job 的拉取策略、生命週期等。一個 ImagePullJob 建立後,會被 kruise-manager 中的 imagepulljob-controller 接收到並處理,將其分解並寫入到全部匹配節點的 NodeImage 中,以此來完成規模化的預熱。
總體的流程以下:
而有了鏡像預熱能力後,咱們怎麼去使用它,或者說什麼場景下須要來使用呢?接下來咱們介紹下鏡像預熱在阿里巴巴中的幾種常見使用方式。
最多見的預熱場景,是在整個集羣維度持續預熱一些基礎鏡像:
apiVersion: apps.kruise.io/v1alpha1 kind: ImagePullJob metadata: name: base-image-job spec: image: xxx/base-image:latest parallelism: 10 completionPolicy: type: Never pullPolicy: backoffLimit: 3 timeoutSeconds: 300
如上述 ImagePullJob 有幾個特徵:
根據咱們的經驗,一個集羣中預熱基礎鏡像的 ImagePullJob 在 10~30 個左右,具體視集羣以及業務場景而定。
咱們一樣也能夠對一些 sidecar 的鏡像作預熱,尤爲是那些基本上每一個業務 Pod 中都會帶有的基礎 sidecar:
apiVersion: apps.kruise.io/v1alpha1 kind: ImagePullJob metadata: name: sidecar-image-job spec: image: xxx/sidecar-image:latest parallelism: 20 completionPolicy: type: Always activeDeadlineSeconds: 1800 ttlSecondsAfterFinished: 300 pullPolicy: backoffLimit: 3 timeoutSeconds: 300
如上述 ImagePullJob 有幾個特徵:
固然,這裏的 sidecar 預熱也能夠配置爲 Never 策略,視場景而定。以咱們的經驗來看,尤爲在 sidecar 作版本迭代、鏡像升級的時候,提早作一次規模化的鏡像預熱,能夠大幅提高後續 Pod 擴容、發佈的速度。
對於一些多租的 Kubernetes 集羣中會存在多個不一樣的業務資源池,其中可能須要將一些特定的業務鏡像按資源池維度來預熱:
apiVersion: apps.kruise.io/v1alpha1 kind: ImagePullJob metadata: name: serverless-job spec: image: xxx/serverless-image:latest parallelism: 10 completionPolicy: type: Never pullPolicy: backoffLimit: 3 timeoutSeconds: 300 selector: matchLabels: resource-pool: serverless
如上述 ImagePullJob 有幾個特徵:
固然,這裏只是以資源池爲例,用戶能夠根據自身的場景來定義在哪些節點上預熱某種鏡像。
最後,再來介紹下 OpenKruise 的下個版本(v0.9.0)中,咱們會基於當前的鏡像預熱實現怎樣的加強能力呢?
以前對 OpenKruise 瞭解過的同窗必定知道,咱們提供的一大特性就是 「原地升級」,即打破了 Kubernetes 原生 workload 發佈時必須將 Pod 刪除、重建的模式,支持在原 Pod 上只更新其中某個容器的鏡像。對原地升級原理感興趣的同窗能夠讀這篇文章:《揭祕:如何爲 Kubernetes 實現原地升級?》。
因爲原地升級避免了 Pod 刪除、重建的過程,它自己已經能爲咱們帶來了以下的好處:
其中,「節省了大部分拉取鏡像的耗時」 後,只須要下載新鏡像上層的部分 layer 便可。而咱們有沒有可能把這個鏡像拉取時間完全優化掉呢?答案是確定的。
如上圖所示,在下個版本中 OpenKruise 的 CloneSet 將支持發佈過程自動鏡像預熱。當用戶還在灰度升級第一批 Pod 的時候,Kruise 會提早在後續 Pod 所在節點上把新版本的鏡像預熱好。這樣一來,在後續批次的 Pod 作原地升級時候,新鏡像都已經在節點上準備好了,也就節省了真正發佈過程當中的拉鏡像耗時。
固然,這種 「發佈+預熱」 的模式也只適用於 OpenKruise 的原地升級場景。對於原生 workload 如 Deployment 而言,因爲發佈時 Pod 是新建出來的,咱們沒法提早預知到它會被調度到的節點,天然也就沒辦法提早把鏡像預熱好了。
若是你們對 OpenKruise 項目感興趣,有任何但願交流的話題,歡迎你們訪問 OpenKruise 官網、GitHub!