news 2026/9/13 4:39:52

containerd CRI 集成设计全解析:从 2017 年架构提案到内置 CRI 插件的落地之路

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
containerd CRI 集成设计全解析:从 2017 年架构提案到内置 CRI 插件的落地之路

containerd CRI 集成设计全解析:从 2017 年架构提案到内置 CRI 插件的落地之路

【免费下载链接】containerdAn open and reliable container runtime项目地址: https://gitcode.com/GitHub_Trending/co/containerd

本文以 docs/historical/cri/proposal.md 这份历史设计提案为骨架,系统梳理 containerd 通过容器运行时接口(CRI)与 Kubernetes Kubelet 集成的完整方案:包括动机权衡、容器生命周期/日志/流式/网络/指标/镜像六大能力设计、范围外事项与里程碑路线图,并结合当前仓库中的 CRI 插件源码、配置指南与测试实践,展示提案从纸面走向生产实现的演进脉络。读完本文,你将掌握 CRI-containerd 的架构职责边界、关键实现机制,以及如何在当前 containerd 中以 CRI 插件方式启用并配置它。

一、提案的历史坐标:它要解决什么问题

该提案由 Lantao Liu(@random-liu)撰写,收录于仓库历史文档目录 docs/historical/cri/proposal.md,核心目标是:将 containerd 作为 Kubernetes Kubelet 的容器运行时,并通过 CRI 接口完成集成。提案中把这一集成方案命名为CRI-containerd

要理解这份提案,需要先回顾 containerd 的出身:

  • containerd 是一个核心容器运行时,只提供管理宿主机上容器完整生命周期所需的最小功能集,包括容器执行与监督(container execution and supervision)、镜像分发与存储(image distribution and storage)等;
  • containerd 于 Docker 1.11 时代被引入(2016 年 4 月),用于管理节点上的 runC 容器。它的经典架构是:为每个容器创建一个 containerd-shim,由 shim 负责对应容器的生命周期管理
  • 2016 年 12 月,Docker Inc. 将 containerd 剥离为独立组件;2017 年 3 月,containerd 被捐赠给 CNCF。

下图(来自提案原文,containerd.png)展示了 Docker Engine → containerd → 多个 containerd-shim → 多个 runc 的分层管理关系:

当时 Kubernetes 通过dockershim+ Docker 来对接容器运行时,而 CRI(Container Runtime Interface)正逐渐成为 kubelet 与运行时之间的标准接口。containerd 作为 Docker 的"子集",天然具备成为独立运行时的条件——这正是这份提案要论证并设计的事情。

二、动机权衡:相比 Docker,containerd 的利与弊

提案明确指出:containerd 是 Kubernetes 集群运行时中 Docker 的潜在替代者,并给出了系统的优劣分析,这构成了整个设计决策的基石。

2.1 优势(Pros)

  • 稳定性(Stability):containerd 功能范围有限、特性演进速度更慢,因此预期更加稳定;
  • 兼容性(Compatibility):containerd 的范围与 Kubernetes 的需求高度对齐,既提供了所需功能,又在镜像拉取、网络、存储卷、日志等领域保留了灵活性;
  • 性能(Performance)
    • 至少因为 containerd 是 Docker 的子集,其资源消耗小于 Docker;
    • CRI 集成消除了调用栈中的额外一跳。如下图所示(performance.png),上方的kubelet → dockershim → docker → containerd路径被下方的kubelet → CRI-containerd → containerd直接路径取代:

  • 中立基金会(Neutral Foundation):containerd 已属于 CNCF,避免了厂商锁定顾虑。

2.2 劣势(Cons)

  • 用户采用(User Adoption):理想情况下 Kubernetes 用户不直接与底层运行时交互,但由于缺乏调试工具,用户有时仍需登录节点用 Docker CLI 调试。containerd 虽然提供了面向开发和调试的极简 CLIctr(当前位于 cmd/ctr),但它可能不够充分,且需要投入时间和计划来完善这些 CLI 的文档并教育用户;
  • 成熟度(Maturity):重新划清边界的 containerd 还很新,处于重度开发之中。

这段"利弊清单"直接推导出了后文的三大 Goals 与具体设计范围,是理解整个提案的钥匙。

三、目标与总体设计:CRI-containerd 的职责边界

