kubelet 原理解析三:runtime

本文轉(zhuǎn)自:https://feisky.xyz/posts/kubernetes-container-runtime/

架構(gòu)

Kubelet 架構(gòu)圖


image
  • Generic Runtime Manager:這是容器運(yùn)行時的管理者,負(fù)責(zé)于 CRI 交互,完成容器和鏡像的管理
  • 在 CRI 之下,包括兩種容器運(yùn)行時的實(shí)現(xiàn)
* 一個是內(nèi)置的 dockershim,實(shí)現(xiàn)了 docker 容器引擎的支持以及 CNI 網(wǎng)絡(luò)插件(包括 kubenet)的支持
* 另一個就是外部的容器運(yùn)行時,用來支持 runc、containerd、gvisor 等外部容器運(yùn)行時

Kubelet 通過 CRI 接口跟外部容器運(yùn)行時交互,它包括

  • CRI Server: 這是 CRI gRPC server,監(jiān)聽在 unix socket 上面
  • Streaming Server: 提供 streaming API,包括 Exec、Attach、Port Forward
  • 容器和鏡像的管理:比如拉取鏡像、創(chuàng)建和啟動容器等
  • CNI 網(wǎng)絡(luò)插件的支持: 用于給容器配置網(wǎng)絡(luò)
  • 容器引擎的管理: 比如支持 runc 、containerd 或者支持多個容器引擎

這樣,Kubernetes 中的容器運(yùn)行時按照不同的功能就可以分為三個部分:

  • 第一個是 Kubelet 中容器運(yùn)行時的管理,它通過 CRI 管理容器和鏡像
  • 第二個是容器運(yùn)行時接口,是 Kubelet 與外部容器運(yùn)行時的通信接口
  • 第三個是具體的容器運(yùn)行時實(shí)現(xiàn),包括 Kubelet 內(nèi)置的 dockershim 以及外部的容器運(yùn)行時(如 cri-o、cri-containerd、frakti等)

這樣的話,我們就可以基于這三個不同部分來看一看容器運(yùn)行時的演進(jìn)過程

演進(jìn)過程

image

容器運(yùn)行時的演進(jìn)可以分為三個階段:

  • 第一階段: 在 Kubernetes v1.5 之前,Kubelet 內(nèi)置了 Docker 和 rkt 的支持,并且通過 CNI 網(wǎng)絡(luò)插件給它們配置容器網(wǎng)絡(luò)。這個階段的用戶如果需要自定義運(yùn)行時的功能是比較痛苦的,需要修改 Kubelet 的代碼,并且很有可能這些修改無法推到上游社區(qū)。這樣,還需要維護(hù)一個自己的 fork 分支,維護(hù)和升級都非常麻煩。

  • 第二階段: 不同用戶實(shí)現(xiàn)的容器運(yùn)行時各有所長,許多用戶都希望Kubernetes支持更多的運(yùn)行時。于是,從v1.5 開始增加了 CRI 接口,通過容器運(yùn)行時的抽象層消除了這些障礙,使得無需修改 Kubelet 就可以支持運(yùn)行多種容器運(yùn)行時。CRI 接口包括了一組 Protocol Buffer、gRPC API 、用于 streaming 接口的庫以及用于調(diào)試和驗(yàn)證的一系列工具等。在此階段,內(nèi)置的 Docker 實(shí)現(xiàn)也逐步遷移到了 CRI 的接口之下。但此時 rkt 還未完全遷移,這是因?yàn)?rkt 遷移 CRI 的過程將在獨(dú)立的 repository 完成,方便其維護(hù)和管理。

  • 第三階段: 從 v1.11 開始,Kubelet 內(nèi)置的 rkt 代碼刪除,CNI 的實(shí)現(xiàn)遷移到 dockershim 之內(nèi)。這樣,除了 docker 之外,其他的容器運(yùn)行時都通過 CRI 接入。外部的容器運(yùn)行時一般稱為 CRI Shim,它除了實(shí)現(xiàn) CRI 接口外,也要負(fù)責(zé)為容器配置網(wǎng)絡(luò)。一般推薦使用 CNI,因?yàn)檫@樣可以支持社區(qū)內(nèi)的眾多網(wǎng)絡(luò)插件,不過這不是必需的,網(wǎng)絡(luò)插件只需要滿足 Kubernetes 網(wǎng)絡(luò)的基本假設(shè)即可,即 IP-per-Pod、所有 Pod 和 Node 都可以直接通過 IP 相互訪問。

容器運(yùn)行時接口(CRI)

