深入理解 OCI 和 CRI它们之间的关系
OCI 是一个由 Linux 基金会在 2015 年主导成立的治理委员会和项目。它的诞生背景是,当时 Docker 公司如日中天,几乎成为了容器的代名词。为了避免容器技术被单一厂商(Docker)锁定,并促进容器生态的健康发展,多家行业巨头(包括 Google, Red Hat, IBM, Microsoft, Amazon 等)联合发起了 OCI。核心目标: 制定容器格式的开放行业标准。让任何人
一、OCI (Open Container Initiative / 开放容器倡议)
1. 是什么?
OCI 是一个由 Linux 基金会在 2015 年主导成立的治理委员会和项目。它的诞生背景是,当时 Docker 公司如日中天,几乎成为了容器的代名词。为了避免容器技术被单一厂商(Docker)锁定,并促进容器生态的健康发展,多家行业巨头(包括 Google, Red Hat, IBM, Microsoft, Amazon 等)联合发起了 OCI。
核心目标: 制定容器格式的开放行业标准。让任何人都可以基于这些标准来创建符合规范的容器工具,从而实现不同工具之间的互操作性。
你可以把 OCI 想象成**“容器世界的 ISO 国际标准化组织”**。它不生产任何软件,只负责制定“规则”。
2. 核心规范
OCI 主要发布了两个关键的规范(Specification),它们构成了现代容器技术的基石:
a. 运行时规范
- 定义了什么? 定义了一个**“解压后的、可运行的容器文件系统包”** 应该是什么样子,以及如何运行它。
- 核心内容:它规定了一个**
config.json
** 文件。这个文件是 OCI 运行时规范的灵魂,它描述了运行一个容器所需的所有元数据,包括:- 进程信息:要运行的命令、参数、环境变量、工作目录。
- 根文件系统:指向容器根目录的路径。
- 用户:以哪个用户身份运行进程。
- 资源限制:CPU、内存的 cgroups 配置。
- 安全配置:Linux capabilities、seccomp 规则、namespaces 配置。
- 挂载点:需要挂载的
proc
,sysfs
,devpts
等伪文件系统。
- 作用:任何一个符合 OCI Runtime 规范的工具(称为 OCI Runtime),只要拿到这个
config.json
和对应的文件系统,就能以完全相同的方式启动这个容器。这实现了运行时的标准化。 - 著名实现:
runc
:这是 OCI Runtime 规范的参考实现,也是目前使用最广泛的容器运行时。它由 Docker 贡献出来,现在是 CNCF 的毕业项目。Docker, containerd, CRI-O 底层默认都是用runc
来实际启动容器的。crun
:一个用 C 语言编写的、更轻量级的 OCI Runtime。kata-runtime
:一个基于轻量级虚拟机的 OCI Runtime,提供了更强的安全隔离。
b. 镜像规范
- 定义了什么? 定义了如何创建和打包一个标准的、可移植的容器镜像。
- 核心内容:它规定了一个 OCI 镜像由以下几部分组成:
- 文件系统层:镜像不是单一的巨大文件,而是由一系列只读的文件系统层 组成。每一层代表了对上一层所做的变更(添加、删除、修改文件)。这种分层设计使得镜像构建、存储和分发都非常高效(可以复用层)。
- 清单:一个 JSON 文件,可以理解为镜像的“总目录”或“清单”。它引用了镜像的配置文件和所有相关的文件系统层。
- 配置文件:一个 JSON 文件,包含了镜像的元数据,比如架构(amd64, arm64)、操作系统、暴露的端口、环境变量,以及最重要的——运行时需要执行的
config.json
的模板。 - 索引:当同一个镜像有多个版本(比如支持不同 CPU 架构的多架构镜像)时,Index 文件会列出所有可用的 Manifest。
- 作用:任何一个符合 OCI Image 规范的工具,都可以创建、打包、解包和分发 OCI 镜像。这实现了镜像格式的标准化。我们现在使用的 Docker 镜像格式,已经完全兼容 OCI 镜像规范。
- 著名实现:
docker build
/buildah
:用于构建 OCI 镜像。docker push
/podman push
:用于将 OCI 镜像推送到镜像仓库。- 所有主流的容器镜像仓库(Docker Hub, Quay.io, GCR 等)都支持 OCI 镜像规范。
3. OCI 总结
规范 | 定义了什么? | 核心产物 | 目标 |
---|---|---|---|
运行时规范 | 如何运行一个解压后的容器 | config.json + 文件系统 |
运行时标准化,确保任何 OCI 运行时都能以相同方式运行容器。 |
镜像规范 | 如何打包和分发一个容器镜像 | Manifest + Config + 文件系统层 | 镜像格式标准化,确保镜像可以在任何兼容的平台上构建和运行。 |
一句话总结 OCI:OCI 为容器生态制定了“法律”(规范),确保了“零件”(镜像和运行时)的通用性和互换性。
二、CRI (Container Runtime Interface / 容器运行时接口)
1. 是什么?
CRI 是 Kubernetes 项目在 v1.5 版本中引入的一个插件接口。
核心目标: 将 Kubernetes(kubelet)与具体的容器运行时解耦。
在 CRI 出现之前,Kubernetes 的 kubelet 是硬编码地直接调用 Docker 的 API 来管理容器。这带来了几个严重问题:
- 强依赖:Kubernetes 的命运和 Docker 强绑定。如果 Docker 出问题或者有更好的运行时出现,Kubernetes 很难切换。
- 功能冗余:Docker 本身是一个功能非常庞杂的“全家桶”(包含镜像构建、镜像管理、容器网络、容器存储、容器运行时等)。Kubernetes 其实只需要它的一部分功能(主要是容器生命周期管理),但不得不依赖整个 Docker 引擎。
- 架构不清晰:kubelet 的代码里混杂了大量与 Docker 交互的逻辑,变得臃肿且难以维护。
为了解决这个问题,Kubernetes 团队设计了 CRI。CRI 是一套 gRPC API。它定义了一组接口,包括如何创建、启动、停止、删除容器,如何拉取镜像,如何管理 Pod 的沙箱环境等。
你可以把 CRI 想象成**“Kubernetes 的‘USB接口’”**。Kubernetes(kubelet)是电脑主机,它不关心你插的是什么牌子的 U 盘(容器运行时),只要你的 U 盘符合 USB 标准(实现了 CRI 接口),它就能识别和使用。
2. CRI 的工作原理
kubelet 内置了一个 CRI 客户端,它会通过 gRPC 连接到一个实现了 CRI 服务端接口的容器运行时服务。这个服务通常以一个守护进程 的形式运行在同一个节点上。
CRI 主要分为两大服务:
-
Image Service (镜像服务):
- 负责所有与镜像相关的操作。
- 主要接口:
PullImage
,ListImages
,ImageStatus
,RemoveImage
。
-
Runtime Service (运行时服务):
- 负责所有与 Pod 和容器生命周期相关的操作。
- 主要接口:
RunPodSandbox
,StopPodSandbox
,RemovePodSandbox
,CreateContainer
,StartContainer
,StopContainer
,RemoveContainer
等。
关键概念 PodSandbox
(Pod 沙箱):
在 CRI 模型中,Kubernetes 管理的最小单元是 Pod。一个 Pod 包含一个或多个容器,它们共享网络和存储命名空间。PodSandbox
就是这个共享环境的抽象。在 Linux 上,它通常就是一个 paused 容器,它的唯一作用就是 hold 住 namespaces,让 Pod 内的其他容器可以加入进来。
3. 著名的 CRI 实现
一个软件要能被 Kubernetes 用作容器运行时,它必须实现 CRI 接口。目前主流的 CRI 实现有:
-
containerd
:- 这是目前 Kubernetes 的事实标准和默认的 CRI 运行时。
- 它本身是一个高级运行时,从 Docker 引擎中剥离出来,成为了一个 CNCF 的毕业项目。
- 它的架构非常清晰:
containerd
(实现了 CRI) ->runc
(OCI Runtime) -> 内核。 - 它负责镜像管理、存储管理、网络管理,并调用底层的
runc
来实际启动容器。
-
CRI-O
:- 这是 Red Hat 主导开发的一个轻量级的 CRI 实现。
- 它的设计哲学是**“只做 Kubernetes 需要的事”**,没有任何多余的功能。
- 它的架构同样清晰:
CRI-O
(实现了 CRI) ->runc
/crun
(OCI Runtime) -> 内核。 - 因为轻量,所以启动速度快,资源占用少,非常适合对性能和资源敏感的场景。
-
Docker Engine
(通过dockershim
):- 这是 Kubernetes 早期使用的方式。kubelet 内部有一个名为
dockershim
的组件,它的作用就是将 CRI 调用“翻译”成 Docker API 调用。 dockershim
是一个临时方案,它让 Kubernetes 能够在 CRI 标准成熟之前继续使用 Docker。- 重要提示:从 Kubernetes v1.20 开始,
dockershim
被标记为废弃(Deprecated),并在 v1.24 版本中被正式移除。这意味着 Kubernetes 不再直接支持 Docker 作为容器运行时。你仍然可以在 K8s 节点上安装 Docker 来构建镜像,但 K8s 本身管理容器会通过containerd
或CRI-O
。
- 这是 Kubernetes 早期使用的方式。kubelet 内部有一个名为
4. CRI 总结
组件 | 角色 | 作用 |
---|---|---|
Kubelet | CRI 客户端 | Kubernetes 的节点代理,负责管理 Pod 和容器。它通过 CRI 接口与运行时通信。 |
CRI 接口 | 标准/契约 | 一套 gRPC API,定义了 kubelet 如何与容器运行时交互。 |
CRI 实现 | CRI 服务端 | containerd , CRI-O 等软件。它们实现了 CRI 接口,负责解析 kubelet 的请求并执行具体操作。 |
OCI 运行时 | 底层执行者 | runc , crun 等。它们被 CRI 实现调用,根据 OCI Runtime 规范来实际创建和运行容器。 |
一句话总结 CRI:CRI 是 Kubernetes 的“USB接口”,它让 K8s 可以灵活地插拔不同的容器运行时,而无需修改自己的核心代码。
三、OCI 和 CRI 的关系:它们如何协同工作?
这是最关键的部分。OCI 和 CRI 并不是竞争关系,而是分层协作、相辅相成的关系。
一个形象的比喻:汽车制造业
- OCI (标准):
- 运行时规范:定义了“发动机”的工作原理(四冲程、点火顺序等)。只要符合这个标准,无论是 V6 还是 V8 发动机,都能让汽车跑起来。这就像
runc
和crun
。 - 镜像规范:定义了“汽车零件”的标准化尺寸和接口(如螺丝型号、轮胎规格)。任何工厂生产的零件,只要符合标准,就能组装到一辆车上。这就像 OCI 镜像。
- 运行时规范:定义了“发动机”的工作原理(四冲程、点火顺序等)。只要符合这个标准,无论是 V6 还是 V8 发动机,都能让汽车跑起来。这就像
- CRI (接口):
- 定义了“汽车总装线”的控制面板和操作流程。工人(kubelet)只需要按按钮(调用 CRI API),总装线(CRI 实现,如
containerd
)就会自动去抓取标准化的零件(OCI 镜像),并使用标准化的发动机(OCI Runtime,如runc
)来组装一辆完整的汽车。
- 定义了“汽车总装线”的控制面板和操作流程。工人(kubelet)只需要按按钮(调用 CRI API),总装线(CRI 实现,如
流程解析:
- 用户通过
kubectl
创建一个 Pod。 - Kubernetes API Server 将 Pod 调度到某个节点。
- 该节点的 Kubelet 接收到任务,它需要创建一个 Pod。
- Kubelet 通过 CRI (gRPC) 接口,调用 CRI 实现(如
containerd
) 的RunPodSandbox
API,要求它创建一个 Pod 沙箱环境。 containerd
收到请求后:- 首先,它会去拉取 Pod 中容器所需的镜像。它拉取的镜像格式是符合 OCI Image Spec 的。
- 然后,它准备好 Pod 的网络和存储。
- 最后,它调用底层的 OCI Runtime(如
runc
) 来实际创建并启动 Pod 的沙箱容器(通常是pause
容器)。
runc
被调用时,containerd
会给它准备好一个目录,里面包含了解压后的镜像文件系统和一个符合 OCI Runtime Spec 的config.json
文件。runc
读取config.json
,根据里面的配置,直接向 Linux 内核 发起系统调用(如clone()
创建 namespace,setrlimit()
设置 cgroups),最终启动容器进程。- 一旦 Pod 沙箱准备好,Kubelet 就会继续通过 CRI 接口,让
containerd
在这个沙箱里创建和启动真正的业务容器。这个过程同样会经历containerd
->runc
-> 内核的调用链。
最终结论
特性 | OCI (开放容器倡议) | CRI (容器运行时接口) |
---|---|---|
性质 | 标准 | 接口 |
制定者 | Linux 基金会下的 OCI 组织 | Kubernetes 社区 (CNCF) |
目标 | 整个容器生态的标准化和互操作性 | Kubernetes 与容器运行时的解耦 |
关注点 | “物”的规范:定义了容器镜像长什么样,容器运行时应该怎么工作。 | “事”的流程:定义了 Kubernetes 如何向运行时下达指令(创建、启动、停止容器等)。 |
关系 | CRI 的实现依赖于 OCI 规范。containerd (CRI实现) 在底层必须调用一个符合 OCI Runtime Spec 的 runc (OCI实现) 来干活。containerd 管理的镜像也必须是符合 OCI Image Spec 的。 |
简单来说:OCI 负责定义“什么是容器”,CRI 负责定义“如何管理容器”。它们共同构成了现代云原生容器化世界的基石,使得生态既开放标准,又灵活可插拔。
更多推荐
所有评论(0)