CRI
简介
Kubernetes 运行时是支持容器运行时接口(CRI) 的高级容器运行时,CRI 在 Kubernetes 1.5 中引入,并且充当 kubelet 和容器运行时之间的桥梁,并且希望像 Docker、containerd 这样的高级容器运行时实现 CRI 接口。
预期容器运行时支持镜像管理并且支持 Kubernetes Pod,并管理各个容器,所以从要求中也能看出来 Kubernetes 运行时要求的必须是 high-level 运行时,low-level 运行时缺少必要的功能。
为了了解更多关于 CRI 的知识,有必要了解 Kubernetes 的架构,Kubelet 是一个位于 Kubernetes 集群中每个工作节点上的代理,Kubelet 负责管理节点的容器工作负载,在实际运行中,kubelet 使用 CRI 在同一节点上运行的容器运行时进行通信,有个 CRI 这个抽象层,我们可以随时将实际的容器运行时切换到其他实现了 CRI 接口的容器运行时,而不必局限某一种容器运行时,也不必内置于 kubelet 中。
kubernetes 中关于 CRI 架构图
CRI 运行时例子
containerd
在前面的文章中提到过 containerd,它是一个 high-level 运行时,containerd 可能是现在最流行的容器运行时,他将 CRI 作为一个插件实现并且默认开启,它默认使用 unix 套接字进行监听,你可以像如下这样使用 crictl 连接 containerd:
1 | cat <<EOF | sudo tee /etc/crictl.yaml |
这是一个有趣的高级运行时,因为它从 1.2 开始通过 “runtime handler” 支持多个 low-level 运行时,runtime handler 通过 CRI 规范进行交互,基于该 runtime handler 的 containerd 将通过 shim 来启动容器,它可以使用除了 runc 以外的其他 low-level 运行时来运行容器。比如像 gVisor、Kata Containers 或者 Nabla Containers。runtime handler 在 Kubernetes 1.12 中公开了一个 alpha 特性的 API 对象 RuntimeClass,更多的 containerd‘s shim 概念点击 这里。
Docker
Docker 也是第一个实现 CRI 的,在 kubelet 和 Docker 之间通过 shim 来实现 CRI。同时 Docker 也将很多的功能剥离到 containerd 中实现了,现在也是通过 containerd 来支持 CRI 了,安装 Docker 的同时会同时安装 containerd,CRI 可以直接和 containerd 交互,因此 Docker 其实已经不再需要支持 CRI 了,因为默认集成了 containerd,所以我们可以直接安装 Docker 来间接安装 containerd。
cri-o
cri-o 是一个轻量级的容器运行时,它是 Kubernetes 特定的高级容器运行时,它支持 OCI compatible images 管理和从 OCI compatible images registry 中拉取镜像,它支持 runc 和 Clear Containers 作为 low-level 运行时。理论上支持其他 OCI 兼容的 low-level 运行时,但是依赖于 runc OCI command line interface 的兼容性,因此在实践中不如 containerd 的 shim API 灵活。
CRI 规范
CRI 是 protocol buffers 和 gRPC API。该规范是在kubelet 下 Kubernetes 仓库中的 protobuf file 中定义的。
CRI 中定义了几种远程调用(RPC)和消息类型。下面这几种 RPC 方法:
- ImageService.PullImage:拉取镜像
- RuntimeService.RunPodSandbox:创建 pod
- RuntimeService.CreateContainer:创建容器
- RuntimeService.StartContainer:启动容器
- RuntimeService.StopContainer:停止容器
例如,新建的 Kubernetes Pod 使用 CRI 的典型交互方式像下面这样(以我自己的伪 gRPC 方式,每个 RPC 都会得到一个更大的请求对象为了简单我对其进行了简化),RunPodSandbox 和 CreateContainer RPC 在他们的响应中返回 ID,并且将这个 ID 在后续请求中使用。
1 | ImageService.PullImage({image: "image1"}) |
我们可以直接使用 crictl 工具直接和 CRI 运行时进行交互,crictl 可以让我们直接从命令行使用 gRPC 发送消息到 CRI 运行时。我们可以使用 crictl 来调试和测试 CRI 运行时,而不用启动成熟的 kubelet 或者 Kubernetes 集群。可以通过 github 上的 cri-tools 版本页面下载 crictl 二进制文件来获得它。
可以通过配置 /etc/crictl.yaml 文件来配置 crictl。在这里你可以指定运行时的 gRPC 端点为 Unix 套接字文件 (unix:///path/to/file) 或者一个 TCP 端点(tcp://
1 | cat <<EOF | sudo tee /etc/crictl.yaml |
或者你可以在每个命令指定运行时的端点,比如下面这样:
1 | crictl --runtime-endpoint unix:///run/containerd/containerd.sock … |
我们使用 crictl 创建一个只包含一个 container 的 Pod,首先我们应该先使用 crictl 拉取一个镜像,如果本地没有存储镜像就不能够启动。
1 | sudo crictl pull nginx |
接下来创建一个 Pod 创建请求,可以将其作为 json 文件进行操作。
1 | cat <<EOF | tee sandbox.json |
接下来创建 pod sandbox,我们将把 sandbox 的 id 存储为变量 SANDBOX_ID
1 | SANDBOX_ID=$(sudo crictl runp --runtime runsc sandbox.json) |
接下来我们可以使用 JSON 文件创建容器创建请求。
1 | cat <<EOF | tee container.json |
然后我们可以在前面创建的 Pod 中创建启动容器。
1 | { |
也可以查看正在运行的 pod 或者 container
1 | sudo crictl inspectp ${SANDBOX_ID} |
使用如下的命令可以删除 container 和 pod
1 | { |