摘要

  • containerd 是一个已从 CNCF 毕业的开源守护进程,在 Linux 和 Windows 上为上层平台管理镜像内容、快照、容器元数据和运行中的任务。
  • 该项目源于 Docker 的运行时重构,于 2017 年进入 Cloud Native Computing Foundation,随后成为共享基础设施层,而不是完整的容器平台。
  • Kubernetes 通过 Container Runtime Interface 连接 containerd,而网络、底层隔离、镜像信任和集群调度仍由其他组件负责,并继续共同决定最终结果。
  • containerd 2.3 于 2026 年 4 月 30 日成为当前 LTS 系列;此后的安全与生命周期修复表明,清理、状态协调和补丁交付也是运行时正确性的一部分。

即使进程成功启动,挂载泄漏仍是运行时故障

2026 年 7 月 10 日,containerd 发布了 2.3.3 版。修复内容包括沙箱状态验证、NRI 退出处理,以及失败钩子路径可能造成挂载泄漏的问题。这些问题听起来都不像容器逃逸那样严重。然而,对运营人员而言,本应消失却仍然存在的挂载就是实际的运行时故障:当这种小规模泄漏在整个节点集群中反复发生时,可能持续消耗宿主机资源、增加清理难度,并使一台原本健康的机器最终不得不停止承载工作负载或被重新构建。

这次发布体现了 containerd 为何重要。大多数用户不会直接与这个守护进程交互。他们要求 Kubernetes 启动一个 Pod,让 Docker 运行一个容器,或者让托管云服务提供一个节点。containerd 位于这些请求下方,将其转换为涉及镜像内容、文件系统快照、容器记录、运行时 shim 和活动进程的一系列状态转换。可见的平台也许能通过重新调度工作恢复服务,但运行时仍必须让节点保持在可理解、可再次使用的状态。

因此,启动进程只是正确性的一部分。创建、观察、删除和恢复都同样重要。一个任务即使启动迅速,但如果遗留挂载、陈旧的网络状态、失去管理的 shim 或尚未协调的元数据,在运营层面仍不能算正确。这类故障可能在数百或数千次表面成功的操作之后才缓慢显现。

这正是 containerd 面临的核心张力。该项目因成为众多产品下方的通用中间层而取得成功。同样的普及程度,也使其日常生命周期决策能够影响大规模节点集群,而应用团队甚至可能从未看到过它的名字。

containerd 因为没有成为完整平台而更有价值

containerd 是嵌入式运行时守护进程,而不是完整的容器产品。它不负责在集群中调度工作负载,不提供应用服务模型,也不决定企业应如何构建和部署软件。其稳定的 gRPC 服务提供范围更窄的能力:传输和存储镜像内容、维护元数据、准备文件系统快照、创建容器记录,以及管理运行中的任务。

这条边界是有意设计的。Docker Engine 可以在其上构建面向用户的产品。Kubernetes 可以通过 Container Runtime Interface 将其用作节点运行时。云服务商可以把它打包进节点镜像。Linux 发行版可以使用自己的默认设置和回溯补丁提供它。每个上层系统都能呈现不同的运营模型,而无须重新实现相同的基础镜像和进程生命周期机制。

这一区别也解释了为何 containerd 附带的命令ctr很容易被误用。项目将其视为不稳定的调试和开发接口,而不是受支持的面向用户契约。如果生产流程暗中依赖ctr的内部行为,它可能多年运行正常,最后却发现自己建立在项目有意保留变更自由的部分之上。

因此,稳定边界不只是 API 决策,也是治理选择。项目围绕已有文档的服务承诺兼容性,同时保留替换其背后实现的空间。只有下游产品同样尊重这条边界,containerd 才能成为有效的共享层。

Docker 提取通用运行时核心,形成其他产品可以复用的基础设施

containerd 最初诞生于 Docker 内部。早期 Docker 将镜像分发、构建、API、网络和进程生命周期整合在一个产品中。随着平台发展,运行时工作被拆分出来,使准备和监管容器的长期机制能够独立于 Docker 的其他产品功能演进。