3.1 三大目标(Goals)

  1. 确保 containerd 满足 Kubernetes 现在及可预见未来的需求;
  2. 实现 containerd CRI shim,使其提供等价的功能、可用性和可调试性
  3. 利用 containerd 提供的灵活性来改进 Kubernetes 本身。

3.2 核心设计哲学:API 翻译 + 元数据自维护

提案给出了一个非常关键的设计定位:

CRI-containerd 依赖 containerd 管理容器生命周期。理想情况下,CRI-containerd 只需要做API 翻译和信息重组

但理想之外,CRI-containerd 必须自行维护一部分元数据,原因有二:

  • 生命周期模型不匹配:containerd 只跟踪运行中的进程。一旦容器及其对应的 containerd-shim 退出,该容器在 containerd API 中就不再可见,而 CRI 需要查询已退出容器的状态;
  • 部分元数据 containerd 不提供:例如沙箱/容器的 labels/annotations、容器的PodSandboxIDFinishedAt时间戳、ExitCodeMounts等。由于生命周期不匹配,这些信息也无法借助 OCI 运行时注解(runtime annotation)来持久化。

因此提案的结论是:CRI-containerd 应自行 checkpoint 这些元数据,或在可用时使用 containerd 的元数据服务

这一设计在今天依然成立:当前 CRI 服务维护了自己的 containerStore、sandboxStore 等内存元数据缓存(参见 internal/cri/server 下的 sandbox_service.go、container_status.go 等实现),并将容器记录持久化到 containerd 的元数据存储中(core/metadata),从而弥补"进程退出即不可见"的缺口。

四、六大能力设计详解

4.1 容器生命周期(Container Lifecycle)

容器生命周期完全托管给 containerd。CRI-containerd 只做接口翻译与信息重组,同时把 3.2 节提到的补充元数据(退出码、完成时间、挂载点等)持久化下来,保证PodSandboxStatusContainerStatus等 CRI 查询接口在容器退出后依然能返回完整状态。

当前实现中,CRI 服务在启动时通过插件系统获取 runtime 与 images 两个 CRI 子服务,再组装出完整的 gRPC CRI 服务(见 plugins/cri/cri.go),生命周期相关的CreateContainerStartContainerStopContainerRemoveContainer等处理逻辑集中在 internal/cri/server 的 container_create.go、container_start.go、container_stop.go、container_remove.go 等文件中。

4.2 容器日志(Container Logging)

提案指出:containerd 不提供持久化的容器日志,它把容器 STDIO 重定向到不同的 FIFO 中。因此 CRI-containerd 需要启动一个 goroutine(未来可演进为独立进程/容器)来:

  1. 持续排空(drain)FIFO;
  2. 将日志行装饰为 CRI 定义的日志格式;
  3. 将日志写入 CRI 定义的日志路径。

这一机制在当前实现中依然清晰可见:internal/cri/io/container_io.go 通过WithNewFIFOs创建容器 IO 的 FIFO 集合(newFifos),CRI 服务则负责读取并转发。日志行长度限制由配置项max_container_log_line_size控制(默认 16384 字节,超长行会被拆分),详见下文配置章节。

4.3 容器流式接口(Container Streaming)

containerd 通过Exec支持在容器内创建进程,且 STDIO 同样以 FIFO 暴露;通过Pty支持调整指定进程控制台尺寸。提案建议 CRI-containerd 复用 Kubernetes 的 streaming server,并实现 streaming runtime 接口。四种 CRI 流式功能的具体设计为:

CRI 流式功能提案设计
ExecSyncExec创建执行进程,收集进程的 stdout/stderr,等待进程终止
ExecExec创建执行进程,启动 goroutine(进程/容器)转发流,等待进程终止
Attach启动 goroutine 将已有容器日志读到输出,转发 init 进程的流,等待任一流关闭
PortForward借助socatnsenter实现,与当时 Docker 的 portforward 实现类似

