Kubernetes 容器运行时接口 (CRI) 详解
Kubernetes 作为业界领先的容器编排平台,其核心组件 Kubelet 需要与节点上的容器运行时进行交互,以管理 Pod 和容器的生命周期。为了实现 Kubernetes 与不同容器运行时的解耦,社区定义了容器运行时接口(Container Runtime Interface, CRI)。本文将深入探讨 CRI 的概念、容器运行时的分层架构、OCI 标准以及主流容器运行时的对比。
1. 为什么需要 CRI?
在 CRI 标准出现之前,Kubernetes 代码直接与 Docker Engine 交互。这种紧耦合的方式存在以下问题:
- 限制了运行时的选择: Kubernetes 难以支持 Docker 之外的其他容器运行时。
- 增加了维护成本: Kubernetes 核心代码需要维护与特定运行时相关的逻辑。
- 阻碍了创新: 新的容器运行时技术难以快速集成到 Kubernetes 生态中。
为了解决这些问题,Kubernetes 引入了 CRI。CRI 定义了一套标准的 gRPC 接口,Kubelet 通过这套接口与任何实现了 CRI 的容器运行时进行通信,从而实现了 Kubernetes 平台与具体容器运行时实现的解耦。
2. CRI (Container Runtime Interface) 定义
CRI 本质上是 Kubernetes 定义的一组 gRPC API 规范。它包含两个主要的服务:
- ImageService: 负责管理容器镜像,如拉取、查看和删除镜像。
- RuntimeService: 负责管理 Pod 和容器的生命周期,如创建、启动、停止和删除 PodSandbox(Pod 的隔离环境)和容器。
Kubelet 作为 Kubernetes 在每个 Node 上的代理,充当 CRI 的客户端。它根据 API Server 的指令,调用 CRI 接口,要求容器运行时执行相应的操作(例如,为新的 Pod 创建 PodSandbox,拉取所需的镜像,然后在 PodSandbox 内创建并启动容器)。

