
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 Liurandom-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 来对接容器运行时而 CRIContainer Runtime Interface正逐渐成为 kubelet 与运行时之间的标准接口。containerd 作为 Docker 的子集天然具备成为独立运行时的条件——这正是这份提案要论证并设计的事情。二、动机权衡相比 Dockercontainerd 的利与弊提案明确指出containerd 是 Kubernetes 集群运行时中 Docker 的潜在替代者并给出了系统的优劣分析这构成了整个设计决策的基石。2.1 优势Pros稳定性Stabilitycontainerd 功能范围有限、特性演进速度更慢因此预期更加稳定兼容性Compatibilitycontainerd 的范围与 Kubernetes 的需求高度对齐既提供了所需功能又在镜像拉取、网络、存储卷、日志等领域保留了灵活性性能Performance至少因为 containerd 是 Docker 的子集其资源消耗小于 DockerCRI 集成消除了调用栈中的额外一跳。如下图所示performance.png上方的kubelet → dockershim → docker → containerd路径被下方的kubelet → CRI-containerd → containerd直接路径取代中立基金会Neutral Foundationcontainerd 已属于 CNCF避免了厂商锁定顾虑。2.2 劣势Cons用户采用User Adoption理想情况下 Kubernetes 用户不直接与底层运行时交互但由于缺乏调试工具用户有时仍需登录节点用 Docker CLI 调试。containerd 虽然提供了面向开发和调试的极简 CLIctr当前位于 cmd/ctr但它可能不够充分且需要投入时间和计划来完善这些 CLI 的文档并教育用户成熟度Maturity重新划清边界的 containerd 还很新处于重度开发之中。这段利弊清单直接推导出了后文的三大 Goals 与具体设计范围是理解整个提案的钥匙。三、目标与总体设计CRI-containerd 的职责边界3.1 三大目标Goals确保 containerd 满足 Kubernetes 现在及可预见未来的需求实现 containerd CRI shim使其提供等价的功能、可用性和可调试性利用 containerd 提供的灵活性来改进 Kubernetes 本身。3.2 核心设计哲学API 翻译 元数据自维护提案给出了一个非常关键的设计定位CRI-containerd 依赖 containerd 管理容器生命周期。理想情况下CRI-containerd 只需要做API 翻译和信息重组。但理想之外CRI-containerd 必须自行维护一部分元数据原因有二生命周期模型不匹配containerd 只跟踪运行中的进程。一旦容器及其对应的 containerd-shim 退出该容器在 containerd API 中就不再可见而 CRI 需要查询已退出容器的状态部分元数据 containerd 不提供例如沙箱/容器的 labels/annotations、容器的PodSandboxID、FinishedAt时间戳、ExitCode、Mounts等。由于生命周期不匹配这些信息也无法借助 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 节提到的补充元数据退出码、完成时间、挂载点等持久化下来保证PodSandboxStatus、ContainerStatus等 CRI 查询接口在容器退出后依然能返回完整状态。当前实现中CRI 服务在启动时通过插件系统获取 runtime 与 images 两个 CRI 子服务再组装出完整的 gRPC CRI 服务见 plugins/cri/cri.go生命周期相关的CreateContainer、StartContainer、StopContainer、RemoveContainer等处理逻辑集中在 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未来可演进为独立进程/容器来持续排空drainFIFO将日志行装饰为 CRI 定义的日志格式将日志写入 CRI 定义的日志路径。这一机制在当前实现中依然清晰可见internal/cri/io/container_io.go 通过WithNewFIFOs创建容器 IO 的 FIFO 集合newFifosCRI 服务则负责读取并转发。日志行长度限制由配置项max_container_log_line_size控制默认 16384 字节超长行会被拆分详见下文配置章节。4.3 容器流式接口Container Streamingcontainerd 通过Exec支持在容器内创建进程且 STDIO 同样以 FIFO 暴露通过Pty支持调整指定进程控制台尺寸。提案建议 CRI-containerd 复用 Kubernetes 的 streaming server并实现 streaming runtime 接口。四种 CRI 流式功能的具体设计为CRI 流式功能提案设计ExecSync用Exec创建执行进程收集进程的 stdout/stderr等待进程终止Exec用Exec创建执行进程启动 goroutine进程/容器转发流等待进程终止Attach启动 goroutine 将已有容器日志读到输出转发 init 进程的流等待任一流关闭PortForward借助socat与nsenter实现与当时 Docker 的 portforward 实现类似当前仓库中的实现与提案一一对应Exec先检查容器处于 RUNNING 状态再交给流式服务器处理container_exec.goAttach加载容器任务task支持通过task.Resize处理终端尺寸调整并以AttachOptions转发 stdin/stdout/stderrcontainer_attach.goPortForward在 Linux 上进入沙箱网络命名空间后向命名空间内的 localhost 端口发起 TCP 连接IPv4 优先、IPv6 回退再用两个 goroutine 在客户端流与命名空间连接之间双向io.Copy并处理超时与取消sandbox_portforward_linux.go。这正对应提案中借助 nsenter 进入命名空间 socat 式双向转发的思路。4.4 容器网络Container Networkingcontainerd 本身不提供容器网络但OCI 运行时规范支持将 Linux 容器加入既有网络命名空间。基于此CRI-containerd 需要为沙箱sandbox创建一个网络命名空间调用网络插件来更新该网络命名空间的配置选项即调用 CNI 插件让同一沙箱内的用户容器共享该网络命名空间。这套以 pause 容器/沙箱网络命名空间为中心、同 Pod 容器共享 netns的模型正是现代 Kubernetes Pod 网络的基础。当前 CRI 插件通过 CNI 实现沙箱网络配置bin_dir/bin_dirsCNI 插件二进制目录默认/opt/cni/bin、conf_dirCNI 配置目录默认/etc/cni/net.d、max_conf_num、conf_template、ip_pref等配置项均可在 CRI 插件配置中调整见 docs/cri/config.md。4.5 容器指标Container Metrics提案指出 containerd 提供容器 cgroup 指标当时的最新进展可参见 docs/historical/reports/2017-03-17.md 中列出的containerd_container_cpu_total_nanoseconds、containerd_container_memory_rss_bytes、containerd_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_period、stats_retention_period等配置控制采集周期与保留时长。4.6 镜像管理与 ImageFS 指标镜像管理Image ManagementCRI-containerd 依赖 containerd 管理镜像containerd 应提供 CRI 所需的全部功能与信息CRI-containerd 只做 API 翻译与信息重组ImageFS 指标Image Filesystem Metricscontainerd 计划提供镜像文件系统指标。同样地CRI 镜像文件系统指标 API 需要先在 Kubernetes 侧定义issue #33048之后确保 containerd 提供所需指标再由 CRI-containerd 翻译为 CRI 指标。五、明确排除在外的范围Out of Scope提案把以下事项明确划出设计范围留待后续版本作为增强或优化可调试性Debuggability这是 CRI-containerd 最大的担忧之一。计划通过kubectl、cri-tools或 containerd CLI 提供与 Docker CLI 等价的可调试能力内置 CRI 支持Built-in CRI supportcontainerd 的插件模型使得直接把 CRI 以插件形式内建进 containerd 成为可能可再减少调用栈中的一跳。但由于当时 golang 插件的限制issue #563要么维护自有分支要么把 CRI 插件推到上游Seccompissue #36997OCI 运行时规范已支持 seccomp但当时 Kubernetes 的 seccomp 实现是实验性的且与 Docker 绑定需要先在 CRI 中定义 API流式服务器认证Streaming server authenticationissue #36666CRI-containerd 与 Kubelet 是独立进程无法复用 Kubelet 的认证其流式服务器应实现自己的认证机制把容器设施移入 Pod cgroup镜像拉取器、流式处理器、日志处理器、containerd-shim 等服务于特定容器的设施应移入对应 Pod 的 cgroup其开销计入 Pod日志轮转Log rotationissue #42718CRI 中可能增加一个通知运行时重开日志文件的函数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_streaming与x509_key_pair_streaming配置获得 TLS 支持。六、路线图与里程碑提案的时间表6.1 里程碑MilestonesKubernetes 1.7Q2[P0] 基础容器生命周期[P0] 基础镜像管理[P0] 容器网络[P1] 容器流式/日志[P2] 容器/ImageFS 指标。测试计划每个新增功能都应附带单元测试并通过其对应的 CRI 一致性验证测试cri validation test。这条测试计划在今日依然有效CRI 插件的验证测试与 cri-tools 集成测试说明可参考 docs/cri/testing.md。Kubernetes 1.8Q3[P0] 功能完整通过 100% 的 CRI 一致性验证测试[P0] 将 CRI-containerd 与 Kubernetes 集成并搭建 e2e / node e2e 测试框架[P1] 解决可调试性问题。6.2 Q2 路线图RoadmapItem1/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 Scopeenable_tls_streamingx509_key_pair_streaming配置项日志轮转原 Out of Scopecontainer_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 生效ctr、nerdctl、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 3containerd 1.x 使用version 2并会自动转换插件 ID 在 v3 中有所变化——这正是提案中内置 CRI 支持从设想变为现实后带来的配置演进。结语这份 2017 年的提案完整回答了三个问题为什么用 containerd 替代 Docker稳定、兼容、少一跳性能开销、中立基金会、做什么生命周期、日志、流式、网络、指标、镜像六大能力的 API 翻译与元数据补齐、不做什么可调试性、内置插件化、seccomp、认证等九项范围外事项及其演进路径。从今天的仓库回看提案中的几乎每一项设计都找到了对应的实现CRI 插件已内置于 containerdplugins/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),仅供参考