引言
这是对于容器运行时系列文章的第四篇,也是最初一篇。从第 1 篇开始曾经有一段时间了,在那篇文章中,我概述了容器运行时,并探讨了低级和高级运行时之间的区别。在第 2 篇文章中,我具体介绍了低级容器运行时,并构建了一个简略的低级运行时。在第 3 篇文章中,我降级了技术栈,介绍了高级容器运行时。
Kubernetes 运行时反对容器运行时接口(CRI)的高级容器运行时。CRI 在 Kubernetes 1.5 中引入,并充当 kubelet 和容器运行时之间的桥梁,冀望与 Kubernetes 集成的高级容器运行时实现 CRI。预计运行时将解决镜像治理并反对 Kubernetes Pod,并治理各个容器,因而依据咱们在第 3 篇文章中的定义,Kubernetes 运行时必须是高级运行时。低级运行时短少一些必要的个性。因为第 3 篇文章介绍了无关高级容器运行时的所有内容,因而在本文中,我将重点介绍 CRI,并介绍一些反对 CRI 的运行时。
为了更多地理解 CRI,值得看一下 Kubernetes 架构。Kubelet 是一个位于 Kubernetes 集群中每个工作节点上的代理。Kubelet 负责管理其节点的容器工作负载,当波及到理论运行工作负载时,kubelet 应用 CRI 与在同一节点上运行的容器运行时进行通信。这样,CRI 仅仅是一个形象层或 API,它使您能够将容器运行时的实现独自拆分进去,而不用将其内置到 kubelet 中。
CRI 运行时示例
这里列出一些可与 Kubernetes 一起应用的 CRI 运行时。
containerd
containerd 是我在第 3 篇文章中提到的高级运行时。containerd 可能是以后最风行的 CRI 运行时,它将 CRI 实现作为默认状况下启用的插件。默认状况下,它在 unix socket 上开启监听,因而通过如下配置连贯到容器:
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
Docker runtime
第一个实现了对 CRI
的反对,并且作为 kubelet
和Docker
之间的一个 shim
而实现。从那以后,Docker
已将其许多性能合成为容器,当初通过容器反对 CRI
。装置最新版本的Docker
时,将同时装置 containerd
和CRI
间接与 containerd
通信。因而,Docker
自身并不需要反对 CRI
。因而,依据你的理论状况,能够间接装置容器或者通过Docker
来装置。
cri-o
cri-o
是一个轻量级的 CRI
运行时,它是 Kubernetes
特定的高级运行时。它反对 OCI
兼容镜像的治理,并从任何 OCI
兼容镜像注册表中提取。它反对 runc
和Clear Containers
作为低级运行时,在实践上反对其余 OCI 兼容的低级运行时,但依赖于与 runc OCI
命令行界面的兼容性,因而在实践中它不如容器的 shim API
灵便。cri-o
的 endpoints
默认状况下位于/var/run/crio/crio.sock
,因而能够通过如下形式配置crictl
:
cat <<EOF | sudo tee /etc/crictl.yaml
runtime-endpoint: unix:///var/run/crio/crio.sock
EOF
CRI 标准
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}
}