用心打造
VPS知识分享网站

Docker镜像构建提示no space left on device怎么办?缓存排查

Docker构建镜像进行到一半,突然报 no space left on device,第一反应往往是执行一次 docker system prune -a。这个命令确实可能腾出空间,但清理范围比构建缓存大得多,未使用镜像、停止容器和网络都可能被删除;再带上 --volumes,误删数据卷的风险更高。

我更习惯先把磁盘、inode、Docker根目录和当前builder分开确认。报错里的device不一定是系统根分区,也可能是 /var/lib/docker 所在挂载、BuildKit专用目录、容器临时层,甚至是inode已经耗尽。先找到真正满的那一层,才能选择最小范围的清理方式。

构建空间不足

先确认容量还是inode用完

先保存错误时间和完整构建输出,然后检查文件系统:

date
df -hT
df -i
docker info --format '{{.DockerRootDir}}'
findmnt /var/lib/docker

df -h 接近100%说明容量不足;df -i 的IUse%接近100%,说明大量小文件耗尽inode,即使还有GB级空间也无法创建新文件。Docker根目录不一定是 /var/lib/docker,修改过 data-root、使用rootless模式或独立挂载时尤其如此。

构建错误指向 /tmp、工作目录或应用包管理器缓存时,还要检查对应挂载。不要只看根分区总空间,也不要看到overlay路径就直接进入底层目录手工删除文件。

还要留意磁盘保留块与用户配额。普通用户可用空间可能早于root耗尽,CI任务在受限账号下运行时尤其明显。确认构建进程的实际用户、项目配额和容器存储驱动,能避免把权限或配额问题误判成全盘容量不足。

查看Docker对象与构建缓存占用

先用Docker自身的统计命令定位:

docker system df -v
docker buildx ls
docker buildx du

docker system df -v 展示镜像、容器、本地卷和构建缓存的可回收空间。docker buildx du 针对当前选中的builder展示BuildKit缓存记录;多builder环境要确认当前构建究竟使用哪一个实例。

统计里的reclaimable表示当前没有被引用的部分,不等于可以不评估就全部删除。共享构建机上,其他项目可能依赖这些缓存加速发布,清理会让下一轮构建重新下载和编译,带来时间与网络成本。

docker buildx du 执行很慢或报连接builder失败时,先检查builder容器和Docker守护进程状态。不要在统计未完成时直接判断缓存损坏,远程builder还可能把缓存放在另一台机器上。

区分镜像层、容器日志与数据卷

构建缓存只是常见来源之一。镜像层大量重复、停止容器的可写层、JSON日志和匿名卷也会占用Docker所在文件系统。分别查看:

docker ps -a --size
docker image ls --digests
docker volume ls
sudo du -xhd1 "$(docker info --format '{{.DockerRootDir}}')" 2>/dev/null

du 在Docker繁忙时会产生额外I/O,线上机器应降低深度并避开高峰。容器日志异常增长时,应该先确认具体文件和对应容器,再处理日志轮转;不能把所有磁盘问题都归到BuildKit。

数据卷通常存放数据库、上传文件或应用状态。它即使显示未被容器引用,也可能仍是待恢复的数据。没有业务确认和备份,不应使用包含卷的全局清理命令。

优先使用定向的缓存清理

确认主要占用来自构建缓存后,可先预览当前builder,再按时间或空间目标清理。传统builder可用:

docker builder prune --filter 'until=168h'

Buildx使用:

docker buildx prune --filter 'until=168h'
docker buildx prune --min-free-space 10gb

命令会提示预计删除内容,应先在非交互自动化之外人工确认。--all 会把更多内部及未使用缓存纳入范围,不能作为默认选项。--min-free-space--max-used-space--reserved-space 的支持情况与Docker版本有关,执行前核对 docker buildx prune --help

清理后缓存命中率会下降,但这比删除未知数据卷安全。紧急腾出空间时,也要记录清理了哪个builder、使用了什么过滤条件,便于后续解释构建时间变化。

为什么不建议直接system prune加volumes

