Container Runtime Interface(CRI)
Container Runtime Interface(CRI)是Kubernetes与容器运行时交互的接口。
在 CRI 出现之前(Kubernetes v1.5 之前),Docker 作为第一个容器运行时,Kubelet 通过内嵌的 dockershim 操作 Docker API 来操作容器,达到一个面向终态的效果。在这之后,又出现了一种新的容器运行时rkt,它也想要被Kubernetes支持,当时它也合到了 Kubelet 的代码之中。但越来越多的容器运行时出现,采用这种方式会使得Kubernetes的代码越来越复杂、难以维护。
因此将 Kubelet 代码与具体的容器运行时的实现代码解耦开,只要实现了这样一套接口,就能接入到 Kubernetes 的体系中,于是就有了Container Runtime Interface (CRI)。
CRI接口的通信协议是 gRPC,它的性能优于 http/REST 模式。gRPC 不需要手写客户端代码和服务端代码,能够自动生成通信协议代码。
在引入了 CRI 接口之后,Kubelet 的架构如上图所示。
跟容器最相关的一个 Manager 是 Generic Runtime Manager,就是一个通用的运行时管理器。我们可以看到目前dockershim还是存在于 Kubelet 的代码中的,它是当前性能最稳定的一个容器运行时的实现。remote 指的就是 CRI 接口。CRI 接口主要包含两个部分:
- 一个是 CRI Server,即通用的比如说创建、删除容器这样的接口;
- 另外一个是流式数据的接口 Streaming Server,比如 exec、port-forward 这些流式数据的接口。
这里需要注意的是,我们的 CNI(容器网络接口)也是在 CRI 进行操作的,因为我们在创建 Pod 的时候需要同时创建网络资源然后注入到 Pod 中。接下来就是我们的容器和镜像。我们通过具体的容器引擎来创建一个具体的容器。
给大家介绍一下 CRI 接口的设计。我们知道 Kubernetes 的一个运作的机制是面向终态的,在每一次调协的循环中,Kubelet 会向 apiserver 获取调度到本 Node 的 Pod 的数据,再做一个面向终态的处理,以达到我们预期的状态。
循环的第一步,首先通过 List 接口拿到容器的状态,再通过 Sandbox 和 Container 接口来创建容器,另外还有镜像接口用来拉取容器镜像。CRI 描述了 Kubelet 期望的容器运行时行为,主要就是我们刚刚所说的 3 个部分。
比方说我们通过 kubectl 命令来运行一个 Pod,那么 Kubelet 就会通过 CRI 执行以下操作:
- 首先调用 RunPodSandbox 接口来创建一个 Pod 容器,Pod 容器是用来持有容器的相关资源的,比如说网络空间、PID空间、进程空间等资源;
- 然后调用 CreatContainer 接口在 Pod 容器的空间创建业务容器;
- 再调用 StartContainer 接口启动容器,相对应的销毁容器的接口为 StopContainer 与 RemoveContainer。