当前仓库中的实现与提案一一对应:

  • Exec先检查容器处于 RUNNING 状态,再交给流式服务器处理(container_exec.go);
  • Attach加载容器任务(task),支持通过task.Resize处理终端尺寸调整,并以AttachOptions转发 stdin/stdout/stderr(container_attach.go);
  • PortForward在 Linux 上进入沙箱网络命名空间后,向命名空间内的 localhost 端口发起 TCP 连接(IPv4 优先、IPv6 回退),再用两个 goroutine 在客户端流与命名空间连接之间双向io.Copy,并处理超时与取消(sandbox_portforward_linux.go)。这正对应提案中"借助 nsenter 进入命名空间 + socat 式双向转发"的思路。

4.4 容器网络(Container Networking)

containerd 本身不提供容器网络,但OCI 运行时规范支持将 Linux 容器加入既有网络命名空间。基于此,CRI-containerd 需要:

  1. 为沙箱(sandbox)创建一个网络命名空间;
  2. 调用网络插件来更新该网络命名空间的配置选项(即调用 CNI 插件);
  3. 让同一沙箱内的用户容器共享该网络命名空间。

这套"以 pause 容器/沙箱网络命名空间为中心、同 Pod 容器共享 netns"的模型正是现代 Kubernetes Pod 网络的基础。当前 CRI 插件通过 CNI 实现沙箱网络配置:bin_dir/bin_dirs(CNI 插件二进制目录,默认/opt/cni/bin)、conf_dir(CNI 配置目录,默认/etc/cni/net.d)、max_conf_numconf_templateip_pref等配置项均可在 CRI 插件配置中调整(见 docs/cri/config.md)。

4.5 容器指标(Container Metrics)