docker system prune 会清理停止容器、未使用网络、悬空镜像和未使用构建缓存;加入 -a 后还会删除所有未被容器使用的镜像。再加入 --volumes,未使用卷也可能进入删除范围。

服务器上保留的回滚镜像通常没有运行容器引用,蓝绿发布暂时下线的旧版本也可能被认定为未使用。一次全局清理会让回滚必须重新拉取,私有仓库不可用时风险更大。

更稳妥的做法是先按对象类型和时间过滤,保留最近构建与回滚所需镜像。清理命令不是修复磁盘增长的终点,必须继续查清为什么缓存或日志没有上限。

优化Dockerfile减少无效层

缓存反复膨胀,经常与Dockerfile写法有关。把变化频繁的源码复制放在依赖安装之前,会让依赖层每次失效;在不同RUN层下载再删除临时文件,也可能让已写入旧层的数据继续存在。

COPY package-lock.json package.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY . .

使用 .dockerignore 排除 .git、本地依赖、测试产物、日志和备份文件,能缩小构建上下文。多阶段构建可让最终镜像只保留运行所需文件,但中间阶段仍可能形成BuildKit缓存,因此仍要配合垃圾回收策略。

不要为了镜像小就把安装、构建和运行全部塞进难以维护的一行。目标是让稳定依赖层可复用、临时产物不进入最终阶段,并保持每层用途可解释。

检查BuildKit垃圾回收策略

BuildKit支持按使用时间、缓存类型和空间阈值执行垃圾回收。长期构建节点不配置上限,缓存可能随着分支、平台架构和构建参数持续增长。

先查看Docker与builder版本、驱动和状态:

docker version
docker buildx inspect --bootstrap
docker buildx du

不同驱动的缓存存放与管理方式不同。dockerdocker-container、远程builder和云端builder不能用同一目录假设。配置GC前先确认驱动文档,并从保留最近常用缓存的小范围策略开始。

需要复现不同builder驱动时,可以在 萤光云 建立隔离构建机,或在按小时计费的 LightNode 上验证清理与重建耗时。只同步脱敏Dockerfile和公开依赖,不要上传生产密钥或私有镜像凭据。

排查清理后空间没有释放

对象删除后 df 仍不下降,可能有进程继续打开已经删除的日志或临时文件。检查:

sudo lsof +L1
sudo du -xhd1 /var/lib/docker 2>/dev/null
df -hT

lsof +L1 显示链接数为0但仍被进程占用的文件。重启对应服务可以释放句柄,但要先确认业务影响,不能为了回收空间直接重启Docker导致所有容器中断。

overlay2底层目录只能由Docker管理。手工删除层文件会破坏元数据,造成镜像或容器无法启动。发现统计与目录占用严重不一致时,应先备份关键卷和配置,再按Docker支持的方式修复或迁移数据目录。

修改后怎样验收

清理和优化后,重新执行同一构建并记录空间变化:

df -hT
df -i
docker buildx du
docker build --progress=plain -t APP:test .
docker run --rm APP:test COMMAND

至少验证一次冷构建和一次有缓存构建,确认镜像能启动、产物完整、回滚镜像仍在,并观察磁盘是否在连续多次构建后稳定在设定范围内。

合格的修复不是只腾出几GB,而是明确哪类数据在增长、清理范围可控,并让构建缓存拥有可持续的空间上限。

常见问题

docker builder prune会删除正在运行的容器吗?

它针对构建缓存,不会直接删除运行容器,但被清理的缓存会影响后续构建速度。仍应确认当前builder和过滤条件。

磁盘还有空间为什么仍提示no space left on device?

常见原因是inode耗尽、目标目录位于另一个已满挂载,或容器临时层达到自身限制,需要结合 df -i 和挂载关系确认。

可以直接进入overlay2目录删除大文件吗?

不可以。绕过Docker删除底层文件可能破坏层与元数据,应使用Docker对象清理命令或迁移数据目录。

赞(0)
未经允许不得转载;国外VPS测评网 » Docker镜像构建提示no space left on device怎么办?缓存排查
分享到