容器運(yùn)行時接口(CRI)是一個用來擴(kuò)展容器運(yùn)行時的接口,它基于 gPRC,用戶不需要關(guān)心內(nèi)部通信邏輯,而只需要實(shí)現(xiàn)定義的接口就可以,包括 RuntimeService 和 ImageService。

  • RuntimeService負(fù)責(zé)管理Pod和容器的生命周期
  • ImageService負(fù)責(zé)鏡像的生命周期管理

除了 gRPC API,CRI 還包括用于實(shí)現(xiàn) streaming server 的庫(用于 Exec、Attach、PortForward 等接口)和 CRI Tools。

image

基于 CRI 接口的容器運(yùn)行時通常稱為 CRI shim, 這是一個 gRPC Server,監(jiān)聽在本地的unix socket上;而kubelet作為gRPC的客戶端來調(diào)用CRI接口。另外,外部容器運(yùn)行時需要自己負(fù)責(zé)管理容器的網(wǎng)絡(luò),推薦使用CNI,這樣跟Kubernetes的網(wǎng)絡(luò)模型保持一致。

CRI 的推出為容器社區(qū)帶來了新的繁榮,cri-o、frakti、cri-containerd 等一些列的容器運(yùn)行時為不同場景而生:

  • cri-containerd——基于 containerd 的容器運(yùn)行時
  • cri-o——基于 OCI 的容器運(yùn)行時
  • frakti——基于虛擬化的容器運(yùn)行時

而基于這些容器運(yùn)行時,還可以輕易聯(lián)結(jié)新型的容器引擎,比如可以通過 clear container、gVisor 等新的容器引擎配合 cri-o 或 cri-containerd 等輕易接入 Kubernetes,將 Kubernetes 的應(yīng)用場景擴(kuò)展到了傳統(tǒng) IaaS 才能實(shí)現(xiàn)的強(qiáng)隔離和多租戶場景。

當(dāng)使用CRI運(yùn)行時,需要配置kubelet的--container-runtime參數(shù)為remote,并設(shè)置--container-runtime-endpoint為監(jiān)聽的unix socket位置(Windows上面為 tcp 端口)。

CRI 接口

CRI 接口包括 RuntimeService 和 ImageService 兩個服務(wù),這兩個服務(wù)可以在一個 gRPC server 里面實(shí)現(xiàn),當(dāng)然也可以分開成兩個獨(dú)立服務(wù)。目前社區(qū)的很多運(yùn)行時都是將其在一個 gRPC server 里面實(shí)現(xiàn)。

image

分類

管理鏡像的 ImageService 提供了 5 個接口,分別是查詢鏡像列表、拉取鏡像到本地、查詢鏡像狀態(tài)、刪除本地鏡像以及查詢鏡像占用空間等。這些都很容易映射到 docker API 或者 CLI 上面。

而 RuntimeService 則提供了更多的接口,按照功能可以劃分為四組:

  • PodSandbox 的管理接口:PodSandbox 是對 Kubernete Pod 的抽象,用來給容器提供一個隔離的環(huán)境(比如掛載到相同的 cgroup 下面),并提供網(wǎng)絡(luò)等共享的命名空間。PodSandbox 通常對應(yīng)到一個 Pause 容器或者一臺虛擬機(jī)。
  • Container 的管理接口:在指定的 PodSandbox 中創(chuàng)建、啟動、停止和刪除容器。
  • Streaming API 接口:包括 Exec、Attach 和 PortForward 等三個和容器進(jìn)行數(shù)據(jù)交互的接口,這三個接口返回的是運(yùn)行時 Streaming Server 的 URL,而不是直接跟容器交互。
  • 狀態(tài)接口,包括查詢 API 版本和查詢運(yùn)行時狀態(tài)。

Streaming API

Streaming API 用于客戶端與容器進(jìn)行交互,包括Exec、PortForward 和 Attach 等三個接口。Kubelet 內(nèi)置的 docker 通過 nsenter、socat 等方法來支持這些特性,但它們不一定適用于其他的運(yùn)行時,也不支持 Linux 之外的其他平臺。因而,CRI 也顯式定義了這些 API,并且要求容器運(yùn)行時返回一個 streaming server 的 URL 以便 Kubelet 重定向 API Server 發(fā)送過來的流式請求。

這是因?yàn)樗腥萜鞯牧魇秸埱蠖紩?jīng)過 Kubelet,這有可能會給節(jié)點(diǎn)的網(wǎng)絡(luò)流量帶來瓶頸,因而 CRI 要求容器運(yùn)行時啟動一個對應(yīng)請求的單獨(dú)的流服務(wù)器,將地址返回給Kubelet。Kubelet然后將這個信息再返回給Kubernetes API Server,它會打開直接與運(yùn)行時提供的服務(wù)器相連的流連接,并通過它跟客戶端連通。


image

