如何優(yōu)雅的在 Kubernetes 集群中緩存容器鏡像
介紹
當(dāng)容器化應(yīng)用部署到Kubernetes 集群時(shí),K8s控制平面會(huì)將 Pod調(diào)度到集群中的工作節(jié)點(diǎn)。運(yùn)行在工作節(jié)點(diǎn)中的節(jié)點(diǎn)代理(Kubelet)與安裝在節(jié)點(diǎn)中的容器運(yùn)行時(shí)(例如 containerd)協(xié)調(diào),并從鏡像registry中拉取必要的容器鏡像。根據(jù)鏡像的大小和可用的網(wǎng)絡(luò)帶寬,將所有鏡像拉取到節(jié)點(diǎn)需要時(shí)間。因此,在任何容器化應(yīng)用程序中,我們都應(yīng)該意識(shí)到由于從registry中獲取鏡像而引入的延遲。然而,作為進(jìn)程運(yùn)行的傳統(tǒng)應(yīng)用程序(例如由 systemd管理)不會(huì)受到這種延遲的影響,因?yàn)樗斜匦璧奈募家呀?jīng)安裝在機(jī)器中。
想象一下,您的容器化應(yīng)用程序遇到了突然的流量激增,它需要立即橫向擴(kuò)展(即需要?jiǎng)?chuàng)建額外的實(shí)例)。如果您配置了 Horizontal Pod Autoscaler (HPA),K8s控制平面會(huì)創(chuàng)建額外的 Pod 副本。但是,這些 Pod 將無法用于處理增加的流量,直到所需的鏡像被拉取,容器啟動(dòng)并運(yùn)行?;蛘呒僭O(shè)您的應(yīng)用程序需要處理高速實(shí)時(shí)數(shù)據(jù)。此類應(yīng)用程序?qū)ζ鋯?dòng)和擴(kuò)展的速度有嚴(yán)格的要求,因?yàn)樗鼘?shí)現(xiàn)的目的的本質(zhì)。
簡而言之,在一些用例中,由于從registry中提取鏡像而引入的延遲是不可接受的。此外,集群和鏡像registry之間的網(wǎng)絡(luò)連接可能會(huì)受到帶寬不足的影響,或者連接可能會(huì)完全丟失。在某些情況下,尤其是在邊緣計(jì)算中,應(yīng)用程序必須優(yōu)雅地容忍間歇性網(wǎng)絡(luò)連接。
這些挑戰(zhàn)可以通過不同的方式來解決。在這些場景中非常有用的一種解決方案是將容器鏡像直接緩存在集群工作節(jié)點(diǎn)上,這樣 Kubelet 不需要拉取這些鏡像,而是立即使用已經(jīng)緩存在節(jié)點(diǎn)中的鏡像。在這篇博客中,我將解釋如何使用開源項(xiàng)目kube-fledged 在 Kubernetes 集群中構(gòu)建和管理容器鏡像的緩存。
現(xiàn)有解決方案
在向您介紹 kube-fledged 之前,讓我簡要介紹一下解決此問題的現(xiàn)有解決方案。廣泛使用的方法是在集群內(nèi)運(yùn)行一個(gè) Registry 鏡像。兩種廣泛使用的解決方案是
- 集群內(nèi)自托管registry
- 直通緩存
在前一種解決方案中,本地注冊(cè)中心在 k8s集群中運(yùn)行,并在容器運(yùn)行時(shí)配置為鏡像注冊(cè)中心。
任何鏡像拉取請(qǐng)求都被定向到集群內(nèi)registry。如果失敗,請(qǐng)求將被定向到主注冊(cè)中心。在后一種解決方案中,本地registry具有緩存功能。第一次拉取鏡像時(shí),它會(huì)緩存在本地registry中。對(duì)鏡像的后續(xù)請(qǐng)求由本地registry提供服務(wù)。
現(xiàn)有解決方案的缺點(diǎn)
設(shè)置和維護(hù)本地registry鏡像會(huì)消耗大量計(jì)算資源和人力資源。
對(duì)于跨越多個(gè)區(qū)域的龐大集群,我們需要有多個(gè)本地registry鏡像。當(dāng)應(yīng)用程序?qū)嵗缭蕉鄠€(gè)區(qū)域時(shí),這會(huì)引入不必要的復(fù)雜性。您可能需要有多個(gè)部署清單,每個(gè)清單都指向該區(qū)域的本地 registry鏡像。
這些方法并不能完全解決Pod快速啟動(dòng)的需求,因?yàn)閺谋镜冂R像拉取鏡像仍然存在明顯的延遲。有幾個(gè)用例不能容忍這種延遲。
節(jié)點(diǎn)可能會(huì)失去與本地registry鏡像的網(wǎng)絡(luò)連接,因此 Pod 將卡住,直到連接恢復(fù)。
kube-fledged 概述
kube-fledged是一個(gè) kubernetes插件或operator,用于直接在 kubernetes 集群的工作節(jié)點(diǎn)上創(chuàng)建和管理容器鏡像的緩存。
它允許用戶定義鏡像列表以及這些鏡像應(yīng)該緩存(即拉?。┑侥男┕ぷ鞴?jié)點(diǎn)上。因此,應(yīng)用程序 pod幾乎立即啟動(dòng),因?yàn)椴恍枰獜膔egistry中提取鏡像。kube-fledged提供了 CRUD API來管理鏡像緩存的生命周期,并支持多個(gè)可配置的參數(shù)來根據(jù)需要自定義功能。
https : //github.com/senthilrch/kube-fledged)
kube-fledged 被設(shè)計(jì)和構(gòu)建為用于管理Kubernetes 中的鏡像緩存的通用解決方案。盡管主要用例是實(shí)現(xiàn)快速 Pod 啟動(dòng)和擴(kuò)展,但該解決方案支持多種用例,如下所述
用例
- 需要快速啟動(dòng)的應(yīng)用程序。例如,由于數(shù)據(jù)量激增,執(zhí)行實(shí)時(shí)數(shù)據(jù)處理的應(yīng)用程序需要快速擴(kuò)展。
- 無服務(wù)器函數(shù),因?yàn)樗鼈冃枰獙?duì)傳入的事件立即做出反應(yīng)。
- 在邊緣設(shè)備上運(yùn)行的 IoT 應(yīng)用程序,因?yàn)檫吘壴O(shè)備和鏡像registry之間的網(wǎng)絡(luò)連接是間歇性的。
- 如果需要從私有registry中拉取鏡像,并且無法授予每個(gè)人從該registry中拉取鏡像的訪問權(quán)限,則可以在集群節(jié)點(diǎn)上提供這些鏡像。
- 如果集群管理員或操作員需要對(duì)應(yīng)用程序進(jìn)行升級(jí),并希望事先驗(yàn)證是否可以成功拉取新鏡像。
kube-fledged 的工作原理
Kubernetes允許開發(fā)人員通過自定義資源擴(kuò)展 kubernetes api。kube-fledged定義了一個(gè)類型為“ ImageCache ”的自定義資源并實(shí)現(xiàn)了一個(gè)自定義控制器(名為 kubefledged-controller)。
kubefledged-controller負(fù)責(zé)管理鏡像緩存。用戶可以使用 kubectl命令來創(chuàng)建和刪除 ImageCache 資源。
kubefledged-controller有一個(gè)內(nèi)置的 Image Manager 例程,負(fù)責(zé)拉取和刪除鏡像。使用 kubernetes job拉取或刪除鏡像。如果啟用,刷新工作者會(huì)定期刷新鏡像緩存。kubefledged-controller 在 ImageCache 資源的 status 字段中更新鏡像拉取、刷新和鏡像刪除的狀態(tài)。kubefledged-webhook-server負(fù)責(zé)驗(yàn)證 ImageCache資源的字段。
如果您需要在集群中創(chuàng)建鏡像緩存,則只需ImageCache通過指定要拉取的鏡像列表以及nodeSelector. 將nodeSelector用于指定在其上的鏡像應(yīng)該被高速緩存的節(jié)點(diǎn)。如果您希望鏡像緩存在集群的所有節(jié)點(diǎn)中,則省略nodeSelector. 當(dāng)您將清單提交到集群時(shí),API 服務(wù)器將向 kubefledged-webhook-server 發(fā)布驗(yàn)證webhook事件。Webhook 服務(wù)器驗(yàn)證cacheSpec的清單。在收到來自 webhook 服務(wù)器的成功響應(yīng)后,API 服務(wù)器將 ImageCache 資源持久保存在etcd 中。這會(huì)觸發(fā)對(duì)kubefledged-controller 的 Informer 通知,該通知將請(qǐng)求排隊(duì)。該請(qǐng)求由鏡像緩存工作器接收,它創(chuàng)建多個(gè)鏡像拉取請(qǐng)求(每個(gè)節(jié)點(diǎn)每個(gè)鏡像一個(gè)請(qǐng)求)并將它們放入鏡像拉取/刪除隊(duì)列中。
這些請(qǐng)求由鏡像管理器例程處理。對(duì)于每個(gè)請(qǐng)求,鏡像管理器都會(huì)創(chuàng)建一個(gè) k8s job,負(fù)責(zé)將鏡像拉入緩存。鏡像管理器會(huì)跟蹤它創(chuàng)建的job,一旦job完成,它就會(huì)將響應(yīng)放在一個(gè)單獨(dú)的隊(duì)列中。然后鏡像緩存工作器聚合來自鏡像管理器的所有結(jié)果,最后更新 ImageCache資源的狀態(tài)部分。
kube-fledged 有一個(gè)刷新工作例程,它會(huì)定期運(yùn)行以保持鏡像緩存刷新。如果它發(fā)現(xiàn)緩存中缺少任何鏡像(可能被 kubelet 的鏡像垃圾收集刪除了),它會(huì)重新將鏡像拉入緩存。帶有:latest標(biāo)簽的鏡像總是在刷新周期中被重新拉出。默認(rèn)情況下,刷新周期每5m. 用戶可以在部署 kube-fledged 時(shí)將其修改為不同的值或完全禁用自動(dòng)刷新機(jī)制。還支持按需刷新機(jī)制,用戶可以使用該機(jī)制請(qǐng)求 kube-fledged 立即刷新鏡像緩存。
kube-fledged 支持的鏡像緩存操作
kube-fledged 支持以下鏡像緩存操作。所有這些操作都可以使用 kubectl 或通過直接向 Kubernetes API服務(wù)器提交 REST API 請(qǐng)求來執(zhí)行:
- 創(chuàng)建鏡像緩存
- 修改鏡像緩存
- 刷新鏡像緩存
- 清除鏡像緩存
- 刪除鏡像緩存
支持的Container Runtime
- Docker
- Containerd
- Cri-o
支持的平臺(tái)
- linux/amd64
- linux/arm
- linux/arm64
使用 kube-fledged
使用 kube-fledged 的最快方法是使用項(xiàng)目的 GitHub Repo ( https://github.com/senthilrch/kube-fledged) 中的 YAML 清單來部署它。您還可以使用 helm chart 和 helm operator 部署它。在下面找到使用清單部署 kube-fledged 的步驟:
克隆源代碼存儲(chǔ)庫
- $ mkdir -p $HOME/src/github.com/senthilrch
- $ git clone https://github.com/senthilrch/kube-fledged.git $HOME/src/github.com/senthilrch/kube-fledged
- $ cd $HOME/src/github.com/senthilrch/kube-fledged
將kube-fledged部署到集群
- $ make deploy-using-yaml
驗(yàn)證kube-fledged是否成功部署
- $ kubectl get pods -n kube-fledged -l app=kubefledged
- $ kubectl get imagecaches -n kube-fledged (Output should be: 'No resources found')
類似解決方案
在下面找到我注意到的類似開源解決方案的列表。這些解決方案嘗試使用替代方法解決問題
Stargz Snapshotter:具有延遲拉動(dòng)功能的快速容器鏡像分發(fā)插件
(https://github.com/containerd/stargz-snapshotter)
Uber Kraken:Kraken 是一個(gè) P2P Docker registry,能夠在幾秒鐘內(nèi)分發(fā) TB數(shù)據(jù)
(https://github.com/uber/kraken)
Imagewolf:ImageWolf 是一種 PoC,它提供了一種將 Docker 鏡像加載到集群上的極快方式,從而可以更快地推送更新
https://github.com/ContainerSolutions/ImageWolf)
結(jié)論
有些應(yīng)用程序和用例需要快速啟動(dòng)和擴(kuò)展。在這種情況下,從registry中拉取鏡像所帶來的延遲可能是不可接受的。此外,與registry的網(wǎng)絡(luò)連接可能不穩(wěn)定/間歇性。不授予所有用戶訪問安全registry的權(quán)限可能是出于安全原因。Kube-fledged 是一個(gè)簡單而有用的解決方案,可以直接在集群工作節(jié)點(diǎn)上構(gòu)建和管理容器鏡像的緩存