自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

CRI shim:kubelet怎么與runtime交互(一)

運(yùn)維 系統(tǒng)運(yùn)維
實(shí)現(xiàn)了 CRI 接口的容器運(yùn)行時(shí)通常稱為 CRI shim, 這是一個(gè) gRPC Server,監(jiān)聽在本地的 unix socket 上;而 kubelet 作為 gRPC 的客戶端來調(diào)用 CRI 接口,來進(jìn)行 Pod 和容器、鏡像的生命周期管理。

[[433449]]

CRI shim是什么?

實(shí)現(xiàn)了 CRI 接口的容器運(yùn)行時(shí)通常稱為 CRI shim, 這是一個(gè) gRPC Server,監(jiān)聽在本地的 unix socket 上;而 kubelet 作為 gRPC 的客戶端來調(diào)用 CRI 接口,來進(jìn)行 Pod 和容器、鏡像的生命周期管理。另外,容器運(yùn)行時(shí)需要自己負(fù)責(zé)管理容器的網(wǎng)絡(luò),推薦使用 CNI。

kubelet 調(diào)用下層容器運(yùn)行時(shí)的執(zhí)行過程,并不會(huì)直接調(diào)用Docker 的 API,而是通過一組叫作 CRI(Container Runtime Interface,容器運(yùn)行時(shí)接口)的 gRPC 接口來間接執(zhí)行的,意味著需要使用新的連接方式與 docker 通信,為了兼容以前的版本,k8s 提供了針對(duì) docker 的 CRI 實(shí)現(xiàn),也就是kubelet包下的dockershim包,dockershim是一個(gè) grpc 服務(wù),監(jiān)聽一個(gè)端口供 kubelet 連接,dockershim收到 kubelet 的請(qǐng)求后,將其轉(zhuǎn)化為 REST API 請(qǐng)求,再發(fā)送給docker daemon。Kubernetes 項(xiàng)目之所以要在 kubelet 中引入這樣一層單獨(dú)的抽象,當(dāng)然是為了對(duì) Kubernetes 屏蔽下層容器運(yùn)行時(shí)的差異。

解決思路再次體現(xiàn)了《代碼大全2》里提到的那句經(jīng)典名言:any problem in computer science can be sloved by another layer of indirecition。計(jì)算機(jī)科學(xué)領(lǐng)域的任何問題都可以通過增加一個(gè)中間層來解決,我們的 CRI shim就是加了這樣一層。

CRI shim server 接口圖示

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

ImageServiceServer 提供了 5 個(gè)接口,用于管理容器鏡像。管理鏡像的 ImageService 提供了 5 個(gè)接口:

  • 查詢鏡像列表;
  • 拉取鏡像到本地;
  • 查詢鏡像狀態(tài);
  • 刪除本地鏡像;
  • 查詢鏡像占用空間等。

關(guān)于容器鏡像的操作比較簡(jiǎn)單,所以我們就暫且略過。接下來,我主要為你講解一下 RuntimeService 部分。RuntimeService 則提供了更多的接口,按照功能可以劃分為四組:

  • PodSandbox 的管理接口:CRI 設(shè)計(jì)的一個(gè)重要原則,就是確保這個(gè)接口本身,只關(guān)注容器,不關(guān)注 Pod。
  • PodSandbox 是對(duì) Kubernete Pod 的抽象,用來給容器提供一個(gè)隔離的環(huán)境(比如掛載到相同的 CGroup 下面),并提供網(wǎng)絡(luò)等共享的命名空間。PodSandbox 通常對(duì)應(yīng)到一個(gè) Pause 容器或者一臺(tái)虛擬機(jī);
  • Container 的管理接口:在指定的 PodSandbox 中創(chuàng)建、啟動(dòng)、停止和刪除容器;

  • Streaming API 接口:包括 Exec、Attach 和 PortForward 等三個(gè)和容器進(jìn)行數(shù)據(jù)交互的接口,這三個(gè)接口返回的是運(yùn)行時(shí) Streaming Server 的 URL,而不是直接跟容器交互。kubelet 需要跟容器項(xiàng)目維護(hù)一個(gè)長(zhǎng)連接來傳輸數(shù)據(jù)。這種 API,我們就稱之為 Streaming API。
  • 狀態(tài)接口:包括查詢 API 版本和查詢運(yùn)行時(shí)狀態(tài)。