公开发布历史始于日期为 2015 年 12 月 4 日的 0.0 系列。2017 年 3 月,Docker 将 containerd 捐赠给 Cloud Native Computing Foundation。1.0 版随后于 2017 年 12 月 5 日发布,确立了稳定的 gRPC API 和面向生产环境的嵌入契约。CNCF 于 2019 年 2 月 28 日宣布该项目毕业。

这一机构归属变化很重要,因为 Docker 既是项目发起者,也是一家商业平台公司。当一个运行时被相互竞争的云服务商、Linux 发行商和 Kubernetes 供应商使用时,如果其上游治理不再由单一产品供应商独占,各方就更容易对其形成依赖。Apache License 2.0 也允许广泛的商业和开源使用,而不要求所有采用者共享同一种商业模式。

基金会托管并未抹去 Docker 的历史,也没有消除雇用维护者的公司所产生的影响。它改变的是共享代码接受治理的正式路径。containerd 成为 Docker 继续使用的基础设施,而不再是其他参与者必须按照 Docker 条件接受的内部组件。

技术结果同样重要。上层平台在编排系统与 runc 等底层运行时之间获得了一个持久层。多个产品不再需要分别发明自己的镜像存储、快照生命周期和进程监管模型,而可以围绕同一套机制汇聚,并在其他领域展开竞争。

镜像、容器和任务被有意定义为不同对象

容器术语之所以容易混淆,是因为面向用户的工具经常用同一个词指代多个对象,containerd 则不会这样做。镜像指内容和元数据;容器记录保存预期的运行时配置与标签;任务代表根据该定义创建的活动进程集合。三者可以拥有不同的生命周期。

任务退出后,容器记录仍可保留。使用某个镜像的所有容器被删除后,镜像仍可存在。任务可能已经终止,但描述其容器的元数据仍然存在。这种分离有利于重启、检查和恢复,因为守护进程无须把进程寿命视为所有关联对象的寿命。

这种设计也带来了运营义务。删除一个对象并不能自动证明所有相关文件系统、数据块、shim 或网络资源都已被回收。只统计容器而不了解任务的监测系统可能报告误导性状态。假设一次删除会自动清除其他对象的清理代码,也可能留下资源。

因此,对象模型并非实现细节,而是运行时对一个基本问题的回答:预期配置、存储内容和活动执行是不同形式的状态。需要可靠恢复的系统必须能够判断其中哪一种状态发生了故障。

这一点在部分失败后尤其重要。如果守护进程重启、节点资源压力事件或钩子错误在操作进行到一半时将其打断,恢复路径必须拥有足够的持久信息,以判断哪些步骤已经完成,哪些内容仍需删除或重新创建。

内容寻址能证明收到的是哪些字节,却不能证明这些字节是否值得信任

容器镜像是由不可变内容构成的图。containerd 按加密摘要存储数据块,使相同内容可以复用,并能依据字节身份进行验证。镜像记录将名称和清单连接到这些内容,传输服务则解析注册表引用,并把所需数据块移动到本地存储。

该模型减少了重复数据,并提供了一种稳定方式来说明节点收到了哪些字节。但它不能确定发布者是谁、是否应信任发布者、签名是否可接受,或软件是否包含已知漏洞。恶意镜像完全可以拥有有效摘要。可变标签也可以随时间指向不同内容,同时每一个数据块仍然被正确寻址。

这一区别很重要,因为 containerd 通常位于更广泛的软件供应链系统内部。注册表身份验证、签名策略、软件物料清单、证明材料、漏洞扫描和准入策略由其他组件或下游产品处理。运行时可以维持内容完整性,而不必成为完整的信任框架。

对运营人员而言,实际规则很简单:镜像身份和镜像信任是两项不同的控制。事件分析既应记录实际运行的摘要,也应记录允许其运行的策略。把按摘要寻址的存储称为安全系统,会把两个独立问题混为一谈。

