Container Runtimes(四): Kubernetes Container Runtimes & CRI

引言

這是關於容器運行時系列文章的第四篇,也是最後一篇。 從第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中。
image.pnggit

CRI運行時示例

這裏列出一些可與Kubernetes一塊兒使用的CRI運行時。github

containerd

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以外的低級運行時來運行容器,例如gVisorKata ContainersNabla Containersruntime handler在k8s 1.12 alpha版本的RuntimeClass object中正式被提交,這裏有更多關於containerd's shim的概念介紹。架構

Docker

Docker runtime第一個實現了對CRI的支持,而且做爲kubeletDocker之間的一個shim而實現。 從那之後,Docker已將其許多功能分解爲容器,如今經過容器支持CRI。 安裝最新版本的Docker時,將同時安裝containerdCRI直接與containerd通訊。 所以,Docker自己並不須要支持CRI。 所以,根據你的實際狀況,能夠直接安裝容器或者經過Docker來安裝。socket

cri-o

cri-o是一個輕量級的CRI運行時,它是Kubernetes特定的高級運行時。 它支持OCI兼容鏡像的管理,並從任何OCI兼容鏡像註冊表中提取。 它支持runcClear Containers做爲低級運行時,在理論上支持其餘OCI兼容的低級運行時,但依賴於與runc OCI命令行界面的兼容性,所以在實踐中它不如容器的shim API靈活。
cri-oendpoints默認狀況下位於/var/run/crio/crio.sock,所以能夠經過以下方式配置crictl:tcp

cat <<EOF | sudo tee /etc/crictl.yaml
runtime-endpoint: unix:///var/run/crio/crio.sock
EOF

CRI規範

CRI是一個protocol buffersgRPC API。 該規範是在kubelet下的Kubernetes鏡像倉庫中的protobuf文件中定義的。 CRI定義了幾種遠程過程調用(RPCs)和消息類型。 RPCs用於「鏡像」(ImageService.PullImage),「建立容器」(RuntimeService.RunPodSandbox),「建立容器」(RuntimeService.CreateContainer),「啓動容器」(RuntimeService.StartContainer),「中止容器」等操做 (RuntimeService.StopContainer)等工具

例如,經過CRI啓動一個新的Kubernetes Pod的典型交互看起來相似於如下內容(以我本身的僞gRPC形式,每一個RPC都會獲得一個更大的請求對象,爲簡便起見,我對其進行了簡化)。 RunPodSandboxCreateContainer 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}
}
相關文章
相關標籤/搜索