从Docker到Containerd:解读K8s运行时演进背后的OCI与CRI双标准协同
Kubernetes 1.24版本移除对Docker Engine的原生支持,标志着云原生容器生态完成了一次关键的标准化迭代。这并非Docker与K8s的决裂,而是一场由OCI与CRI两大标准驱动的、从单一工具依赖走向开放生态的必然演进。本文将为你清晰梳理这段技术史,揭示标准如何重塑云平台的基础架构。
一、 混沌初开: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 → runc。Docker 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) → runc。kubelet → 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 |
四、 生态迁移: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双层标准协同落地的过程。这场演进解决了早期生态中的核心痛点:
- 打破垄断:通过CRI接口,K8s摆脱了对单一运行时(Docker)的技术绑定。
- 明确边界:OCI标准定义了“容器是什么”,CRI定义了“K8s如何操作容器”,各层职责清晰。
- 促进创新:标准接口催生了containerd、CRI-O、kata-containers等多样化的运行时,满足不同场景需求。
K8s弃用Dockershim,不是一个时代的结束,而是一个更开放、更标准、更以云原生理念为核心的时代的开始。它告诉我们,在基础设施领域,开放标准远比单一公司的技术实现更具生命力和价值。对于开发者和架构师而言,理解OCI与CRI的协同作用,是深入理解现代容器化与编排体系的关键。
浙公网安备 33010602011771号