這是關於容器運行時系列文章的第四篇,也是最後一篇。 從第1篇開始已經有一段時間了,在那篇文章中,我概述了容器運行時,並討論了低級和高級運行時之間的區別。 在第2篇文章中,我詳細介紹了低級容器運行時,並構建了一個簡單的低級運行時。 在第3篇文章中,我升級了技術棧,介紹了高級容器運行時。linux
Kubernetes運行時支持容器運行時接口(CRI)的高級容器運行時。 CRI在Kubernetes 1.5中引入,並充當kubelet和容器運行時之間的橋樑,指望與Kubernetes集成的高級容器運行時實現CRI。 預計運行時將處理鏡像管理並支持Kubernetes Pod,並管理各個容器,所以根據咱們在第3篇文章中的定義,Kubernetes運行時必須是高級運行時。低級運行時缺乏一些必要的特性。 因爲第3篇文章介紹了有關高級容器運行時的全部內容,所以在本文中,我將重點介紹CRI,並介紹一些支持CRI的運行時。nginx
爲了更多地瞭解CRI,值得看一下Kubernetes架構。 Kubelet是一個位於Kubernetes集羣中每一個工做節點上的代理。 Kubelet負責管理其節點的容器工做負載,當涉及到實際運行工做負載時,kubelet使用CRI與在同一節點上運行的容器運行時進行通訊。 這樣,CRI僅僅是一個抽象層或API,它使您能夠將容器運行時的實現單獨拆分出來,而沒必要將其內置到kubelet中。
git
這裏列出一些可與Kubernetes一塊兒使用的CRI運行時。github
containerd是我在第3篇文章中提到的高級運行時。containerd多是當前最流行的CRI運行時,它將CRI實現做爲默認狀況下啓用的插件。 默認狀況下,它在unix socket上開啓監聽,所以經過以下配置鏈接到容器:json
cat <<EOF | sudo tee /etc/crictl.yaml runtime-endpoint: unix:///run/containerd/containerd.sock EOF
這是一個有趣的高級運行時,由於它在1.2版開始經過稱爲「runtime handler
」的東西支持多個低級運行時。 runtime handler
是經過CRI
中的一個字段傳遞的,基於該運行時處理程序的容器將運行一個名爲shim
的應用程序以啓動容器。 它能夠用於使用除runc
以外的低級運行時來運行容器,例如gVisor
,Kata Containers
或Nabla Containers
。 runtime handler
在k8s 1.12 alpha版本的RuntimeClass object
中正式被提交,這裏有更多關於containerd's shim
的概念介紹。架構
Docker runtime
第一個實現了對CRI
的支持,而且做爲kubelet
和Docker
之間的一個shim
而實現。 從那之後,Docker
已將其許多功能分解爲容器,如今經過容器支持CRI
。 安裝最新版本的Docker
時,將同時安裝containerd
和CRI
直接與containerd
通訊。 所以,Docker
自己並不須要支持CRI
。 所以,根據你的實際狀況,能夠直接安裝容器或者經過Docker
來安裝。socket
cri-o
是一個輕量級的CRI
運行時,它是Kubernetes
特定的高級運行時。 它支持OCI
兼容鏡像的管理,並從任何OCI
兼容鏡像註冊表中提取。 它支持runc
和Clear Containers
做爲低級運行時,在理論上支持其餘OCI兼容的低級運行時,但依賴於與runc OCI
命令行界面的兼容性,所以在實踐中它不如容器的shim API
靈活。cri-o
的endpoints
默認狀況下位於/var/run/crio/crio.sock
,所以能夠經過以下方式配置crictl
:tcp
cat <<EOF | sudo tee /etc/crictl.yaml runtime-endpoint: unix:///var/run/crio/crio.sock EOF
CRI
是一個protocol buffers
和gRPC API
。 該規範是在kubelet
下的Kubernetes
鏡像倉庫中的protobuf
文件中定義的。 CRI
定義了幾種遠程過程調用(RPCs
)和消息類型。 RPCs
用於「鏡像」(ImageService.PullImage
),「建立容器」(RuntimeService.RunPodSandbox
),「建立容器」(RuntimeService.CreateContainer
),「啓動容器」(RuntimeService.StartContainer
),「中止容器」等操做 (RuntimeService.StopContainer
)等工具
例如,經過CRI
啓動一個新的Kubernetes Pod的典型交互看起來相似於如下內容(以我本身的僞gRPC
形式,每一個RPC
都會獲得一個更大的請求對象,爲簡便起見,我對其進行了簡化)。 RunPodSandbox
和CreateContainer RPC
在其響應中返回ID,這些ID在後續請求中使用:測試
ImageService.PullImage({image: "image1"}) ImageService.PullImage({image: "image2"}) podID = RuntimeService.RunPodSandbox({name: "mypod"}) id1 = RuntimeService.CreateContainer({ pod: podID, name: "container1", image: "image1", }) id2 = RuntimeService.CreateContainer({ pod: podID, name: "container2", image: "image2", }) RuntimeService.StartContainer({id: id1}) RuntimeService.StartContainer({id: id2})
使用crictl
工具能夠直接與CRI
運行時進行交互,能夠直接從命令行將gRPC消
息發送到CRI
運行時並用它來調試和測試CRI
實現,而無需啓動kubelet
或Kubernetes集羣,能夠從GitHub上cri-tools版本頁面下載crictl二進制文件
來獲取相關文件。
能夠經過在/etc/crictl.yaml
下建立配置文件來配置crictl
。 在這裏,你應該將運行時的gRPC
端點指定爲Unix socket
文件(unix:///path/to/file
)或TCP端點(tcp://<host>:<port>
)。 在本例中將使用containerd
:
cat <<EOF | sudo tee /etc/crictl.yaml runtime-endpoint: unix:///run/containerd/containerd.sock EOF
或者能夠在每次命令行執行時指定runtime endpoint
:
crictl --runtime-endpoint unix:///run/containerd/containerd.sock …
使用crictl
運行一個單容器pod
, 首先,告訴運行時pull
所需的nginx
鏡像,由於沒有本地存儲的鏡像就沒法啓動容器。
sudo crictl pull nginx
接下來建立一個Pod
的建立請求,可使用JSON
文件進行操做。
cat <<EOF | tee sandbox.json { "metadata": { "name": "nginx-sandbox", "namespace": "default", "attempt": 1, "uid": "hdishd83djaidwnduwk28bcsb" }, "linux": { }, "log_directory": "/tmp" } EOF
而後建立pod sandbox
,將sandbox
的ID存儲爲SANDBOX_ID
。
SANDBOX_ID=$(sudo crictl runp --runtime runsc sandbox.json)
接下來,在JSON
文件中建立容器的建立請求。
cat <<EOF | tee container.json { "metadata": { "name": "nginx" }, "image":{ "image": "nginx" }, "log_path":"nginx.0.log", "linux": { } } EOF
而後,在前面建立的Pod
中建立並啓動容器。
{ CONTAINER_ID=$(sudo crictl create ${SANDBOX_ID} container.json sandbox.json) sudo crictl start ${CONTAINER_ID} }
檢查正在運行的pod
以及正在運行的容器:
sudo crictl inspectp ${SANDBOX_ID} sudo crictl inspect ${CONTAINER_ID}
經過中止並刪除容器進行清理:
{ sudo crictl stop ${CONTAINER_ID} sudo crictl rm ${CONTAINER_ID} }
而後中止並刪除Pod
:
{ sudo crictl stopp ${SANDBOX_ID} sudo crictl rmp ${SANDBOX_ID} }