图 1: Kubelet 通过 CRI 与容器运行时交互
3. 容器运行时详解
容器运行时是实际负责运行容器的软件。为了更好地理解其工作方式,通常将其分为两个层级:高级运行时和低级运行时。
3.1 高级运行时 (High-level Runtime)
- 职责: 主要负责容器镜像的管理(下载、存储、分发)、容器生命周期的管理(通过调用低级运行时),以及实现 CRI 接口与 Kubelet 通信。它们提供了一个更上层的抽象,隐藏了与操作系统内核交互的细节。
- 代表:
containerd
、CRI-O
。(注意:Docker Engine 本身包含高级运行时功能,但在 Kubernetes CRI 场景下,通常通过dockershim
(已废弃)或直接使用containerd
)。 - 工作流程(以 containerd 为例):
- Kubelet 通过 CRI gRPC 接口向 containerd 发送请求(例如,
RunPodSandbox
)。 - containerd 接收请求,进行必要的准备工作(如创建网络命名空间等)。
- 如果需要创建容器(
CreateContainer
请求),containerd 会确保镜像存在(调用 ImageService 拉取),然后准备容器的根文件系统(rootfs)和 OCI 运行时规范文件(config.json
)。 - containerd 调用低级运行时(如 runC)来创建和运行容器进程。
- containerd 持续监控容器状态,并通过 CRI 接口向 Kubelet 汇报。
- Kubelet 通过 CRI gRPC 接口向 containerd 发送请求(例如,
3.2 低级运行时 (Low-level Runtime)
- 职责: 直接与操作系统内核交互,负责创建和管理容器的隔离环境。它专注于容器进程的实际运行,不关心镜像管理或 API 交互。
- 标准: 遵循 OCI(Open Container Initiative)运行时规范。该规范定义了容器配置(
config.json
)和状态,以及低级运行时需要实现的命令行接口(如create
,start
,kill
,delete
等)。 - 代表:
runC
(最常用)、crun
、kata-runtime
(用于 Kata Containers 安全容器)等。 - 工作流程(以 runC 为例):
- 接收来自高级运行时的指令,通常包括容器的根文件系统路径和 OCI 配置文件(
config.json
)。 - 根据
config.json
中的定义,利用 Linux 内核特性创建容器的隔离环境:- 命名空间 (Namespaces): PID, Net, IPC, Mount, UTS, User 等,隔离进程视图、网络、进程间通信、挂载点、主机名和用户。
- 控制组 (Cgroups): 限制容器可使用的资源(CPU、内存、磁盘 I/O 等)。
- 文件系统: 设置容器的根文件系统(rootfs),通常使用
chroot
或pivot_root
。 - 能力 (Capabilities): 限制容器内进程的特权。
- Seccomp/AppArmor: 应用安全策略。
- 在创建好的隔离环境中启动容器指定的初始进程。
- 将容器进程的 PID 等信息返回给高级运行时。
- 接收来自高级运行时的指令,通常包括容器的根文件系统路径和 OCI 配置文件(
4. OCI (Open Container Initiative)
OCI 是一个旨在制定容器格式和运行时开放标准的组织,由 Linux 基金会托管。它的目标是促进容器生态系统的互操作性,避免厂商锁定。OCI 定义了两个核心规范:
- 镜像规范 (Image Specification): 定义了容器镜像的格式,包括镜像层、manifest 文件和配置文件的结构。这使得符合 OCI 标准的镜像可以在不同的容器引擎和运行时之间共享和使用。
- 运行时规范 (Runtime Specification): 定义了容器的配置 (
config.json
)、执行环境和生命周期管理。它规定了如何从一个 OCI 文件系统包(filesystem bundle,包含 rootfs 和config.json
)运行一个容器。低级运行时(如 runC)是该规范的具体实现。
CRI 和 OCI 是相辅相成的:CRI 定义了 Kubelet 与高级运行时之间的接口,而 OCI 定义了高级运行时与低级运行时之间的接口以及容器镜像的格式。
5. 主流 CRI 运行时对比
目前,Kubernetes 生态中最常用的 CRI 运行时主要是 containerd
和 CRI-O
。Docker Engine 通过 dockershim
的方式已被 Kubernetes 废弃(自 v1.24 起移除)。

图 2: Docker, containerd, CRI-O 架构对比
1. 架构对比
- Docker (via dockershim): 架构链路较长(Kubelet -> dockershim -> Docker Engine -> containerd -> runC)。
dockershim
作为 Kubelet 和 Docker Engine 之间的适配层,增加了复杂性和潜在的故障点。由于dockershim
已被移除,直接使用 Docker Engine 作为 Kubernetes 运行时的方式不再被推荐或支持。 - Containerd: 架构相对简洁(Kubelet -> containerd (CRI Plugin) -> runC)。
containerd
本身是一个专注于容器核心功能的守护进程,通过内置的 CRI 插件直接实现了 CRI 接口。它是 CNCF 的毕业项目,社区活跃,被广泛应用于生产环境,也是许多云厂商托管 Kubernetes 服务的默认运行时。 - CRI-O: 架构最为精简(Kubelet -> CRI-O -> runC)。CRI-O 是专门为 Kubernetes 设计的轻量级 CRI 实现,其唯一目标就是满足 CRI 规范。它不包含构建镜像等额外功能,紧密跟随 Kubernetes 的发布周期。
2. 关键组件
- dockershim: Kubernetes 为了兼容 Docker 而开发的 CRI 实现,现已废弃。
- containerd: 包含 CRI 插件,提供完整的容器生命周期管理、镜像管理等功能。通过
containerd-shim
进程来管理具体的容器实例(runC 进程),即使 containerd 主进程重启,运行中的容器也不会受影响。 - CRI-O: 轻量级 CRI 守护进程。使用
conmon
工具来监控每个容器,conmon
负责处理容器的日志、TTY 和退出码等,并将容器进程与 CRI-O 主进程解耦。 - OCI Runtime (runC): 所有这三种运行时最终都依赖符合 OCI 规范的低级运行时(通常是 runC)来创建和运行容器。
3. Kubernetes 集成
- Docker: 依赖外部的
dockershim
组件,已被移除。 - Containerd: 通过内置的 CRI 插件原生集成。是 Kubernetes 当前推荐和广泛使用的运行时。
- CRI-O: 原生实现 CRI 接口,与 Kubernetes 紧密集成。
4. 性能考虑
虽然架构不同,但在典型的 Pod 创建/销毁等操作上,containerd
和 CRI-O
的性能通常优于通过 dockershim
的 Docker,因为它们的调用链更短。实际性能差异可能因具体负载和环境而异。

图 3: 不同运行时在 Pod 启动延迟等方面的性能比较(示例性,具体数据可能变化)
6. 总结
CRI 作为 Kubernetes 与容器运行时之间的标准接口,极大地促进了 Kubernetes 生态的开放性和灵活性。通过将 Kubelet 与具体的运行时实现解耦,用户可以根据需求选择最合适的容器运行时。containerd
和 CRI-O
作为遵循 CRI 和 OCI 标准的现代容器运行时,凭借其简洁的架构、高效的性能和良好的社区支持,已成为 Kubernetes 环境下的主流选择。理解 CRI、OCI 以及不同运行时的特点,对于深入掌握 Kubernetes 的工作原理和进行技术选型至关重要。