这种分离也有利于可移植性。稳定的内容模型允许不同上层平台使用相同的本地数据,但注册表凭据、信任策略和平台选择方面的差异,仍可能使一个镜像在某组节点上可用、在另一组节点上被阻止。

快照器把不可变镜像转换为可工作的文件系统

镜像可以已经存储,却尚未准备好执行。运行中的容器需要一种文件系统视图,将镜像层与可写状态组合起来。containerd 把这项工作交给快照器插件。

快照器接口将内容分发与文件系统实现分离。快照器可以准备活动、只读视图或已提交的快照,为解包或执行挂载这些快照,并在之后将其删除。常见实现包括基于 overlay 的方式和原生方式,而专用及远程快照器可以改变工作负载启动前需要获取的数据量。

其优势在于模块化。核心守护进程不必为每种文件系统策略重新编写。云服务商、存储供应商和边缘平台可以在共享服务边界后方优化启动速度、磁盘使用或远程访问。

代价是不同快照器的行为并不统一。垃圾回收、挂载语义、配额行为、启动延迟和恢复方式都取决于具体实现。因此,如果一项基准测试把快速冷启动归功于“containerd”,却没有说明镜像路径、注册表、磁盘和快照器,那么这项测试并不完整。

2.3.3 的挂载泄漏修复从故障角度再次说明了同一点。当钩子或生命周期步骤失败时,存储状态必须能够彻底回退。如果快照或挂载被错误引用,在上层平台认为工作负载已经消失之后,内容仍可能留在磁盘上。在大规模节点集群中,清理行为会成为容量问题。

远程和延迟拉取快照器进一步强化了这种取舍。它们可以按需提供更多镜像路径,从而缩短启动延迟或减少本地磁盘使用,但也会使注册表或远程存储的可用性以不同于完全本地镜像的方式参与执行过程。抽象接口保持稳定,其下方的故障模型却发生了变化。

Runtime v2 shim 让任务能够在创建它的守护进程之外继续存活

containerd 通常不会自行执行 Linux 容器进程。它使用运行时 shim 与 runc 等底层运行时通信,由后者利用宿主操作系统原语完成最终的创建和运行工作。

Runtime v2 模型为每个任务或沙箱提供一个中间进程,该进程可以独立于长期运行的 containerd 守护进程继续存在。如果守护进程重启,正在运行的工作负载不一定需要随之终止。containerd 可以重新连接 shim,并根据保存的状态重建监管关系。

这是一项重要的韧性能力。运行时守护进程可以接受补丁或重启,而不必自动让节点上的每个工作负载都发生中断。它还允许多个底层运行时共存于同一个上层 API 后方,其中包括会改变隔离边界的沙箱运行时。

这项保证也有局限。死亡或失去管理的 shim、损坏的本地状态、底层运行时缺陷或宿主机内核故障,仍可能导致监管关系或工作负载本身丢失。节点重启不同于守护进程重启,磁盘损坏也不同于干净的进程重启。该架构支持工作负载在某一类故障中保持连续性,但不会让执行脱离机器而独立存在。

这种分层运行时模型也解释了为何“容器运行时”一词可能含义模糊。containerd 是长期运行的生命周期和状态服务。runc 及其他底层运行时负责创建进程。Kata Containers 或 gVisor 可以改变下层隔离模型,而 containerd 仍作为其上方的协调层。准确归因事件,首先要明确究竟是哪一层发生了故障。

Kubernetes 依赖 containerd,但不会把整个集群交给它

Kubernetes 通过 Container Runtime Interface 连接 containerd。内置的 CRI 插件实现 kubelet 所需的运行时服务和镜像服务,将 Pod 沙箱与容器映射为 containerd 对象,并与已配置的运行时和网络路径协调。

