大型 Docker 开发镜像磁盘排查:containerd、缓存与容量规划
大型 Docker 开发镜像磁盘排查containerd、缓存与容量规划摘要给出大型开发镜像的磁盘排查顺序区分 containerd 镜像数据、snapshot、BuildKit cache、导出归档和源码构建输出。文章目录大型 Docker 开发镜像磁盘排查containerd、缓存与容量规划1. 第一层先看文件系统2. 第二层看宿主机真实目录3. 第三层看 Docker 逻辑对象4. 构建过大镜像以后单独看 BuildKit cache5. 为什么 builder prune 不会把 50 GiB 镜像变成 27 GiB6. inactive 不等于“不需要”7. image prune 和 system prune 的范围不同8. 不要直接删除 /var/lib/containerd9. 导出归档经常是被忽略的一大块10. 源码 build 目录也可能很大11.>大型开发镜像 containerd content unpacked snapshot BuildKit cache 源码 build 输出 镜像导出归档这时如果只看到/dev/sdaX 90%然后立刻运行docker system prune -a很容易删掉重新构建代价很高的开发镜像。更好的方式是先回答空间到底被哪一类数据占用了1. 第一层先看文件系统df-h/它回答的是整个根文件系统还剩多少空间它不会告诉你 Docker 哪个对象在占空间。如果镜像归档放在其他分区还要分别看df-h/path/to/archive大型镜像导出时Docker 数据和归档文件可能位于不同文件系统不能只检查一个路径。2. 第二层看宿主机真实目录containerd image store 环境下至少检查sudodu-sh/var/lib/containerd /var/lib/docker这回答的是宿主机目录实际占用。Docker 当前文档说明containerd image store 下的 image contents 和 container snapshots 默认在/var/lib/containerd所以新 Docker 环境中只看/var/lib/docker已经不够。3. 第三层看 Docker 逻辑对象dockersystemdf需要更详细时dockersystemdf-v它让你从 Docker 对象视角看Images Containers Local Volumes Build Cachedu和docker system df不要求数字完全一致。一个是文件系统目录视角一个是 Docker 对象和共享关系视角。4. 构建过大镜像以后单独看 BuildKit cache如果刚执行过大型docker buildx build再看dockerbuildxdu大型 SDK 构建经常产生很大的 BuildKit cache。它和“最终镜像本身”不是同一个对象Build Cache 构建阶段复用 Final Image docker run 使用如果 cache 明确可回收可以dockerbuilder prune这通常是大型镜像构建完成后的第一类清理对象。5. 为什么 builder prune 不会把 50 GiB 镜像变成 27 GiB如果当前 containerd image store 需要同时保存image content unpacked snapshot那么它们属于最终镜像运行体系。执行dockerbuilder prune只会处理 BuildKit cache。所以即使清掉 20 GiB 构建缓存最终镜像相关的几十 GiB 仍然可能存在。这不是 prune 失败而是你清理的是不同对象。6. inactive 不等于“不需要”大型开发镜像有一个危险特点平时不一定有常驻容器。所以 Docker 可能显示ACTIVE 0但这只说明现在没有容器使用它不代表镜像应该删除。一个 20 GiB 开发镜像可能构建几个小时甚至需要重新复制大型 SDK。因此看到 inactive 后不要直接dockersystem prune-a更合理的是先确定这个镜像是否是正式开发环境 是否已经有可靠离线备份 是否能快速重新构建7. image prune 和 system prune 的范围不同较温和dockerimage prune更激进dockerimage prune-a范围更广dockersystem prune-a越往后自动判断“未使用”的对象范围越大。对于普通 CI 临时环境这可能很方便对于手工制作的大型 SDK 镜像应该更保守。建议先dockersystemdf-v确认对象再做针对性清理。8. 不要直接删除/var/lib/containerd即使sudodu-sh/var/lib/containerd显示几十 GiB也不要执行sudorm-rf/var/lib/containerd/*containerd 的 content、metadata 和 snapshot 之间存在引用关系。绕过 Docker/containerd 管理层手工删除可能导致镜像 metadata 仍然存在 底层 blob 已损坏 snapshot 引用不一致 容器无法启动底层目录不是普通“缓存目录”。清理应优先通过 Docker CLI 或明确的官方迁移流程完成。9. 导出归档经常是被忽略的一大块例如你为了迁移镜像在~/images/放了cross-dev.tar cross-dev.tar.zst如果原始 TAR 没删可能同时多占20 GiB TAR 10 GiB 压缩归档它们完全不属于 Docker因此dockersystemdf不会显示。这就是为什么排查不能只看 Docker CLI。可以检查du-sh~/imagesfind~/images-maxdepth1-typef-size1G-ls大型镜像场景里“导出文件忘了删”非常常见。10. 源码 build 目录也可能很大如果开发容器使用 bind mount宿主机源码 - /workspace那么容器生成的build/ *.o *.a *.so 打包产物都可能实际写在宿主机项目目录。这些也不属于 Docker image store。所以完整排查还应该看du-sh~/work/demo-appdu-sh~/work/demo-app/build不要看到“是容器编译产生的”就认为一定在/var/lib/docker。11.>{data-root:/mnt/docker-data}但 Docker 当前官方文档明确说明在 containerd image store 下data-root不会自动改变 containerd image contents 和 snapshots 的位置。于是可能变成/mnt/docker-data Docker 其他数据 /var/lib/containerd 镜像内容和 snapshot如果你的目标是彻底把大型镜像数据迁出根分区就必须把 containerd 数据位置也纳入设计而不是只修改 Dockerdata-root。这类存储迁移涉及 daemon 停止、数据复制和配置一致性不建议在磁盘快满时临时手工移动目录应按 Docker/containerd 官方数据目录配置方式操作。12. 一个 80 GiB 根分区为什么很容易不够假设系统自身 15 GiB 开发镜像相关数据 45~55 GiB BuildKit cache 10 GiB 源码和 build 5 GiB总量已经可能达到75~85 GiB如果这时还在根分区导出cross-dev.tar.zst 10 GiB很容易直接失败。因此对超大型开发镜像磁盘规划应该比“镜像 SIZE 5 GiB”保守得多。13. 更实用的容量规划方式可以按几类数据分别估算A. 操作系统和普通工具 B. containerd image content C. unpacked snapshots D. BuildKit cache 峰值 E. 源码和构建产物 F. 镜像导出归档其中 F 最适合放到另一块盘或网络存储因为它只在迁移时需要。如果这台机器长期维护大 SDK 镜像更合理的硬件布局是系统根分区 放 OS 和普通工具 大容量数据分区 放 Docker/containerd 数据 或至少放离线镜像归档14. 推荐的排查顺序以后遇到 Docker 把磁盘吃满可以按下面顺序df -h文件系统还剩多少du/var/lib/containerd 与 /var/lib/dockerdocker system df -v镜像/容器/volumedocker buildx duBuildKit cache检查导出归档和源码 build针对确认对象清理或迁移对应命令df-h/sudodu-sh/var/lib/containerd /var/lib/dockerdockersystemdf-vdockerbuildxdudu-sh~/images ~/work/demo-app/build2/dev/null这五步基本能把“大空间去哪了”分成明确类别。15. 最后再决定处理策略如果主要是 BuildKit cachedockerbuilder prune如果是明确不需要的旧镜像dockerimagermimage如果是离线归档转移到移动硬盘 / NAS 或确认备份后删除本地副本如果是 containerd image store 的最终镜像本身它不是“垃圾缓存”此时真正的解决方式可能是扩大数据分区、迁移 containerd 数据位置或者重新评估是否必须长期保留如此巨大的完整 SDK 镜像。关键原则只有一句先确认数据身份再清理不要用最激进的 prune 去替代诊断。参考资料Docker Docscontainerd image store with Docker Engine — https://docs.docker.com/engine/storage/containerd/Docker DocsDocker daemon data directory — https://docs.docker.com/engine/daemon/Docker DocsStorage drivers — https://docs.docker.com/engine/storage/drivers/