OCI/CRI 双标准下:从 dockerd 到 containerd 的 K8s 运行时迭代史
文章目录
- [OCI/CRI 双标准下:从 dockerd 到 containerd 的 K8s 运行时迭代史](#OCI/CRI 双标准下:从 dockerd 到 containerd 的 K8s 运行时迭代史)
-
- 引言
- [一、 2013-2015:Docker 垄断容器市场,K8s 早期被动适配](#一、 2013-2015:Docker 垄断容器市场,K8s 早期被动适配)
-
- [1. Docker 侧:单体架构+私有实现,奠定容器普及基础](#1. Docker 侧:单体架构+私有实现,奠定容器普及基础)
- [2. K8s 侧:诞生初期无标准接口,随Docker底层变化调整适配链路](#2. K8s 侧:诞生初期无标准接口,随Docker底层变化调整适配链路)
- [二、 2016:Docker 主动架构解耦,抽离 containerd 埋下标准化伏笔](#二、 2016:Docker 主动架构解耦,抽离 containerd 埋下标准化伏笔)
-
- [1. Docker 侧:从"单体"到"模块化",拆分通用运行时能力](#1. Docker 侧:从“单体”到“模块化”,拆分通用运行时能力)
- [2. K8s 侧:适配逻辑未变,等待专属接口标准落地](#2. K8s 侧:适配逻辑未变,等待专属接口标准落地)
- [三、 2017:生态转折点------CRI 推出(K8s层)+ containerd 中立化(OCI层)](#三、 2017:生态转折点——CRI 推出(K8s层)+ containerd 中立化(OCI层))
-
- [1. K8s 侧:推出CRI接口标准,推动运行时对接标准化](#1. K8s 侧:推出CRI接口标准,推动运行时对接标准化)
- [2. Docker 侧:捐赠 containerd 给 CNCF,完成中立化+适配CRI](#2. Docker 侧:捐赠 containerd 给 CNCF,完成中立化+适配CRI)
- [3. 核心矛盾与过渡逻辑](#3. 核心矛盾与过渡逻辑)
- [四、 2017-2022:containerd 成熟,dockershim 逐步退出历史舞台](#四、 2017-2022:containerd 成熟,dockershim 逐步退出历史舞台)
-
- [1. 生态趋势:独立 containerd 成为主流(OCI+CRI双标准兼容)](#1. 生态趋势:独立 containerd 成为主流(OCI+CRI双标准兼容))
- [2. K8s 侧:逐步弱化 dockerdshim](#2. K8s 侧:逐步弱化 dockerdshim)
- [五、 2022:K8s 1.24 正式移除 dockerdshim,完成标准化交替](#五、 2022:K8s 1.24 正式移除 dockerdshim,完成标准化交替)
-
- [1. 核心动作:移除内置 dockerdshim,不再原生支持 Docker Engine](#1. 核心动作:移除内置 dockerdshim,不再原生支持 Docker Engine)
- [2. 最终生态格局](#2. 最终生态格局)
- [六、 核心总结:演进的本质是"双层标准"的协同落地](#六、 核心总结:演进的本质是“双层标准”的协同落地)
-
- [1. 双标准的核心价值:定义生态边界,实现跨工具兼容](#1. 双标准的核心价值:定义生态边界,实现跨工具兼容)
-
- [(1)OCI 标准:容器技术的 "通用语言",打破工具壁垒](#(1)OCI 标准:容器技术的 “通用语言”,打破工具壁垒)
- [(2)CRI 标准:K8s 编排的 "对接接口",实现运行时中立](#(2)CRI 标准:K8s 编排的 “对接接口”,实现运行时中立)
- (3)双标准协同:构建云原生容器生态的底层基石
- 关键术语精准回顾
引言
容器技术从"单机工具"走向"集群编排"的过程,本质是 "Docker 推动容器普及(基于OCI标准)"与"K8s 推动编排标准化(基于CRI接口)" 的协同演进史。
而"K8s 1.24 版本开始不再原生支持 Docker Engine",并非两者的"对立",而是生态从"依赖单一工具"走向"多运行时标准化"的必然结果。
本文梳理演进全流程的核心前提,是厘清两个极易混淆的标准体系 ------ 这是理解后续技术链路变化的关键:
| 标准体系 | 发起方 | 核心定义 | 覆盖范围 | 代表实现 / 产物 |
|---|---|---|---|---|
| OCI(开放容器倡议) | Linux 基金会 | 包含两大子标准:1. OCI Image Spec (镜像标准):定义容器镜像的格式 / 结构;2. OCI Runtime Spec(运行时标准):定义容器生命周期(创建 / 启动 / 停止)的底层操作规范 | 行业级通用标准(跨平台 / 跨工具) | 镜像:Docker 镜像;运行时:runc |
| CRI(容器运行时接口) | Kubernetes 社区 | 定义 Kubernetes kubelet组件与容器运行时之间的通信接口规范 | K8s 专属接口标准(仅适用于 K8s) | containerd (cri-containerd 模块)、CRI-O |
简单总结:OCI 是容器技术的行业通用标准(管镜像格式、底层容器运行),CRI是 K8s 对接运行时的专属接口标准(管 K8s 和运行时的通信),二者分属不同层面,不可混淆。
本文将按时间线,完整拆解 Docker 基于OCI标准的架构解耦、K8s 基于CRI接口的运行时标准化、两者从"适配依赖"到"标准化交替"的全流程。
一、 2013-2015:Docker 垄断容器市场,K8s 早期被动适配
1. Docker 侧:单体架构+私有实现,奠定容器普及基础
-
2013 年:Docker 正式发布,以 "单体架构的 Docker Engine" 为核心(包含
dockerd守护进程、Docker CLI、网络/存储插件,以及**dockerd内置的核心底层库libcontainer**)。libcontainer是 Docker Engine 中dockerd自研的私有库,直接集成在dockerd进程中,负责实现容器的核心隔离能力(Linux Namespace/Cgroup),无任何行业标准约束;- 此时 Docker Engine 是"大而全"的单机容器工具:
dockerd包揽镜像构建、容器生命周期管理、API 服务等所有功能,底层通过自身内置的libcontainer实现容器隔离,用户通过docker run即可启动容器,快速成为容器技术的事实标准。
-
2015 年:Docker 推动行业标准化,捐赠
libcontainer并落地OCI标准为避免生态割裂,Linux 基金会牵头成立 OCI(开放容器倡议) ,制定两大核心行业标准:
OCI Image Spec(镜像标准)和OCI Runtime Spec(运行时标准)。- Docker 主动将 自身 Docker Engine 中
dockerd内置的libcontainer库完整捐赠给 OCI; - 基于
libcontainer的核心代码,改造出符合 OCI Runtime Spec(OCI运行时标准) 的独立工具------runc(runc成为 OCI Runtime Spec 的官方参考实现,不再属于 Docker Engine 私有组件); - 同时,Docker 镜像格式同步适配
OCI Image Spec(OCI镜像标准),成为行业通用镜像格式。 - 关键补充 :此次拆分捐赠并非将
runc从 Docker Engine 架构中剔除,而是将runc的代码维护权和标准化制定权移交 OCI 社区,Docker Engine 依然内置并依赖runc完成底层容器隔离操作。
此时 Docker Engine 的链路更新为:
Docker CLI → dockerd → runc → 容器(runc替代了 Docker Engine 中dockerd内置的libcontainer,Docker Engine 从"私有隔离实现"转向"调用OCI标准工具",但仍为单体架构)。 - Docker 主动将 自身 Docker Engine 中
2. K8s 侧:诞生初期无标准接口,随Docker底层变化调整适配链路
- 2014 年:K8s 诞生,核心目标是"大规模容器编排"。但此时容器市场几乎被 Docker 垄断,K8s 若不支持 Docker,将失去全部潜在用户。
- 核心问题:Docker Engine 的
dockerd仅提供 私有 API (无统一接口标准),K8s 无法直接对接。为此,K8s 团队开发了 零散的 Docker 适配代码 (无正式命名),直接调用dockerd的私有 API 管理容器。 - 此时 K8s 链路(2014-2015年,Docker用libcontainer阶段):
kubelet → 无名适配代码 → dockerd(内置libcontainer) → 容器。
- 核心问题:Docker Engine 的
- 2015 年后:Docker 切换为runc,K8s适配链路同步调整
- 核心变化:Docker Engine 中
dockerd底层从自身内置的libcontainer切换为runc,但dockerd的对外私有 API 未发生核心变化; - K8s 适配逻辑无需大幅修改,仅链路随Docker底层变化更新:
kubelet → 无名适配代码 → dockerd → runc → 容器。
- 核心变化:Docker Engine 中
- 潜在矛盾:无论 Docker 中
dockerd底层是libcontainer还是runc,K8s 始终绑定dockerd的私有 API------Docker 迭代 API 就可能导致 K8s 适配代码失效,维护成本极高。
二、 2016:Docker 主动架构解耦,抽离 containerd 埋下标准化伏笔
1. Docker 侧:从"单体"到"模块化",拆分通用运行时能力
-
核心动因:Docker 团队意识到,臃肿的
dockerd单体架构既不利于自身生态扩展,也无法满足 K8s 等编排平台对"轻量运行时"的需求(dockerd包含镜像构建、Swarm 编排等对集群无用的冗余功能)。 -
关键动作:从
dockerd中抽离出 容器运行时核心功能 (镜像拉取、容器启停、资源隔离、调用runc的逻辑),独立为组件------containerd。- 关键补充 :
containerd抽离后仍属于 Docker Engine 的核心依赖,用户安装 Docker Engine 时会自动部署该组件,Docker 并未剔除这部分能力,只是将其从dockerd单体中拆分,移交独立的维护链路,同时保留自身对containerd的调用权限。
拆分后 Docker Engine 架构:
Docker CLI → dockerd(专注 Docker 特有功能:build、compose、Swarm) → containerd → containerd-shim → runc → 容器。核心变化:
containerd聚焦"OCI标准下的通用容器运行时能力",具备独立运行能力;- 此时
containerd仍属于 Docker Engine 生态,未实现任何编排平台的接口标准(如CRI)。 - 配套引入
containerd-shim作为工程化优化组件,核心解决containerd与容器生命周期耦合、容器资源独立管控的问题。
- 关键补充 :
2. K8s 侧:适配逻辑未变,等待专属接口标准落地
- 此时 K8s 仍依赖早期的"无名适配代码"对接 Docker Engine 的
dockerd,未直接对接containerd(原因:containerd仍受dockerd管控,且无 K8s 所需的CRI接口); - 潜在机遇:
containerd的独立,为后续 K8s 对接"通用运行时"提供了技术基础。
三、 2017:生态转折点------CRI 推出(K8s层)+ containerd 中立化(OCI层)
1. K8s 侧:推出CRI接口标准,推动运行时对接标准化
- 核心动因:K8s 团队希望摆脱对单一运行时的依赖,避免为不同厂商的运行时编写专属适配代码。
- 关键动作:2017 年 K8s 1.5 版本正式推出 CRI(Container Runtime Interface,容器运行时接口)------这是 K8s 层面的专属接口标准,定义了 kubelet 与容器运行时之间的通信规范,任何运行时只要实现 CRI,即可无缝接入 K8s,无需 K8s 额外适配。
- 适配逻辑升级:K8s 团队将早期的"无名 Docker 适配代码"封装改造,赋予正式名称
dockershim(shim即"垫片/适配层"),使其承担"将 CRI 指令翻译成 Docker Enginedockerd私有 API"的职责。
此时 K8s 链路(存量 Docker Engine 用户):kubelet → CRI → dockershim → Docker Daemon(dockerd) → containerd → containerd-shim → runc → 容器。
2. Docker 侧:捐赠 containerd 给 CNCF,完成中立化+适配CRI
-
关键动作1:2017 年,Docker 将
containerd捐赠给 云原生计算基金会(CNCF),使其成为中立的开源项目(不再属于 Docker Engine 私有组件)。- 关键补充 :此次捐赠同样不是将
containerd从 Docker Engine 中剔除,而是移交containerd的代码维护权和生态主导权,Docker Engine 依然内置并依赖containerd实现核心运行时能力,保障单机用户的使用体验不受影响。
- 关键补充 :此次捐赠同样不是将
-
关键动作2:中立后的
containerd快速开发cri-containerd模块,原生实现 K8s 的 CRI 接口标准 (注意:containerd底层仍遵循 OCI Runtime Spec 调用runc,上层实现 CRI 对接 K8s)。此时 K8s 理想链路(新部署用户):
kubelet → CRI → containerd → containerd-shim → runc → 容器(无适配层,更高效)。
这标志着 containerd 正式完成 "去 Docker 化" 转型,成为中立的通用容器生命周期管理组件。而 runc 的去 Docker 化进程更早 ------ 早在 2015 年被捐赠给 OCI 成为运行时标准参考实现时,就已脱离 Docker 的专属控制。runc 归属于 OCI 社区,聚焦底层容器运行;containerd 归属于 CNCF 社区,聚焦上层容器生命周期管理,且二者形成协作关系(containerd 底层调用 runc 执行容器启停)。此后,Docker 仅作为二者的使用者之一,K8s、Podman、AWS ECS 等各类容器工具 / 平台,都可直接调用 containerd 或 runc 实现容器功能。
3. 核心矛盾与过渡逻辑
两条链路并行存在的核心原因(严格区分OCI/CRI):
- 存量用户约束:绝大多数 K8s 用户仍使用 Docker Engine(
containerd是其内置依赖,受dockerd管控),K8s 若直接对接内置containerd,会与dockerd产生管控冲突(如用户用docker stop停容器,K8s 用containerd起容器); - 标准层差异:
containerd同时满足"底层OCI运行时标准"和"上层CRI接口标准",但 Docker Engine 的dockerd仅遵循OCI标准,未实现CRI接口------因此必须通过dockershim做CRI→Docker API的翻译。
简言之:dockershim 是"兼容存量 Docker Engine 用户"的临时方案,允许用户在不改动现有环境的前提下继续使用 K8s;而直接对接独立 containerd 是 K8s 推荐的长期方案(同时满足OCI/CRI双标准)。
四、 2017-2022:containerd 成熟,dockershim 逐步退出历史舞台
1. 生态趋势:独立 containerd 成为主流(OCI+CRI双标准兼容)
containerd作为 CNCF 项目,迭代速度加快,稳定性、轻量性远超 Docker Engine(剔除了build、Swarm等冗余功能),且同时兼容 OCI 行业标准和 CRI 接口标准,成为 K8s 官方推荐的容器运行时;- 其他原生 CRI 运行时(如 CRI-O、kata-containers)百花齐放,均遵循"底层OCI+上层CRI"的双标准模式,K8s 生态彻底摆脱对 Docker Engine 的依赖。
2. K8s 侧:逐步弱化 dockerdshim
- 2020 年:K8s 团队宣布计划移除
dockerdshim,给用户留足 2 年迁移时间; - 核心原因:
dockerdshim作为临时适配层,存在三大问题:- 链路冗余:多一层适配导致通信延迟增加,故障排查复杂度提升;
- 维护成本高:需同步跟进 Docker Engine
dockerd的 API 迭代,占用 K8s 核心开发资源; - 标准割裂:Docker Engine 的
dockerd仅遵循OCI标准,未实现CRI接口,与 K8s 标准化目标相悖。
五、 2022:K8s 1.24 正式移除 dockerdshim,完成标准化交替
1. 核心动作:移除内置 dockerdshim,不再原生支持 Docker Engine
- 本质:K8s 不再为 Docker Engine 的
dockerd进程提供 CRI 适配层,并非"不支持 Docker 生态"------Docker Engine 构建的 OCI 标准镜像仍可在 K8s 中运行(所有支持 CRI 的运行时都兼容 OCI 镜像标准); - 延伸补充:即便 K8s 不再原生支持
dockerd,Docker Engine 依然通过内置containerd和runc,保障单机场景下docker run/docker build等核心命令的正常执行,这正是"捐赠不剔除"策略的价值所在。 - 存量用户解决方案:
- 长期最优解:迁移到独立部署的
containerd(卸载 Docker Engine,直接部署containerd,链路简化为kubelet → CRI → containerd → containerd-shim → runc,同时满足OCI/CRI双标准); - 临时应急:使用 Docker 官方维护的
cri-dockerd(替代原生dockerdshim的第三方适配层),继续调用 Docker Engine,非官方推荐长期使用。
- 长期最优解:迁移到独立部署的
2. 最终生态格局
- Docker Engine 定位:回归"单机容器工具"本质,聚焦开发者体验(
docker build、docker compose等),核心价值是"基于OCI标准构建镜像、本地调试容器",不再是 K8s 集群的核心运行时; - K8s 定位:基于 CRI 接口标准,对接多种遵循OCI标准的运行时,实现"编排平台"与"运行时"的解耦,架构更简洁、维护成本更低;
- containerd 定位:成为云原生领域的"通用容器运行时",底层遵循OCI Runtime Spec,上层实现CRI接口,承接 Docker Engine 中
dockerd在 K8s 中的核心角色,实现生态平滑过渡。
六、 核心总结:演进的本质是"双层标准"的协同落地
- 标准层面:OCI 是容器的"行业通用标准"(管镜像/底层运行),CRI 是 K8s 的"专属接口标准"(管编排对接),二者分层协作,不可混淆;
- 链路层面:K8s 早期适配链路随 Docker 中
dockerd底层变化同步调整(2014-2015年:dockerd+内置libcontainer;2015年后:dockerd+runc),但始终绑定dockerd私有 API,这是后续CRI标准推出的核心动因; - 表述精准性:
libcontainer是 Docker Engine 中dockerd内置的私有库,而非整个 Docker Engine 内置,runc替代的是dockerd内置的libcontainer,而非 Docker Engine 整体的底层依赖; - 生态层面:不是"对立"而是"协同演进"------Docker 推动 OCI 标准落地(runc/镜像),K8s 推动 CRI 接口落地,Docker 抽离 containerd 是主动适配生态,K8s 移除 dockerdshim 是顺势完成架构优化;
- 关键补充:Docker 拆分捐赠
runc和containerd的核心逻辑是"移交维护权,保留使用权",并非从自身架构中剔除这两个组件,而是通过"社区化维护+自身内置依赖"的模式,既推动容器生态标准化,又保障单机用户的核心体验; - 逻辑层面:从"依赖单一工具(Docker Engine)的私有适配"到"基于双层标准(OCI+CRI)的多选择",是云原生生态发展的必然,最终实现"编排平台标准化、运行时组件通用化、镜像格式统一化"。
1. 双标准的核心价值:定义生态边界,实现跨工具兼容
历经多年演进,OCI 与 CRI 双标准的落地,为容器与 K8s 生态划定了清晰的协作规则,彻底解决了早期 "工具绑定、接口私有、格式混乱" 的痛点,其核心作用可概括为:
(1)OCI 标准:容器技术的 "通用语言",打破工具壁垒
OCI 作为行业级通用标准,为容器的 "镜像格式" 和 "底层运行时操作" 制定了统一规范,实现了 "一次构建 / 运行,跨工具复用" 的核心价值:
- 对镜像而言:只要符合 OCI Image Spec,无论通过 Docker Engine、Buildpacks、Ko 等何种工具构建,都能在所有支持 OCI 标准的运行时(containerd、CRI-O、runc 等)中启动,无需适配改造;
- 对运行时而言:只要遵循 OCI Runtime Spec,无论由哪个厂商开发(如 runc、kata-runtime),都能被上层容器工具(Docker、containerd、Podman)调用,承担底层容器隔离与生命周期管理职责。
简言之,OCI 标准让容器技术脱离了 "单一工具专属" 的局限,成为跨平台、跨工具的通用能力,这也是后续容器生态百花齐放的基础。
(2)CRI 标准:K8s 编排的 "对接接口",实现运行时中立
CRI 作为 K8s 专属接口标准,为 kubelet 与容器运行时的通信制定了统一规则,实现了 "编排平台与运行时解耦" 的目标:
- 对运行时厂商而言:只要原生实现 CRI 接口,就能无缝接入 K8s 生态,无需 K8s 团队为其编写专属适配代码(如 containerd、CRI-O 均通过此方式接入 K8s);
- 对 K8s 而言:无需绑定单一运行时,可根据场景选择不同的 CRI 运行时(如通用场景用 containerd,安全场景用 kata-containers),架构更灵活,维护成本大幅降低。
(3)双标准协同:构建云原生容器生态的底层基石
后续我们讲解容器运行时原理、镜像构建与分发、K8s 容器管控等技术时,都会围绕这两大标准展开 ------ 理解 OCI 与 CRI 的核心作用,是掌握整个容器技术体系的关键前提。
关键术语精准回顾
- libcontainer:2013-2015年 Docker Engine 中
dockerd内置的私有隔离库,是runc的前身; - runc:2015年诞生,OCI Runtime Spec 的参考实现(行业标准),所有容器运行时的底层执行工具,替代了
dockerd内置的libcontainer; - containerd:2016年从
dockerd抽离,先为 Docker Engine 核心依赖,后成为中立组件,同时兼容 OCI 运行时标准和 CRI 接口标准; - dockershim:2017年CRI推出后命名的适配层,因绑定
dockerd私有 API 最终被移除; - containerd-shim:工程化优化组件,核心解决
containerd与容器生命周期耦合、容器资源独立管控的问题; - OCI:行业级通用标准(镜像+运行时);
- CRI:K8s 专属接口标准(编排对接)。