这使 containerd 成为许多 Kubernetes 节点的直接依赖,却没有让它变成 Kubernetes 本身。调度器仍决定 Pod 应在哪里运行。控制器仍负责协调应用的预期状态。kubelet 仍管理节点级意图。集群网络和策略依赖 CNI 实现及其他组件。宿主机内核和底层运行时仍提供隔离进程所需的原语。

发生故障时,这条边界尤其重要。Pod 无法启动,可能是因为 kubelet 配置、CRI 兼容性、镜像缺失、快照器错误、CNI 设置、shim、runc 或内核。简单地把事件报告为“containerd 故障”,可能掩盖真正失败的交接环节。把每个运行时问题都归为“Kubernetes 问题”同样不准确。

兼容性还具有版本维度。Kubernetes 发行版会验证特定的 containerd、CRI 和配置组合。托管服务可能包含下游补丁或延迟版本。上游版本可能本身正确,而云节点镜像仍停留在旧构建上;云服务商也可能回溯安全修复,但不会按照上游用户预期的方式改变版本号。

因此,真正需要关注的是节点物料清单,而不只是项目名称:containerd、底层运行时、CNI 二进制文件、快照器、内核、配置和下游补丁。这套组合才是真正负责启动和删除 Pod 的技术栈。

CNI 和 NRI 通过把更多责任移入节点技术栈,使 containerd 保持精简

containerd 的架构高度依赖组件组合。CRI 路径可以调用外部 Container Network Interface 插件来创建和删除沙箱网络。Node Resource Interface 插件可以观察生命周期事件,并调整被允许修改的资源或运行时设置。快照器和运行时插件则能替换存储与执行组件,而无须重写核心 API。

这正是守护进程能够保持足够精简并被广泛复用的方式。网络专家可以推进 CNI 实现。硬件供应商可以使用 NRI 或相关机制,而不必维护私有分支。存储开发者可以增加远程快照器。沙箱项目可以集成替代运行时。

每个扩展也会增加故障和信任依赖。CNI ADD 或 DEL 失败可能遗留地址、接口或命名空间。不良 NRI 插件可能阻塞生命周期操作,或改变节点上的资源分配。第三方快照器可能泄漏挂载或错误处理垃圾回收。运行时插件可能在一种内核上工作正常,却在另一种内核上失败。

核心项目的成熟度不会自动传递到整个扩展关系图。受支持的 containerd 版本并不代表项目已经认证与它组合使用的每个插件、运行时或配置。运营人员需要掌握每个获准进入节点生命周期的特权扩展的来源、版本、签名、支持路径和回滚方案。

因此,插件关系图既是 containerd 的主要可移植机制,也是其主要组合风险来源之一。项目通过建立扩展点减少维护分支的需求,但代价是必须跨越这些扩展评估生产可靠性,而不能仅根据核心守护进程的声誉作出推断。

命名空间在一个守护进程内组织客户端,但不会建立新的宿主机边界

containerd 命名空间允许不同客户端在同一个守护进程内分组和寻址资源。Docker、CRI 和其他嵌入方可以在逻辑上分离各自的镜像、容器、快照和任务,而不是让它们在一个扁平对象空间中发生冲突。

这有助于维持多客户端环境的整洁,但并不等同于把两个租户隔离在不同机器上。守护进程仍是特权进程,管理套接字仍是高后果接口;除非使用更强的沙箱,普通容器工作负载仍依赖宿主机内核提供隔离。

拥有足够守护进程访问权限的客户端,可能根据其权限和插件行为枚举或操作多个命名空间中的资源。因此,这条边界是一种 API 作用域机制,不能替代 Unix 权限、套接字保护、内核命名空间、cgroup、强制访问控制或虚拟机支持的隔离。

这一区别在商业和技术层面都很重要。平台可以宣传逻辑隔离,同时仍把多个客户放在共享的特权运行时和宿主机内核上。安全依据必须在宿主机和沙箱层建立,不能仅从存在 containerd 命名空间字符串这一事实推断出来。

明确这条边界也有利于项目本身。containerd 可以提供清晰的组织模型,而不假装解决属于技术栈更下层的租户隔离问题。

