OCI/CRI 双标准下:从 dockerd 到 containerd 的 K8s 运行时迭代史

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运行时标准) 的独立工具------runcrunc 成为 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标准工具",但仍为单体架构)。

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) → 容器
  • 2015 年后:Docker 切换为runc,K8s适配链路同步调整
    • 核心变化:Docker Engine 中 dockerd 底层从自身内置的 libcontainer 切换为 runc,但 dockerd 的对外私有 API 未发生核心变化;
    • K8s 适配逻辑无需大幅修改,仅链路随Docker底层变化更新:kubelet → 无名适配代码 → dockerd → runc → 容器
  • 潜在矛盾:无论 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 → 容器

    核心变化:

    1. containerd 聚焦"OCI标准下的通用容器运行时能力",具备独立运行能力;
    2. 此时 containerd 仍属于 Docker Engine 生态,未实现任何编排平台的接口标准(如CRI)。
    3. 配套引入 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 适配代码"封装改造,赋予正式名称 dockershimshim 即"垫片/适配层"),使其承担"将 CRI 指令翻译成 Docker Engine dockerd 私有 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):

  1. 存量用户约束:绝大多数 K8s 用户仍使用 Docker Engine(containerd 是其内置依赖,受 dockerd 管控),K8s 若直接对接内置 containerd,会与 dockerd 产生管控冲突(如用户用 docker stop 停容器,K8s 用 containerd 起容器);
  2. 标准层差异: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(剔除了 buildSwarm 等冗余功能),且同时兼容 OCI 行业标准和 CRI 接口标准,成为 K8s 官方推荐的容器运行时;
  • 其他原生 CRI 运行时(如 CRI-O、kata-containers)百花齐放,均遵循"底层OCI+上层CRI"的双标准模式,K8s 生态彻底摆脱对 Docker Engine 的依赖。

2. K8s 侧:逐步弱化 dockerdshim

  • 2020 年:K8s 团队宣布计划移除 dockerdshim,给用户留足 2 年迁移时间;
  • 核心原因:dockerdshim 作为临时适配层,存在三大问题:
    1. 链路冗余:多一层适配导致通信延迟增加,故障排查复杂度提升;
    2. 维护成本高:需同步跟进 Docker Engine dockerd 的 API 迭代,占用 K8s 核心开发资源;
    3. 标准割裂: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 依然通过内置 containerdrunc,保障单机场景下 docker run/docker build 等核心命令的正常执行,这正是"捐赠不剔除"策略的价值所在。
  • 存量用户解决方案:
    1. 长期最优解:迁移到独立部署的 containerd(卸载 Docker Engine,直接部署 containerd,链路简化为 kubelet → CRI → containerd → containerd-shim → runc,同时满足OCI/CRI双标准);
    2. 临时应急:使用 Docker 官方维护的 cri-dockerd(替代原生 dockerdshim 的第三方适配层),继续调用 Docker Engine,非官方推荐长期使用。

2. 最终生态格局

  • Docker Engine 定位:回归"单机容器工具"本质,聚焦开发者体验(docker builddocker compose 等),核心价值是"基于OCI标准构建镜像、本地调试容器",不再是 K8s 集群的核心运行时;
  • K8s 定位:基于 CRI 接口标准,对接多种遵循OCI标准的运行时,实现"编排平台"与"运行时"的解耦,架构更简洁、维护成本更低;
  • containerd 定位:成为云原生领域的"通用容器运行时",底层遵循OCI Runtime Spec,上层实现CRI接口,承接 Docker Engine 中 dockerd 在 K8s 中的核心角色,实现生态平滑过渡。

六、 核心总结:演进的本质是"双层标准"的协同落地

  1. 标准层面:OCI 是容器的"行业通用标准"(管镜像/底层运行),CRI 是 K8s 的"专属接口标准"(管编排对接),二者分层协作,不可混淆;
  2. 链路层面:K8s 早期适配链路随 Docker 中 dockerd 底层变化同步调整(2014-2015年:dockerd+内置libcontainer;2015年后:dockerd+runc),但始终绑定 dockerd 私有 API,这是后续CRI标准推出的核心动因;
  3. 表述精准性:libcontainer 是 Docker Engine 中 dockerd 内置的私有库,而非整个 Docker Engine 内置,runc 替代的是 dockerd 内置的 libcontainer,而非 Docker Engine 整体的底层依赖;
  4. 生态层面:不是"对立"而是"协同演进"------Docker 推动 OCI 标准落地(runc/镜像),K8s 推动 CRI 接口落地,Docker 抽离 containerd 是主动适配生态,K8s 移除 dockerdshim 是顺势完成架构优化;
  5. 关键补充:Docker 拆分捐赠 runccontainerd 的核心逻辑是"移交维护权,保留使用权",并非从自身架构中剔除这两个组件,而是通过"社区化维护+自身内置依赖"的模式,既推动容器生态标准化,又保障单机用户的核心体验;
  6. 逻辑层面:从"依赖单一工具(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 专属接口标准(编排对接)。
相关推荐
天才奇男子21 小时前
HAProxy高级功能全解析
linux·运维·服务器·微服务·云原生
_运维那些事儿1 天前
VM环境的CI/CD
linux·运维·网络·阿里云·ci/cd·docker·云计算
lpruoyu1 天前
【Docker进阶-05】Docker网络
网络·docker·容器
人间打气筒(Ada)1 天前
k8s:CNI网络插件flannel与calico
linux·云原生·容器·kubernetes·云计算·k8s
江畔何人初1 天前
pod的内部结构
linux·运维·云原生·容器·kubernetes
三块钱07941 天前
群晖docker部署Mattermost,对接openclaw
运维·docker·容器
周航宇JoeZhou1 天前
JB2-7-HTML
java·前端·容器·html·h5·标签·表单
苦逼IT运维1 天前
从 0 到 1 理解 Kubernetes:一次“破坏式”学习实践(一)
linux·学习·docker·容器·kubernetes
萧曵 丶1 天前
Docker 面试题
运维·docker·容器