我們通過 kubectl 命令來運(yùn)行一個(gè) Pod,那么 Kubelet 就會(huì)通過 CRI 執(zhí)行以下操作:

  • 首先調(diào)用 RunPodSandbox 接口來創(chuàng)建一個(gè) Pod 容器,Pod 容器是用來持有容器的相關(guān)資源的,比如說網(wǎng)絡(luò)空間、PID空間、進(jìn)程空間等資源;
  • 然后調(diào)用 CreatContainer 接口在 Pod 容器的空間創(chuàng)建業(yè)務(wù)容器;
  • 再調(diào)用 StartContainer 接口啟動(dòng)運(yùn)行容器
  • 最后調(diào)用停止,銷毀容器的接口為 StopContainer 與 RemoveContainer。

就完成了整個(gè)Container的生命周期。

Streaming API

CRI shim 對(duì) Streaming API 的實(shí)現(xiàn),依賴于一套獨(dú)立的 Streaming Server 機(jī)制。Streaming API 用于客戶端與容器進(jìn)行交互,包括 Exec、PortForward 和 Attach 等三個(gè)接口。kubelet 內(nèi)置的 Docker 通過 nsenter、socat 等方法來支持這些特性,但它們不一定適用于其他的運(yùn)行時(shí),也不支持 Linux 之外的其他平臺(tái)。因而,CRI 也顯式定義了這些 API,并且要求容器運(yùn)行時(shí)返回一個(gè) Streaming Server 的 URL 以便 kubelet 重定向 API Server 發(fā)送過來的流式請(qǐng)求。

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

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

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

也就是說 apiserver 其實(shí)實(shí)際上是跟 streaming server 交互來獲取我們的流式數(shù)據(jù)的。這樣一來讓我們的整個(gè) CRI Server 接口更輕量、更可靠。

注意:當(dāng)然,這個(gè) Streaming Server 本身,是需要通過使用 SIG-Node 為你維護(hù)的 Streaming API 庫來實(shí)現(xiàn)的。并且,Streaming Server 會(huì)在 CRI shim 啟動(dòng)時(shí)就一起啟動(dòng)。此外,Stream Server 這一部分具體怎么實(shí)現(xiàn),完全可以由 CRI shim 的維護(hù)者自行決定。比如,對(duì)于 Docker 項(xiàng)目來說,dockershim 就是直接調(diào)用 Docker 的 Exec API 來作為實(shí)現(xiàn)的。

CRI-containerd架構(gòu)解析與主要接口解析

整個(gè)架構(gòu)看起來非常直觀。這里的 Meta services、Runtime service 與 Storage service 都是 containerd 提供的接口。它們是通用的容器相關(guān)的接口,包括鏡像管理、容器運(yùn)行時(shí)管理等。CRI 在這之上包裝了一個(gè) gRPC 的服務(wù)。右側(cè)就是具體的容器的實(shí)現(xiàn)。比如說,創(chuàng)建容器時(shí)就要?jiǎng)?chuàng)建具體的 runtime 和它的containerd-shim。Container 和 Pod Sandbox組成了一個(gè)Pod。

CRI-containerd 的一個(gè)好處是,containerd 還額外實(shí)現(xiàn)了更豐富的容器接口,所以它可以用 containerd 提供的 ctr 工具來調(diào)用這些豐富的容器運(yùn)行時(shí)接口,而不只是 CRI 接口

