Docker overlay2目录数据泄漏:通用可扩展排查方法咨询
问题背景
修改Jenkins流水线中的Docker构建脚本或升级Docker后,出现严重的镜像数据泄漏:/var/lib/docker/overlay2目录大小持续增长,远超Docker CLI(如docker system df)可识别并清理的镜像大小。繁忙的构建服务器上每日泄漏约1TB数据,常规docker system prune等命令无法清理,只能手动删除整个/var/lib/docker目录。
典型现象
执行docker system prune -af后立即查看,磁盘占用与CLI统计完全不符:
$ sudo du -sch /var/lib/docker/overlay2; echo ""; docker system df 68G /var/lib/docker/overlay2 68G total TYPE TOTAL ACTIVE SIZE RECLAIMABLE Images 0 0 0B 0B Containers 0 0 0B 0B Local Volumes 0 0 0B 0B Build Cache 0 0 0B 0B
几乎所有无法被识别的泄漏文件都位于diff子目录,但该目录同时存储有效镜像层,无法直接区分:
$ find /var/lib/docker/overlay2 | grep diff | wc -l 989599 # vs. $ find /var/lib/docker/overlay2 | grep -v diff | wc -l 792
泄漏目录的内容无异常,多为编译源码或预编译库文件:
./268713830f68824c1f6f5b21aad89055c18e8c0e6f1751654ec82ca4caf5bba9/diff/opt/conda/lib/python3.11/site-packages/sklearn/ensemble/_weight_boosting.py ./268713830f68824c1f6f5b21aad89055c18e8c0e6f1751654ec82ca4caf5bba9/diff/opt/conda/lib/python3.11/site-packages/sklearn/ensemble/_gradient_boosting.cpython-311-x86_64-linux-gnu.so ./268713830f68824c1f6f5b21aad89055c18e8c0e6f1751654ec82ca4caf5bba9/diff/opt/conda/lib/python3.11/site-packages/sklearn/ensemble/tests/test_gradient_boosting.py ./268713830f68824c1f6f5b21aad89055c18e8c0e6f1751654ec82ca4caf5bba9/diff/opt/conda/lib/python3.11/site-packages/sklearn/ensemble/tests/__pycache__/test_gradient_boosting.cpython-311.pyc ./268713830f68824c1f6f5b21aad89055c18e8c0e6f1751654ec82ca4caf5bba9/diff/opt/conda/lib/python3.11/site-packages/sklearn/ensemble/tests/__pycache__/test_weight_boosting.cpython-311.pyc ./268713830f68824c1f6f5b21aad89055c18e8c0e6f1751654ec82ca4caf5bba9/diff/opt/conda/lib/python3.11/site-packages/sklearn/ensemble/tests/test_weight_boosting.py ./268713830f68824c1f6f5b21aad89055c18e8c0e6f1751654ec82ca4caf5bba9/diff/opt/conda/lib/python3.11/site-packages/scipy/special/tests/data/boost.npz ./268713830f68824c1f6f5b21aad89055c18e8c0e6f1751654ec82ca4caf5bba9/diff/opt/conda/lib/python3.11/site-packages/scipy/special/tests/test_boost_ufuncs.py
Docker BuildX场景下的问题表现
升级为Docker BuildX(v0.15.1 1c1dbb2)后,初期overlay2大小稳定,但清理后无法回收存储,长期仍会耗尽空间:
$ sudo du -sch /var/lib/docker/overlay2; echo ; docker system df build-srv-3-fi: Sun Jul 28 14:43:42 2024 117G /var/lib/docker/overlay2 117G total TYPE TOTAL ACTIVE SIZE RECLAIMABLE Images 0 0 0B 0B Containers 0 0 0B 0B Local Volumes 0 0 0B 0B Build Cache 0 0 0B 0B
常规清理方式(先删镜像再清缓存、docker system prune)无改善。
注:场景不限于Docker Compose部署的Jenkins,并行数十个带层重叠、多阶段关联的流水线,每个业务容器构建需4个额外服务容器。
补充验证:排除分层文件系统计数干扰
使用du的--one-file-system参数确认是真实磁盘占用,而非分层文件系统重复计数:
$ sudo du -sch /var/lib/docker/overlay2; echo ""; sudo du -sch --one-file-system /var/lib/docker/overlay2; echo ""; docker system df 452G /var/lib/docker/overlay2 452G total 452G /var/lib/docker/overlay2 452G total TYPE TOTAL ACTIVE SIZE RECLAIMABLE Images 0 0 0B 0B Containers 0 0 0B 0B Local Volumes 0 0 0B 0B Build Cache 0 0 0B 0B
极端场景数据对比
当目录过于复杂导致du无法快速执行时:
$ df -haT Filesystem Type Size Used Avail Use% Mounted on [..] /dev/md2 ext4 3.4T 3.2T 5.3G 100% / $ docker system df TYPE TOTAL ACTIVE SIZE RECLAIMABLE Images 333 0 2.873TB 2.873TB (100%) Containers 0 0 0B 0B Local Volumes 0 0 0B 0B Build Cache 2532 0 55.14GB 55.14GB $ docker system prune -af Total reclaimed space: 812.8GB $ docker system df TYPE TOTAL ACTIVE SIZE RECLAIMABLE Images 0 0 0B 0B Containers 0 0 0B 0B Local Volumes 0 0 0B 0B Build Cache 0 0 0B 0B
实际结果:docker system prune宣称回收812.8GB,但实际仅回收约0.4T,overlay2仍有大量未识别数据;docker system df预估的可回收空间与实际相差约2.5T;仅BuildX的50GB构建缓存限制基本有效(仅超出10%)。
$ df -haT Filesystem Type Size Used Avail Use% Mounted on [..] /dev/md2 ext4 3.4T 2.8T 400G 88% / # 重启Docker守护进程和套接字无帮助: $ sudo systemctl restart docker docker.socket $ df -haT Filesystem Type Size Used Avail Use% Mounted on [..] /dev/md2 ext4 3.4T 2.8T 400G 88% /
通用可扩展调试步骤
步骤1:追踪Overlay2目录的元数据关联
Docker通过元数据文件跟踪overlay2的有效层,对比元数据记录与实际目录:
# 列出所有元数据中记录的层哈希 ls /var/lib/docker/image/overlay2/layerdb/sha256/ > /tmp/known_layers.txt # 列出overlay2目录下的所有层目录(排除临时目录) ls /var/lib/docker/overlay2 | grep -v l | grep -v merged | grep -v diff > /tmp/actual_layers.txt # 找出不在元数据中的可疑泄漏层 comm -23 <(sort /tmp/actual_layers.txt) <(sort /tmp/known_layers.txt) > /tmp/leaked_layers.txt
步骤2:分析泄漏层的创建时间与关联进程
通过时间匹配定位泄漏层的来源:
# 查看泄漏层的创建时间 stat /var/lib/docker/overlay2/[泄漏层哈希]/ # 匹配Docker守护进程日志中的操作时间 journalctl -u docker --since "YYYY-MM-DD HH:MM:SS" --until "YYYY-MM-DD HH:MM:SS" | grep [泄漏层哈希] # 匹配Jenkins流水线日志 grep -r [泄漏层哈希] /var/lib/jenkins/jobs/[流水线名]/builds/[构建号]/log
步骤3:验证层的引用关系
检查是否有残留的镜像、容器引用泄漏层:
# 遍历所有镜像(即使df显示0,也要检查残留元数据) for img in $(docker images -q); do docker inspect $img | grep -A5 -B5 "Layer"; done # 检查停止的容器残留 docker ps -a -q | xargs docker inspect | grep -A5 -B5 "Layer"
步骤4:BuildX特定调试
检查BuildX缓存与泄漏层的关联:
# 列出BuildX的构建器 docker buildx ls # 查看构建器缓存配置 docker buildx inspect [builder-name] --bootstrap # 模拟清理,查看将被删除的缓存 docker buildx prune -n
步骤5:排查文件句柄泄漏
检查是否有进程持有overlay2文件句柄,导致Docker无法删除:
# 查找持有overlay2文件的进程 lsof +D /var/lib/docker/overlay2
内容的提问来源于stack exchange,提问作者mirekphd