提案指出 containerd 提供容器 cgroup 指标(当时的最新进展可参见 docs/historical/reports/2017-03-17.md 中列出的containerd_container_cpu_total_nanosecondscontainerd_container_memory_rss_bytescontainerd_container_pids_current等 Prometheus 指标样例),并计划提供容器可写层磁盘用量。CRI 容器指标 API 需要先在 Kubernetes 侧定义(issue #27097),之后 CRI-containerd 再把 containerd 指标翻译为 CRI 容器指标。

如今这部分已经落地为 CRI 的ContainerStats/PodSandboxStats能力,实现在 internal/cri/server 的 container_stats.go、sandbox_stats.go 以及 stats_collector.go 中,并由stats_collect_periodstats_retention_period等配置控制采集周期与保留时长。

4.6 镜像管理与 ImageFS 指标

  • 镜像管理(Image Management):CRI-containerd 依赖 containerd 管理镜像,containerd 应提供 CRI 所需的全部功能与信息,CRI-containerd 只做 API 翻译与信息重组;
  • ImageFS 指标(Image Filesystem Metrics):containerd 计划提供镜像文件系统指标。同样地,CRI 镜像文件系统指标 API 需要先在 Kubernetes 侧定义(issue #33048),之后确保 containerd 提供所需指标,再由 CRI-containerd 翻译为 CRI 指标。

五、明确排除在外的范围(Out of Scope)

提案把以下事项明确划出设计范围,留待后续版本作为增强或优化:

  • 可调试性(Debuggability):这是 CRI-containerd 最大的担忧之一。计划通过kubectlcri-tools或 containerd CLI 提供与 Docker CLI 等价的可调试能力;
  • 内置 CRI 支持(Built-in CRI support):containerd 的插件模型使得直接把 CRI 以插件形式内建进 containerd 成为可能,可再减少调用栈中的一跳。但由于当时 golang 插件的限制(issue #563),要么维护自有分支,要么把 CRI 插件推到上游;
  • Seccomp(issue #36997):OCI 运行时规范已支持 seccomp,但当时 Kubernetes 的 seccomp 实现是实验性的且与 Docker 绑定,需要先在 CRI 中定义 API;
  • 流式服务器认证(Streaming server authentication)(issue #36666):CRI-containerd 与 Kubelet 是独立进程,无法复用 Kubelet 的认证,其流式服务器应实现自己的认证机制;
  • 把容器设施移入 Pod cgroup:镜像拉取器、流式处理器、日志处理器、containerd-shim 等服务于特定容器的设施应移入对应 Pod 的 cgroup,其开销计入 Pod;
  • 日志轮转(Log rotation)(issue #42718):CRI 中可能增加一个通知运行时重开日志文件的函数,CRI-containerd 应在该函数定义后实现它;
  • Exec 容器:利用 containerd 的灵活性,可以用一个与原始容器共享同一 rootfs 和 mount namespace 的独立容器来实现Exec,其好处是 Exec 容器拥有独立子 cgroup,不消耗应用容器资源,且可为其指定专用资源;
  • 高级镜像管理(Advanced image management):由于当时 Kubelet 镜像管理需求范围尚未明确,CRI 的镜像管理接口相对简单,未来希望更多利用 containerd 的灵活性(例如拉取前估算镜像大小)。

有趣的是,若干年后这份"范围外"清单中的多项已经成为现实:CRI 已作为内置插件直接编译进 containerd(见 plugins/cri/cri.go 中的插件注册,plugins.GRPCPlugin类型、ID 为cri,依赖 RuntimeService、ImageService、SandboxController、NRI 等一揽子插件);日志轮转已通过 CRI 的ReopenContainerLog接口实现(container_log_reopen.go);流式服务器认证已通过enable_tls_streamingx509_key_pair_streaming配置获得 TLS 支持。

六、路线图与里程碑:提案的时间表

6.1 里程碑(Milestones)

Kubernetes 1.7(Q2)

  • [P0] 基础容器生命周期;
  • [P0] 基础镜像管理;
  • [P0] 容器网络;
  • [P1] 容器流式/日志;
  • [P2] 容器/ImageFS 指标。

测试计划:每个新增功能都应附带单元测试,并通过其对应的 CRI 一致性验证测试(cri validation test)。

这条测试计划在今日依然有效:CRI 插件的验证测试与 cri-tools 集成测试说明可参考 docs/cri/testing.md。

Kubernetes 1.8(Q3)

  • [P0] 功能完整,通过 100% 的 CRI 一致性验证测试;
  • [P0] 将 CRI-containerd 与 Kubernetes 集成,并搭建 e2e / node e2e 测试框架;
  • [P1] 解决可调试性问题。

6.2 Q2 路线图(Roadmap)

Item1/2 Mar.2/2 Mar.1/2 Apr.2/2 Apr.1/2 May.2/2 May.
Survey
POC
Proposal
Containerd Feature Complete
Runtime Management Integration
Image Management Integration
Container Networking Integration

从时间表可见,提案遵循"调研 → POC → 提案 → 运行时/镜像/网络逐项集成"的节奏推进,这也解释了为什么该文档被收录在 docs/historical 目录——它完整记录了 CRI 集成从 0 到 1 的历史决策过程。

七、从提案到现实:当前仓库中的落地对照

提案中的架构设想,在今天仓库中的对应实现如下:

提案设计点当前落地位置
CRI-containerd 作为独立 shimCRI 已内置为 containerd 插件:plugins/cri/cri.go(以及 plugins/cri/runtime/plugin.go、plugins/cri/images/plugin.go)
元数据自维护,弥补生命周期不匹配internal/cri/server 的容器/沙箱 store 与 core/metadata 持久化
日志:排空 FIFO、CRI 格式、CRI 路径internal/cri/io/container_io.go,日志行大小受max_container_log_line_size约束
Exec / ExecSync / Attach / PortForwardcontainer_exec.go、container_execsync.go、container_attach.go、sandbox_portforward_linux.go
沙箱网络命名空间 + CNI 插件CRI 插件cni配置段,见 docs/cri/config.md
容器/ImageFS 指标翻译container_stats.go、sandbox_stats.go、stats_collector.go
镜像管理 API 翻译plugins/cri/images/plugin.go
流式服务器认证(原 Out of Scope)enable_tls_streaming+x509_key_pair_streaming配置项
日志轮转(原 Out of Scope)container_log_reopen.go

八、如何启用与配置:CRI 插件配置速览

提案落地后,CRI 插件配置是 containerd 全局配置的一部分(默认路径/etc/containerd/config.toml),完整配置项说明见 docs/cri/config.md。需要特别注意的是,[plugins."io.containerd.grpc.v1.cri"](containerd 1.x)或[plugins.'io.containerd.cri.v1.runtime']/[plugins.'io.containerd.cri.v1.images'](containerd 2.x)这些配置段仅对 CRI 生效ctrnerdctl、Docker/Moby 等其他 containerd 客户端并不识别。

提案中讨论过的"流式服务器"相关配置,在 containerd 2.x 中示例如下:

version = 3 [plugins.'io.containerd.grpc.v1.cri'] # 是否禁用 TCP 上的 CRI 服务(默认 true) disable_tcp_service = true # 流式服务器监听地址(kubelet 会访问它来执行 exec/attach/portforward) stream_server_address = '127.0.0.1' # 流式服务器监听端口,'0' 表示自动分配 stream_server_port = '0' # 流式连接空闲超时 stream_idle_timeout = '4h0m0s' # 是否启用 TLS 流式(对应提案中"流式服务器认证"这一范围外事项) enable_tls_streaming = false [plugins.'io.containerd.grpc.v1.cri'.x509_key_pair_streaming] tls_cert_file = '' tls_key_file = ''

提案中"沙箱网络命名空间 + 网络插件"设计对应的 CNI 配置段(containerd 2.x):

[plugins.'io.containerd.cri.v1.runtime'.cni] # 已废弃,改用 bin_dirs(自 containerd v2.1 起) bin_dir = '' bin_dirs = ['/opt/cni/bin'] conf_dir = '/etc/cni/net.d' max_conf_num = 1 setup_serially = false conf_template = '' ip_pref = '' use_internal_loopback = false

提案中"镜像管理依赖 containerd"对应的镜像配置段(containerd 2.x):

[plugins.'io.containerd.cri.v1.images'] snapshotter = 'overlayfs' max_concurrent_downloads = 3 image_pull_progress_timeout = '5m0s' stats_collect_period = 10 [plugins.'io.containerd.cri.v1.images'.pinned_images] sandbox = 'registry.k8s.io/pause:3.10.2'

注意配置文件必须以版本头开始(containerd 2.x 使用version = 3,containerd 1.x 使用version = 2并会自动转换),插件 ID 在 v3 中有所变化——这正是提案中"内置 CRI 支持"从设想变为现实后带来的配置演进。

结语

这份 2017 年的提案完整回答了三个问题:为什么用 containerd 替代 Docker(稳定、兼容、少一跳性能开销、中立基金会)、做什么(生命周期、日志、流式、网络、指标、镜像六大能力的 API 翻译与元数据补齐)、不做什么(可调试性、内置插件化、seccomp、认证等九项范围外事项及其演进路径)。

从今天的仓库回看,提案中的几乎每一项设计都找到了对应的实现:CRI 插件已内置于 containerd(plugins/cri/cri.go),日志/流式/网络/指标的实现逻辑集中在 internal/cri/server,配置入口与完整参数说明见 docs/cri/config.md。对于希望深入理解 Kubernetes 容器运行时抽象、或者排查 containerd 作为 CRI 运行时各类问题的开发者而言,这份提案 + 当前源码 + 配置文档构成了完整的"设计—实现—运维"知识闭环。

【免费下载链接】containerdAn open and reliable container runtime项目地址: https://gitcode.com/GitHub_Trending/co/containerd

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 4:38:21

大模型‘中间失焦’现象解析:Lost in the Middle原理与工程应对

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 4:36:39

嵌入式Linux开发必备指令集与实战技巧

1. 嵌入式Linux操作指令概述在嵌入式Linux开发中,命令行操作是开发者必须掌握的核心技能。与桌面版Linux相比,嵌入式系统通常资源有限,且需要针对特定硬件进行优化,因此其指令集和使用场景也有独特之处。嵌入式Linux指令主要分为以…

作者头像 李华
网站建设 2026/9/13 4:34:10

Arduino红外协议解析:从NEC解码到万能遥控器实战

1. 这不是“遥控器驱动”,而是一套红外通信的底层操作系统你手头那块Arduino Uno,插着一个38kHz红外接收头,对着电视遥控器按一下——串口监视器突然跳出一串十六进制数字:0x2FD807F。你兴奋地复制粘贴进代码里,写了个…

作者头像 李华
网站建设 2026/9/13 4:34:01

MBD在BMS开发中的应用与优化实践

1. MBD与BMS的跨界融合:一场技术革命的开端在汽车电子领域摸爬滚打十几年,我见证了电池管理系统(BMS)从简单的电压监测到如今复杂的状态估算、均衡控制、热管理的演进过程。而Model-Based Development(MBD)…

作者头像 李华