CRI實(shí)現(xiàn)了兩個(gè)GRPC協(xié)議的API,提供兩種服務(wù)ImageService和RuntimeService。

  1. // grpcServices are all the grpc services provided by cri containerd. 
  2. type grpcServices interface { 
  3.   runtime.RuntimeServiceServer 
  4.   runtime.ImageServiceServer 
  5. // CRIService is the interface implement CRI remote service server. 
  6. type CRIService interface { 
  7.   Run() error 
  8.   // io.Closer is used by containerd to gracefully stop cri service. 
  9.   io.Closer 
  10.   plugin.Service 
  11.   grpcServices 

CRI的實(shí)現(xiàn)CRIService中包含了很多重要的組件:其中最重要的是cni.CNI,用于配置容器網(wǎng)絡(luò)。還有containerd.Client,用于連接containerd來創(chuàng)建容器。

  1. // criService implements CRIService. 
  2. type criService struct { 
  3.  // config contains all configurations. 
  4.  config criconfig.Config 
  5.  // imageFSPath is the path to image filesystem. 
  6.  imageFSPath string 
  7.  // os is an interface for all required os operations. 
  8.  os osinterface.OS 
  9.  // sandboxStore stores all resources associated with sandboxes. 
  10.  sandboxStore *sandboxstore.Store 
  11.  // sandboxNameIndex stores all sandbox names and make sure each name 
  12.  // is unique
  13.  sandboxNameIndex *registrar.Registrar 
  14.  // containerStore stores all resources associated with containers. 
  15.  containerStore *containerstore.Store 
  16.  // containerNameIndex stores all container names and make sure each 
  17.  // name is unique
  18.  containerNameIndex *registrar.Registrar 
  19.  // imageStore stores all resources associated with images. 
  20.  imageStore *imagestore.Store 
  21.  // snapshotStore stores information of all snapshots. 
  22.  snapshotStore *snapshotstore.Store 
  23.  // netPlugin is used to setup and teardown network when run/stop pod sandbox. 
  24.  netPlugin cni.CNI 
  25.  // client is an instance of the containerd client 
  26.  client *containerd.Client 
  27.  // streamServer is the streaming server serves container streaming request. 
  28.  streamServer streaming.Server 
  29.  // eventMonitor is the monitor monitors containerd events. 
  30.  eventMonitor *eventMonitor 
  31.  // initialized indicates whether the server is initialized. All GRPC services 
  32.  // should return error before the server is initialized. 
  33.  initialized atomic.Bool 
  34.  // cniNetConfMonitor is used to reload cni network conf if there is 
  35.  // any valid fs change events from cni network conf dir. 
  36.  cniNetConfMonitor *cniNetConfSyncer 
  37.  // baseOCISpecs contains cached OCI specs loaded via `Runtime.BaseRuntimeSpec` 
  38.  baseOCISpecs map[string]*oci.Spec 

我們知道 Kubernetes 的一個(gè)運(yùn)作的機(jī)制是面向終態(tài)的,在每一次調(diào)協(xié)的循環(huán)中,Kubelet 會(huì)向 apiserver 獲取調(diào)度到本 Node 的 Pod 的數(shù)據(jù),再做一個(gè)面向終態(tài)的處理,以達(dá)到我們預(yù)期的狀態(tài)。

循環(huán)的第一步,首先通過 List 接口拿到容器的狀態(tài)。確保有鏡像,如果沒有鏡像則 pull 鏡像再通過 Sandbox 和 Container 接口來創(chuàng)建容器。需要注意的是,我們的 CNI(容器網(wǎng)絡(luò)接口)也是在 CRI 進(jìn)行操作的,因?yàn)槲覀冊(cè)趧?chuàng)建 Pod 的時(shí)候需要同時(shí)創(chuàng)建網(wǎng)絡(luò)資源然后注入到 Pod 中(PS:CNI包含在創(chuàng)建Pod 這個(gè)動(dòng)作里)。接下來就是我們的容器和鏡像。我們通過具體的容器創(chuàng)建引擎來創(chuàng)建一個(gè)具體的容器。

執(zhí)行流程為:

  • Kubelet 通過 CRI runtime service API 調(diào)用 CRI plugin 創(chuàng)建 pod
  • CRI 通過 CNI 創(chuàng)建 pod 的網(wǎng)絡(luò)配置和 namespace
  • CRI使用 containerd 創(chuàng)建并啟動(dòng) pause container (sandbox container) 并且把這個(gè) container 置于 pod 的 cgroups/namespace
  • Kubelet 接著通過 CRI image service API 調(diào)用 CRI plugin, 獲取容器鏡像
  • CRI 通過 containerd 獲取容器鏡像
  • Kubelet 通過 CRI runtime service API 調(diào)用 CRI, 在 pod 的空間使用拉取的鏡像啟動(dòng)容器
  • CRI 通過 containerd 創(chuàng)建/啟動(dòng) 應(yīng)用容器, 并且把 container 置于 pod 的 cgroups/namespace. Pod 完成啟動(dòng)。

總結(jié)

發(fā)現(xiàn) CRI 只是服務(wù)于 Kubernetes 的,而且它呈現(xiàn)向上匯報(bào)的狀態(tài)。它是幫助 Kubernetes 的,它不幫助OCI的。所以說當(dāng)你去做這個(gè)集成時(shí)候,你會(huì)發(fā)現(xiàn)尤其對(duì)于 VM gVisor\KataContainer 來說,它與 CRI 的很多假設(shè)或者是 API 的寫法上是不對(duì)應(yīng)的。所以你的集成工作會(huì)比較費(fèi)勁,這是一個(gè)不 match 的狀態(tài)。

最后一個(gè)就是我們維護(hù)起來非常困難,因?yàn)橛捎谟辛? CRI 之后,比如 RedHat 擁有自己的 CRI 實(shí)現(xiàn)叫 cri-o,他們和 containerd 在本質(zhì)上沒有任何區(qū)別,跑到最后都是靠 runC 起容器,為什么還需要cri-o這種東西?