守护进程套接字应纳入与宿主机管理相同的威胁模型

containerd 有权创建进程、挂载和命名空间,并向下层传递后果重大的运行时规范。在许多系统上,控制守护进程或其套接字可能等同于控制宿主机。

因此,套接字访问、远程暴露、身份验证、审计和插件权限都是核心安全控制。如果把守护进程视为隐藏的实现细节,团队可能会严密保护 Kubernetes API,却较少关注真正负责创建特权进程的节点本地接口。

运行时配置也需要同样谨慎。容器可以请求能力、设备、命名空间和挂载,从而改变它与宿主机的关系。containerd 会把这些规范传递给底层运行时和内核机制。运行时层可以执行已经配置的限制,却无法消除内核漏洞,也不能仅凭使用标准 API 就让不安全的特权规范变得安全。

因此,安全归因需要覆盖多个层次。containerd API 处理漏洞不同于 runc 逃逸、内核缺陷、不安全的 CNI 插件或权限过高的 Kubernetes 工作负载。上游 containerd 修复并不能证明所有下游节点都已受到保护,而安全的 containerd 版本也无法让存在漏洞的内核变得安全。

2026 年 6 月 18 日发布的 2.3.2 版除其他运行时修复外,还包含针对五个已列出 containerd CVE 的补丁。正确的运营问题不是最新版是否“安全”,而是哪个安全通告影响已部署配置、哪个构建包含修复,以及该构建何时真正到达运行中的节点。

事件与垃圾回收让状态协调成为持续性工作

长期运行的运行时必须保存足够状态,以便从被中断的操作中恢复,同时回收不再需要的资源。containerd 的元数据、事件和垃圾回收机制为这项工作提供支持。

事件使编排系统和监测工具能够响应生命周期和内容变化,而无须持续轮询每个对象。它们有助于状态协调和可观测性,但使用方不应把事件流当作顺序绝对可靠、永久持久化的数据库。重新连接逻辑仍需查询当前状态,并从遗漏或顺序错乱的观察结果中恢复。

租约、标签和元数据引用有助于保护仍在使用的内容与快照,同时允许系统收集无法再访问的资源。这套机制可以控制大量使用镜像的节点集群中的磁盘增长,也可能在两个方向上出错:泄漏的引用会无限期保留数据,而错误引用可能让内容遭到过早清理。

因此,即使应用的内存和 CPU 看起来正常,磁盘压力仍属于运行时问题。镜像拉取、已解包的层、可写快照和陈旧状态都会争用宿主机存储。一个无法拉取、解包或清理镜像的节点,可能早在宿主机本身发生故障之前就已无法供调度器使用。

这也是 containerd 应依据包含删除和恢复能力的节点级服务目标来运营的原因。平台不仅需要知道 Pod 启动有多快,还需要确定失败操作是否会让机器保持在能够安全复用的状态。

2.3 LTS 系列把发布工程变成运营契约

随着项目进入 2.x 时代,containerd 的发布政策变得更加明确。1.6 系列始于 2022 年 2 月 15 日,1.7 LTS 系列于 2023 年 3 月 10 日发布。2.0 版于 2024 年 11 月 5 日到来,2.1 和 2.2 在 2025 年继续推进这一过渡。

2026 年 4 月 30 日,项目发布 containerd 2.3,并将其指定为当前长期支持系列,计划支持至 2028 年 4 月 30 日。项目还转为每四个月发布一个次要版本,并公布平台等级、API 稳定性预期和受支持的升级路径。

这是基础设施政策,而不是代码仓库整理工作。云服务商和 Kubernetes 发行版需要知道一个分支会接收修复多久、哪些升级顺序预计可以正常工作,以及项目能够持续测试哪些平台。LTS 状态使运营人员能够根据明确的支持期限规划节点镜像维护,而不是从提交活动中推断支持情况。

