mthoutai

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

从Docker到Containerd:解读K8s运行时演进背后的OCI与CRI双标准协同

Kubernetes 1.24版本移除对Docker Engine的原生支持,标志着云原生容器生态完成了一次关键的标准化迭代。这并非Docker与K8s的决裂,而是一场由OCICRI两大标准驱动的、从单一工具依赖走向开放生态的必然演进。本文将为你清晰梳理这段技术史,揭示标准如何重塑云平台的基础架构。

一、 混沌初开:Docker的垄断与K8s的早期适配

2013年,Docker以其革命性的易用性迅速成为容器的事实标准。其核心是单体架构的Docker Engine,它集成了守护进程、CLI以及实现容器隔离的核心私有库libcontainer。用户通过简单的docker run命令即可启动容器,这极大地推动了容器技术的普及,为后来的云原生浪潮奠定了基础。

与此同时,2014年诞生的Kubernetes面临着严峻挑战:容器市场已被Docker垄断。为了生存,K8s不得不编写零散的适配代码,直接调用Docker Engine的私有API来管理容器。此时的链路非常简单:K8s → Docker API → libcontainer。这种强绑定意味着Docker底层的任何变动都可能引发K8s的适配危机,维护成本高昂。

2015年,为避免生态割裂,Linux基金会牵头成立OCI(开放容器倡议)。Docker将libcontainer捐赠给OCI,并基于其发展出符合OCI运行时标准(Runtime Spec)的参考实现——runc。Docker Engine随后改用runc作为底层运行时。虽然K8s的适配链路同步更新为K8s → Docker API → runc,但核心问题未解:K8s依然深度耦合在Docker的私有API上。

二、 架构解耦:Containerd的诞生与标准化伏笔

2016年,Docker团队开始反思其臃肿的单体架构。庞大的Docker Engine包含了镜像构建、Swarm编排等众多功能,但对于K8s这样的云平台而言,许多功能是冗余的。为了追求更轻量、更模块化的设计,Docker从Engine中抽离出容器运行时的核心功能(镜像拉取、容器启停、调用runc等),形成了一个独立的组件——containerd

此时,Docker Engine的架构演变为:Docker CLI → Docker Daemon → containerd → runcDocker CLI → dockerd(专注 Docker 特有功能:build、compose、Swarm) → containerd → containerd-shim → runc → 容器 这一拆分意义重大:

  • containerd开始聚焦于成为通用的容器生命周期管理器。
  • 它为后续运行时生态的标准化提供了技术基础。
  • 然而,此时的containerd仍属于Docker生态,并未实现任何编排平台的接口标准。

对于K8s而言,它仍在等待一个专属的、能够一劳永逸摆脱运行时绑定的接口标准出现。

三、 标准确立:CRI的推出与Containerd的中立化

2017年是整个生态的转折点。K8s社区正式推出了容器运行时接口(CRI)。CRI定义了kubelet与容器运行时之间的通信规范,任何运行时只要实现CRI,就能无缝接入K8s。同时,K8s将之前杂乱的Docker适配代码封装成一个正式组件——dockershim,其作用是将CRI指令翻译成Docker API。

几乎在同一时间,Docker做出了另一个影响深远的决定:将containerd项目捐赠给云原生计算基金会(CNCF),使其成为一个完全中立的开源项目。中立后的containerd迅速开发了CRI插件,原生实现了K8s的CRI接口。至此,一条更高效、更标准的理想链路诞生了:K8s (kubelet) → containerd (with CRI) → runckubelet → CRI → containerd → containerd-shim → runc → 容器

这里必须厘清一个关键概念:OCI标准CRI标准分属不同层面。

标准体系发起方核心定义覆盖范围代表实现 / 产物
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编排的“专属插座”(管通信接口)。Containerd的成功在于它同时兼容了这两层标准。

四、 生态迁移:Containerd的崛起与Dockershim的退场

成为CNCF项目后,containerd的发展进入快车道。它轻量、稳定,且专注于容器运行时核心职责,迅速成为各大云服务商和企业的首选。相比之下,通过dockershim对接Docker Engine的链路显得愈发笨重和不合时宜。

K8s社区开始推动生态迁移。2020年,K8s团队宣布了移除dockershim的计划,并给出了长达两年的迁移缓冲期。移除原因主要包括:

  • 链路冗余:dockershim作为额外适配层,增加了复杂性和故障排查难度。
  • 维护负担:需要持续跟进Docker API的变化,消耗核心开发资源。
  • 标准背离:与K8s推动运行时中立和标准化的长期目标相悖。

这一时期,除了containerd,其他原生支持CRI的运行时(如CRI-O)也蓬勃发展,K8s生态真正实现了运行时的多元化与标准化。[AFFILIATE_SLOT_1]

五、 新时代:Dockershim正式移除与最终格局

2022年,随着Kubernetes 1.24版本的发布,dockershim被正式从代码库中移除。这意味着K8s不再原生支持Docker Engine作为容器运行时。这并非“不支持Docker”,而是不再支持通过Docker Engine的私有API来接入K8s。

用户依然可以在节点上安装Docker用于构建镜像或单机测试,但K8s将直接通过CRI接口与containerd(或其他CRI运行时)通信。对于需要进行云迁移或集群升级的用户,这意味着需要将运行时从Docker Engine切换为containerd或CRI-O。至此,云原生容器生态的格局尘埃落定:

  • 底层:OCI标准(runc等)统一容器隔离与镜像格式。
  • 中层:containerd等成为主流的容器生命周期管理组件。
  • 上层:CRI标准统一了K8s与运行时的对接方式。

这种分层标准化使得整个生态更加健壮和灵活,为构建更复杂的云存储与计算服务提供了坚实基础。[AFFILIATE_SLOT_2]

六、 核心总结:双层标准如何重塑云原生基石

回顾从Docker到Containerd的演进史,其本质是OCI与CRI双层标准协同落地的过程。这场演进解决了早期生态中的核心痛点:

  1. 打破垄断:通过CRI接口,K8s摆脱了对单一运行时(Docker)的技术绑定。
  2. 明确边界:OCI标准定义了“容器是什么”,CRI定义了“K8s如何操作容器”,各层职责清晰。
  3. 促进创新:标准接口催生了containerd、CRI-O、kata-containers等多样化的运行时,满足不同场景需求。

K8s弃用Dockershim,不是一个时代的结束,而是一个更开放、更标准、更以云原生理念为核心的时代的开始。它告诉我们,在基础设施领域,开放标准远比单一公司的技术实现更具生命力和价值。对于开发者和架构师而言,理解OCI与CRI的协同作用,是深入理解现代容器化与编排体系的关键。

posted on 2026-03-07 18:40  mthoutai  阅读(41)  评论(0)    收藏  举报