這樣一個完整的 Exec 流程就如上圖所示,分為多個階段:

  • 客戶端 kubectl exec -i -t ...
  • kube-apiserver 向 Kubelet 發(fā)送流式請求 /exec/
  • Kubelet 通過 CRI 接口向 CRI Shim 請求 Exec 的 URL
  • CRI Shim 向 Kubelet 返回 Exec URL
  • Kubelet 向 kube-apiserver 返回重定向的響應(yīng)
  • kube-apiserver 重定向流式請求到 Exec URL,接著就是 CRI Shim 內(nèi)部的 Streaming Server 跟 kube-apiserver 進(jìn)行數(shù)據(jù)交互,完成 Exec 的請求和響應(yīng)

在 v1.10 及更早版本中,容器運(yùn)行時必需返回一個 API Server 可直接訪問的 URL(通常跟 Kubelet 使用相同的監(jiān)聽地址);而從 v1.11 開始,Kubelet 新增了--redirect-container-streaming選項(xiàng)(默認(rèn)為 false),支持不轉(zhuǎn)發(fā)而是代理 Streaming 請求,這樣運(yùn)行時可以返回一個 localhost 的 URL(當(dāng)然也不再需要配置 TLS)。

運(yùn)行時分類

以下是幾個常見容器運(yùn)行時的例子,它們各有所長,并且也支持不同的容器引擎:

image

隔離性

在多租戶場景下,強(qiáng)隔離(特別是虛擬化級別的隔離)是一個最基本的需求。

以前使用 Kubernetes 時,由于只支持Docker 容器,而它只提供了內(nèi)核命名空間(namespace)的隔離,雖然也支持 SELinux、AppArmor 等基本的安全控制,但還是無法滿足多租戶的需求。所以曾經(jīng)社區(qū)有人提出節(jié)點(diǎn)獨(dú)占的方式實(shí)現(xiàn)租戶隔離,即每個容器或租戶獨(dú)占一臺虛擬機(jī),資源的浪費(fèi)是很明顯的。

有了 CRI 之后,就可以接入 Kata Container、Clear Container 等基于虛擬化的容器引擎。這樣通過虛擬化實(shí)現(xiàn)了容器的強(qiáng)隔離,不同租戶的容器也可以運(yùn)行在相同的 Node 上,大大提高了資源的利用率。

當(dāng)然了,多租戶不僅需要容器自身的強(qiáng)隔離,還需要眾多其他的功能一起配合,比如

網(wǎng)絡(luò)隔離,比如可以使用 CNI 構(gòu)建新的網(wǎng)絡(luò)插件,把不同租戶的 Pod 接入到相互隔離的虛擬網(wǎng)絡(luò)中。
資源管理,比如基于 CRD 構(gòu)建租戶 API 和租戶控制器,管理租戶和租戶的資源。
認(rèn)證、授權(quán)、配額管理等等也都可以在 Kubernetes API 之上構(gòu)建。

工具

CRI Tools 是社區(qū) Node 組針對 CRI 接口開發(fā)的輔助工具,它包括兩個工具:crictl 和 critest。

crictl 是一個容器運(yùn)行時命令行接口,它對系統(tǒng)和應(yīng)用的排錯來說是個很有用的工具。當(dāng)使用 Docker 運(yùn)行時,調(diào)試系統(tǒng)信息的時候我們可能使用 docker ps 和 docker inspect 等命令檢查應(yīng)用的進(jìn)程情況。但是對于其他基于 CRI 的容器運(yùn)行時來說,它們可能沒有自己的命令行工具;或者即便有,它們的操作界面也不一定與 Kubernetes 中的概念一致。更不用說,很有很多的命令對 Kubernetes 沒什么用,甚至?xí)p害系統(tǒng)(比如 docker rename)。因而,我們推薦使用 crictl 作為 Docker CLI 的繼任者,用于 Kubernetes 節(jié)點(diǎn)上 pod、容器以及鏡像的除錯工具。

crictl 提供了類似 Docker CLI 的使用體驗(yàn), 并且支持所有 CRI 兼容的容器運(yùn)行時。并且,crictl 提供了一個對 Kubernetes 來說更加友好的容器視角:它就是為 Kubernetes 而設(shè)計的,有不同的命令分別與Pod 和容器進(jìn)行交互。例如 crictl pods 會列出 Pod 信息,而 crictl ps 只會列出應(yīng)用容器的信息。

而 critest 則是一個容器運(yùn)行時的驗(yàn)證測試工具,用于驗(yàn)證容器運(yùn)行時是否符合 Kubelet CRI 的要求。除了驗(yàn)證測試,critest 還提供了 CRI 接口的性能測試,比如 critest -benchmark。

