服务器磁盘突然写满,容器一个接一个退出,docker ps看着还正常,一跑docker run就甩出一串:write /var/lib/docker/overlay2/9f3a.../diff/usr/lib/x86_64-linux-gnu/libc.so.6: no space left on device。拉镜像是另一种说法:failed to register layer: Error processing tar file(exit status 1): there might be not enough disk space。两种报错指的是同一件事,Docker的数据目录把分区吃光了。
处理不难,难的是别乱删。prune按下去容易,把还在用的卷一起删掉就麻烦了。
先确认是不是Docker吃的
df看到的100%未必和Docker有关,先用Docker自己的账目对一遍:
docker system df
输出四行:Images、Containers、Local Volumes、Build Cache,重点看RECLAIMABLE那一列,那是能安全回收的量。之前有台机器Images占42GB,其中31GB可回收,全是半年攒下来的旧版本镜像,一次prune就下来了。
反过来,如果RECLAIMABLE只有几百MB,说明空间是真在用,不是垃圾堆的,这时候再怎么prune也没用,得往下查是容器可写层还是日志。
定位到具体目录
df -h /var/lib/docker df -i /var/lib/docker du -sh /var/lib/docker/* | sort -rh | head -n 10
df -i那行别跳过。overlay2的层里全是大把小文件,字节还剩着、inode先耗光的情况见过不止一次,表现同样是no space left on device,这时候删几个大镜像一点用没有,只能清层数。
du的结果大致三种:overlay2最大,是镜像层和容器可写层;containers最大,基本就是日志文件;volumes最大,是业务数据,先别动。Docker Engine的25.0.18、26.1.5、27.5.1、28.5.2、29.9.0,这几个版本在这一点上没区别,日志驱动的默认行为一直没改。
容器日志是头号元凶
默认json-file驱动不设上限,一个话痨容器能把盘写穿。查一下单个日志有多大:
du -sh /var/lib/docker/containers/*/*-json.log 2>/dev/null | sort -rh | head -n 5
实测里见过单个日志文件27GB,容器本身才跑了三个月。要定位是哪个容器,用inspect取路径:
docker inspect --format='{{.LogPath}}' <容器名>
线上不能重建容器时,直接把文件截断,比删文件安全(删了Docker还握着句柄,空间不会释放):
truncate -s 0 $(docker inspect --format='{{.LogPath}}' <容器名>)
截断只是救急。要断根,在/etc/docker/daemon.json里写死上限:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "20m",
"max-file": "3"
}
}
改完systemctl restart docker。注意一点:这个配置只对重启后新建的容器生效,老容器还是老样子,日志已经涨起来的那批要单独truncate或者重建。
overlay2里的层怎么判断能不能删
overlay2目录下每个层长这样:diff/是这一层的实际内容,link是短标识,lower记录下层引用,merged/是运行时的合并视图,work/是工作目录。处于运行中的容器,merged和work都在,这部分绝对动不得。
docker system df -v docker ps -a --size docker container diff <容器名>
docker ps --size里SIZE列带virtual前缀的是镜像共享部分,前面那个数字才是可写层。遇到可写层几个GB的,通常就是应用把日志或临时文件写进了容器内部而不是挂卷,这种情况光删不解决问题,得改成volume或bind mount。
清理从保守到激进
按这个顺序来,每一步都能停:
docker container prune -f docker image prune -f docker builder prune -f docker buildx prune -af docker volume ls
image prune不带-a只删悬空镜像(<none>),带-a会把当前没被任何容器引用的镜像全删了,rollback时想拉回旧版本就得重新下载。volume prune更狠,卷里是真数据,删之前务必docker volume ls过一遍,确认没有数据库目录在里面。构建缓存那一项在CI机器上往往是最大头,buildx prune -af一次能回收十几GB。
真要一步到位:
docker system prune -a --volumes
这条命令在生产上慎用,跑之前心里得有数。
清完还是满的两种情况
一种是Docker握着已删除文件的句柄,du和df对不上。重启daemon让句柄释放:
systemctl restart docker df -h /var/lib/docker
另一种是磁盘真的不够用,那就迁data-root。停服务、rsync过去、改配置、起服务,顺序别乱:
systemctl stop docker
rsync -aHAX /var/lib/docker/ /data/docker/
echo '{"data-root": "/data/docker"}' > /etc/docker/daemon.json
systemctl start docker
docker info | grep -i 'docker root dir'
用rsync不用mv,是因为mv中断了难恢复,rsync可以重跑。确认新目录生效后再决定要不要清掉/var/lib/docker。Ubuntu 22.04、24.04、26.04,默认的存储驱动都是overlay2,迁目录这一步操作是一样的。
日常怎么防
新机器装完Docker第一件事就是把daemon.json的log-opts配上,这个动作五秒钟,能省掉后面半夜的一次救火。再就是给/var/lib/docker单独挂个盘,别和系统盘挤在一起,CI机器上再加一条定期清理:
docker system prune -f --filter "until=168h" docker buildx prune -f --filter "until=168h"
监控别只盯字节,inode使用率一起加进告警项,超过85%就提示。2019年到2026年这段时间里,Docker在磁盘这块的变化主要在存储驱动和构建缓存,json-file默认不限制这一点一直没动过,指望版本升级自动解决是不现实的。
数据截至2026年10月。