这项契约具有明确边界。稳定性保证适用于已有文档的 API 和受支持平台,ctr不在承诺范围内。第三方插件不会自动受到覆盖。下游发行版可以回溯、延迟或修改版本。项目层面的支持期限并不能告诉企业,其托管云服务商何时会替换存在漏洞的节点镜像。

截至本文 2026 年 8 月 6 日的研究截止日期,2.3.3 是已核实的最新稳定版本,之前是 6 月包含安全修复的 2.3.2 补丁。2.4 版暂定于 2026 年 8 月 26 日发布。该日期只是计划,而不是已经完成的发布;如果文章在截止日期后刊发,应在发布前更新。

这种发布模式使隐藏依赖更容易治理。其成效将体现在回溯补丁质量、2.3 分支直至 2028 年的健康状况,以及下游节点集群吸收重要修复所需的时间上。

平台等级揭示可移植性如何依赖持续的测试能力

containerd 支持不止一种运行环境,但支持并不意味着所有架构的行为完全一致。截至研究截止日期,一级平台包括linux/amd64linux/arm64windows/amd64

平台等级反映的是持续维护的功能测试和项目能力。Windows 容器采用不同于 Linux 的宿主机原语和运行时路径。文件系统语义、进程隔离和持续集成覆盖范围也有所不同。因此,共同 API 中存在的功能,在不同平台系列上可能拥有不同的成熟度或故障行为。

平台等级政策既是技术文件,也反映资源配置。只有维护者拥有可靠的运行环境、硬件、测试和能够在测试失败时响应的人员,一个平台才能保持一级地位。可移植性依赖支撑兼容性承诺的持续基础设施。

这对采购方有两项影响。第一,项目功能必须针对确切的受支持平台和运行时组合进行评估。第二,平台等级的提升或降低具有实质意义,因为它代表项目保障该分支能力的变化,而不仅是文档编辑。

同一原则也适用于专用硬件集成。守护进程可以提供通用扩展点,但特定 GPU、存储或网络路径的生产质量取决于其他地方维护的代码和测试。

CNCF 建立了中立上游,但已部署的 containerd 仍有众多责任方

containerd 作为 CNCF 项目,通过维护者、提交者、发布负责人、治理记录和安全流程接受治理。项目没有传统意义上的董事会、股东或高管团队。权力来自项目角色和贡献流程,而不是股权所有关系。

这种模式使相互竞争的公司都能使用该运行时。云服务商无须向另一家云服务商购买 containerd。Linux 发行商可以打包它,Docker 可以嵌入它,Kubernetes 供应商可以验证它,受雇于不同机构的维护者也可以共同参与同一上游代码。

中立治理并不意味着雇主影响消失。工程能力主要来自供应商付薪的人员,或来自能够从该运行时获益的机构所作贡献。持续集成、发布管理和安全工作都需要时间与基础设施。公开治理记录能够显示正式角色,却无法完整衡量非正式路线图影响或私下商业优先事项。

部署控制更加分散。上游维护者决定哪些内容进入正式版本。Linux 发行版决定打包和回溯哪些内容。云服务商决定哪个构建进入节点镜像,以及该镜像何时提供给客户。集群运营人员决定何时停止节点承载工作负载并替换运行中的节点。因此,同一个项目在同一时期可能以多个存在实质差异的生产构建出现。

这种分层控制对事件报告至关重要。上游发布日期并不等于整个市场完成补丁部署的日期。云服务通告可能描述已经修复的节点集群,即使公开版本号与上游不同。可靠答案只能来自对具体构建及其发布路径的追踪。

containerd 创造经济价值,却没有常规收入项目

containerd 不是一家拥有公开损益表的独立产品公司。现有证据中没有 containerd 收入数据、企业估值或经审计的部署数量。CNCF 托管该项目,雇主通过员工工时为大量工程工作提供资金,下游公司则从嵌入该运行时的产品和服务中获得收入。