推薦將 critest 集成到容器運(yùn)行時開發(fā)的 Devops 流程中,保證每個變更都不會破壞 CRI 的基本功能。另外,還可以選擇將 critest 的測試結(jié)果與 Kubernetes Node E2E 的結(jié)果提交到 Sig-node 的 TestGrid,向社區(qū)和用戶展示。

多容器運(yùn)行時

多容器運(yùn)行時用于不同的目的,比如使用虛擬化容器引擎式運(yùn)行不可信應(yīng)用和多租戶應(yīng)用,而使用 Docker 運(yùn)行系統(tǒng)組件或者無法虛擬化的容器(比如需要 HostNetwork 的容器)。比如典型的用例為:

  • Kata Containers/gVisor + runc
  • Windows Process isolation + Hyper-V isolation containers

以前,多容器運(yùn)行時通常以注解(Annotation)的形式支持,比如 cri-o、frakti 等都是這么支持了多容器運(yùn)行時。但這一點(diǎn)也不優(yōu)雅,并且也無法實(shí)現(xiàn)基于容器運(yùn)行時來調(diào)度容器。因而,Kubernetes 在 v1.12 中將開始增加 RuntimeClass 這個新的 API 對象,用來支持多容器運(yùn)行時。

GPU 支持

Nvidia提供了k8s pod使用GPU的一整套解決方案。運(yùn)行時方面,nvidia提供了特定的運(yùn)行時,主要的功能是為了讓container訪問從node節(jié)點(diǎn)上分配的GPU資源。

image

如上圖所示,libnvidia-container被整合進(jìn)docker的runc中。通過在runc的prestart hook 中調(diào)用nvidia-container-runtime-hook來控制GPU。啟動容器時,prestart hook會校驗(yàn)環(huán)境變量GPU-enabled來校驗(yàn)該容器是否需要啟用GPU,一旦確認(rèn)需要啟用,就調(diào)用nvidia定制的運(yùn)行時來啟動容器,從而為容器分配limit指定個數(shù)的GPU。

在k8s的調(diào)度方面,nvidia基于k8s的device plugin來實(shí)現(xiàn)了kubelet層GPU資源的上報,通過在pod的spec中指定對應(yīng)的limit來聲明對GPU個數(shù)的申請情況。在spec中必須指定limit的值(且必須為整數(shù)),reqire的值要么不設(shè)置,要么等于limit的值。

NVIDIA_VISIBLE_DEVICES : controls which GPUs will be accessible inside the container. By default, all GPUs are accessible to the container.
NVIDIA_DRIVER_CAPABILITIES : controls which driver features (e.g. compute, graphics) are exposed to the container.
NVIDIA_REQUIRE_* : a logical expression to define the constraints (e.g. minimum CUDA, driver or compute capability) on the configurations supported by the container.

serverless

無服務(wù)器(Serverless)現(xiàn)在是一個很熱門的方向,各個云平臺也提供很多種類的無服務(wù)器計算服務(wù),比如 Azure Container Instance、AWS Farget 等。它們的好處是用戶不需要去管理容器底層的基礎(chǔ)設(shè)施,而只需要管理容器即可,并且容器通常按實(shí)際的運(yùn)行時間收費(fèi)。這樣,對用戶來說,不僅省去了管理基礎(chǔ)設(shè)施的繁瑣步驟,還更節(jié)省成本。

那么 CRI 在這里有什么應(yīng)用場景呢?假如你是一個云平臺的管理者,想要構(gòu)建一個無服務(wù)器容器服務(wù),那么使用 CRI 配合多容器運(yùn)行時就是一個很好的思路。這樣的話,

  • Kubernetes 可以用來給整個平臺提供調(diào)度和編排
  • 基于 Kubernetes API 可以搭建租戶管理功能
  • 基于 CRI 可以實(shí)現(xiàn)多租戶容器運(yùn)行的強(qiáng)隔離
  • 基于 CNI 可以實(shí)現(xiàn)多租戶的網(wǎng)絡(luò)強(qiáng)隔離

那么,對云平臺的用戶呢?這些無服務(wù)器容器服務(wù)提供的功能通常都比較簡單,并不具備編排的功能。但可以借助 Virtual Kubelet 項(xiàng)目,使用 Kubernetes 為這些平臺的容器提供編排功能。

Virtual Kubelet 是針對 Serverless 容器平臺設(shè)計的虛擬 Kubernetes 節(jié)點(diǎn),它模擬了 Kubelet 的功能,并將 Serverless 容器平臺抽象為一個虛擬的無限資源的 Node。這樣就可以通過 Kubernetes API 來管理其上的容器。

image

目前 Virtual Kubelet 已經(jīng)支持了眾多的云平臺,包括

  • Azure Container Instance
  • AWS Farget
  • Service Fabric
  • Hyper.sh
  • IoT Edge
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容