2. containerd 架构概览:核心组件与磁盘使用的关系

理解 containerd 的垃圾回收机制,首先需要掌握其核心架构。containerd 采用模块化设计,其中 shim、snapshotter 和 metadata store 三个组件与磁盘空间的使用和释放直接相关。本节将剖析这三个组件的职责及其对磁盘容量的影响。

2.1 核心组件与磁盘使用全景

containerd 的磁盘占用主要来自镜像层数据、容器可写层数据以及元数据索引。下图展示了三个核心组件在容器生命周期中的位置及其与磁盘的交互关系。

containerd 核心组件与磁盘使用关系 容器进程 (Container) containerd-shim Snapshotter (镜像层 + 容器层) Metadata Store (事件、索引、引用计数) 磁盘 (Disk) /var/lib/containerd 关键说明 • shim:管理容器进程生命周期,持有容器层文件描述符,阻止 GC 回收。 • snapshotter:管理镜像层和容器层的存储,是磁盘占用的主要来源。 • metadata store:记录镜像、容器、快照的引用关系,GC 依赖其判定可回收对象。 • 三者协作:shim 存活 → metadata 标记引用 → snapshotter 保留数据;shim 退出 → GC 清理。

2.2 containerd-shim:容器进程的守护者

shim 是容器进程与 containerd 守护进程之间的中间层。每个容器对应一个独立的 shim 进程,其主要职责包括:

  • 进程生命周期管理:接管容器进程的 stdin/stdout/stderr,即使 containerd 重启,容器也不会中断。
  • 文件描述符持有:shim 持有容器可写层的文件描述符,这直接阻止了 snapshotter 对该层数据的回收。
  • 退出信号传递:当容器退出时,shim 负责通知 containerd 更新元数据状态。

磁盘影响:只要 shim 进程存活,其对应的容器可写层数据就被视为“正在使用”,垃圾回收器不会触碰这些数据。容器停止后,shim 退出,元数据标记为“可回收”,GC 才能释放磁盘空间。

2.3 Snapshotter:镜像与容器层的存储引擎

snapshotter 是 containerd 中管理磁盘存储的核心组件,负责镜像层和容器可写层的创建、挂载与删除。常见的 snapshotter 实现包括 overlayfs、native、devmapper 等。

快照类型 存储位置 磁盘占用特点 GC 回收条件
镜像层 (Image Layer) /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/ 只读,多个容器共享 无任何容器或镜像引用
容器可写层 (Container Layer) 同上,以容器 ID 命名 读写,每个容器独享 容器已删除且 shim 已退出
临时快照 (Ephemeral) 同上,临时命名 构建镜像时产生,构建完成后可删除 构建任务结束且无引用

snapshotter 使用 引用计数 机制管理磁盘空间。每个快照被引用时计数 +1,释放时计数 -1。只有当引用计数归零时,该快照才具备被 GC 回收的资格。

2.4 Metadata Store:GC 的决策大脑

metadata store 是 containerd 的“数据库”,通常基于 BoltDB(嵌入式键值存储)实现,存储在 /var/lib/containerd/io.containerd.metadata.v1.bolt/meta.db 文件中。它记录了以下关键信息:

  • 镜像索引:所有已拉取镜像的 manifest、配置和层列表。
  • 容器记录:每个容器的状态、关联的快照 ID、运行时配置。
  • 快照引用关系:每个快照被哪些镜像或容器引用,以及引用计数。
  • 事件时间戳:容器创建、启动、停止的时间,用于 GC 判断“过期”条件。

GC 决策流程:垃圾回收器首先查询 metadata store 获取所有快照的引用计数和容器状态。引用计数为 0 且关联容器已删除的快照,被标记为“待回收”。然后 GC 通知 snapshotter 执行实际的磁盘删除操作。

2.5 三组件协作与磁盘释放流程

下面通过一个容器从创建到删除的完整生命周期,展示三个组件如何协作影响磁盘使用:

  1. 容器创建:containerd 调用 snapshotter 为容器创建可写层(基于镜像层),metadata store 记录新快照的引用。
  2. 容器运行:shim 启动容器进程,持有可写层文件描述符,metadata 标记该容器为“运行中”。
  3. 容器停止:shim 退出,释放文件描述符,metadata 更新容器状态为“已停止”。
  4. 容器删除:用户执行 ctr container delete,metadata 移除容器记录,快照引用计数减 1。
  5. GC 触发:GC 扫描 metadata,发现该快照引用计数为 0,通知 snapshotter 删除磁盘数据。
  6. 磁盘释放:snapshotter 执行 unlink 或 rmdir 操作,释放磁盘空间。

常见陷阱:如果容器停止后未执行删除操作,shim 虽然退出,但 metadata 中仍保留容器记录和快照引用。此时 GC 不会回收该快照,磁盘空间无法释放。务必在容器停止后执行 ctr container delete 或使用 --rm 参数自动清理。

2.6 磁盘使用分布总结

下表汇总了三个核心组件在典型 containerd 节点上的磁盘占用分布:

组件 磁盘路径 典型占用比例 GC 可回收
Snapshotter (镜像层) /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/ 60% - 80% 是(无引用时)
Snapshotter (容器层) 同上,容器子目录 10% - 30% 是(容器删除后)
Metadata Store /var/lib/containerd/io.containerd.metadata.v1.bolt/meta.db < 1% 否(仅索引,不存储数据)
shim 运行时数据 /run/containerd/ (内存文件系统) < 1% 否(shim 退出后自动释放)