其经济价值主要体现为避免重复投入。Docker、Kubernetes 供应商、云服务商和发行版可以共享镜像与生命周期机制,而不必分别资助完全不同的守护进程。一项缺陷修复可以传播到多个产品,稳定 API 则能降低多年维护集成的成本。

这种公共产品结构也带来可持续性问题。许多公司可以依赖 containerd,却不必按照依赖程度投入相应工程资源。新版本的热度消退很久之后,LTS 分支仍需要发布负责人、持续集成、回溯工作和安全响应。平台专项测试则需要硬件和维护者。嵌入式使用让项目很有价值,也让其直接预算难以被外界看见。

因此,即使没有公开预算,2.3 LTS 承诺也具有财务维度。一个两年期分支需要持续劳动。评估这项承诺的健康状况,应依据发布责任、补丁节奏、测试覆盖和贡献者多样性,而不是虚构项目收入估算。

完整部署普查同样不存在。项目在 Docker、Kubernetes 和云产品中的广泛使用可由其角色看出,但这不足以支持精确的市场份额主张。最稳妥的描述是 containerd 被广泛嵌入且影响重大,而不是它运行着全球某个已知比例的容器。

只有先明确技术边界,才能判断哪些方案是 containerd 的替代者

容器运行时比较经常混合位于不同层次的产品。CRI-O 是面向 Kubernetes 的 CRI 部署中的直接替代方案。Docker Engine 是范围更广、面向用户的平台,它嵌入 containerd,而不是以相同范围替换技术栈的每一层。Podman 和 libpod 技术栈采用不同的用户与守护进程模型。runc 是通常位于 containerd 下方的底层 OCI 运行时,而不是它的直接竞争者。

Kata Containers 和 gVisor 改变生命周期协调层下方的隔离模型。它们可以通过运行时集成执行,而 containerd 仍处理上层镜像和任务服务。Kubernetes 本身是位于节点运行时上方的编排系统。Open Container Initiative 定义技术栈使用的规范,而不负责运行容器。

这些区别很重要,因为替换一个组件不会消除所有依赖。从 containerd 转向另一种 CRI 运行时,会影响节点镜像、兼容性测试、快照存储、运行时配置和运营工具。从 runc 转向由虚拟机支持的沙箱,则会改变另一条边界。替换 Docker Engine 后,最终架构中仍可能存在 containerd。

因此,最有用的是功能性比较:要替换的是哪一层?哪些状态必须迁移?哪些运营工具假设旧 API 或对象模型存在?哪些故障模式会改变?笼统的“运行时市场”概念掩盖的信息多于其能够解释的内容。

containerd 的优势并不在于没有替代方案,而在于许多产品已经围绕其中间稳定层积累了集成代码、运营知识和测试能力。即使软件许可本身没有造成法律锁定,这些既有知识仍会形成切换成本。

真正的可移植性测试始于操作中途停止之时

containerd 的设计建立在有效的职责分离之上。守护进程管理持久运行时状态和服务。快照器准备文件系统。shim 协调活动任务。底层运行时创建进程。CNI 配置网络。Kubernetes 或其他上层系统决定应运行什么。内核提供实际的隔离原语。

这种分工让项目保持足够精简并被广泛复用,也意味着没有任何单一组件能够保证整体结果。容器可能因为无法解析镜像、快照器无法挂载、CNI 无法清理、shim 消失、运行时拒绝规范,或内核拒绝操作而失败。

成熟的节点运行时因此必须让故障变得可理解。运营人员需要知道操作停止前哪些状态已经改变、哪些资源仍然存在、重试是否安全,以及节点能否在不重新构建的情况下恢复服务。错误处理、任务状态、事件、清理和恢复与正常启动路径同样重要。

这是理解 containerd 长期成就的最佳方式。它建立了一个通用中间层,却没有把该层变成完整的容器平台。只要这套共享机制足够稳定以供嵌入、足够透明以便调试,并且具有足够可替换性,不让普及程度成为接受隐性运营负担的理由,该项目就会继续保持价值。