我們不知道,如果我想使用Kata container與containerd多運(yùn)行時(shí)的話,我需要給他們兩個(gè)分別寫兩部分的一體化把 Kata 集成進(jìn)去。這就很麻煩,就意味著我有 100 種這樣的 CRI ,我就要寫 100 個(gè)shim去集成,而且他們的功能全部都是重復(fù)的。

所以這就產(chǎn)生了Containerd ShimV2的這樣的shim來解決這個(gè)問題。我們下回分解。

reference

https://time.geekbang.org/column/article/71499?utm_campaign=guanwang&utm_source=baidu-ad&utm_medium=ppzq-pc&utm_content=title&utm_term=baidu-ad-ppzq-title

https://blog.frognew.com/2021/04/relearning-container-02.html

https://github.com/kubernetes-sigs/cri-tools/blob/master/docs/crictl.md

https://developer.aliyun.com/article/679993

本文轉(zhuǎn)載自微信公眾號(hào)「運(yùn)維開發(fā)故事」

責(zé)任編輯:姜華 來源: 運(yùn)維開發(fā)故事
相關(guān)推薦

2021-12-23 07:58:06

Kubelet容器運(yùn)行

2024-07-15 18:20:18

2023-09-07 07:17:01

KubernetesCRI標(biāo)準(zhǔn)

2015-08-06 15:13:49

runtimeIOS開發(fā)

2023-01-10 13:48:50

ContainerdCRI源碼

2014-07-29 11:16:07

2015-03-25 13:42:42

ZAKER

2021-08-30 09:44:47

Kubelet機(jī)制驅(qū)逐

2010-08-10 17:01:48

FlexJavaScript

2017-10-18 12:22:43

NativeHybirdJavaScript

2023-08-29 08:20:35

Kubernete跨云容器

2015-09-07 09:53:02

Objective-CRuntime

2009-06-10 21:46:02

JavaScript與

2021-09-07 07:48:37

kubeletKubernetesContainerd

2014-09-16 11:17:36

AndroidService交互方式

2015-08-17 09:46:15

UIjs

2009-07-01 14:42:30

JSP和XML

2011-06-13 09:04:39

QT Flash 交互

2022-02-16 20:04:08

容器KubernetesShim

2021-10-22 00:09:16

Kubernetes容器接口
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)