Docker磁盘告警?一键清理脚本设计与防复发实践

发布时间:2026/10/27 11:31:26
Docker磁盘告警?一键清理脚本设计与防复发实践 磁盘告警这件事做过运维和开发的人大概都经历过某天早上打开终端敲个df -h发现根分区已经飘红使用率 98%而业务代码一行没改、日志也没暴增。顺着du -sh一层层往下翻最后大概率会停在/var/lib/docker这个目录上——镜像、容器层、构建缓存、卷、日志全堆在里面。这篇就围绕“一键清理 Docker 磁盘文件”这个主题把我这些年处理这类问题的完整思路、脚本设计、踩过的坑和验证方法讲透从“为什么 Docker 会吃盘”一直讲到“怎么安全地一键清理并防止复发”。适合谁看日常要和 Docker 打交道的后端开发、测试环境维护者、个人服务器玩家以及刚开始接触容器化部署、被磁盘告警搞得手忙脚乱的同学。全文不依赖任何特定云平台命令和脚本都是通用的照着改改就能用。1. Docker 磁盘被吃掉的真实构成很多人一看到磁盘满第一反应是“删镜像”。但实测下来镜像往往不是最大头。要设计一个靠谱的一键清理方案第一步必须搞清楚 Docker 占用的磁盘到底分成了哪几块每块的增长逻辑是什么删掉之后会不会影响正在跑的业务。这一节把账算清楚。1.1 五类占用镜像、容器层、构建缓存、卷、日志Docker 在磁盘上的数据基本可以归为五类它们的增长方式和清理风险完全不同占用类型典型路径增长原因清理风险镜像Image/var/lib/docker/overlay2频繁拉取、构建新版本低但删了要重新拉容器可写层/var/lib/docker/overlay2容器内写文件、临时数据中删容器即丢数据构建缓存/var/lib/docker/buildkit反复docker build低重建会慢一点数据卷Volume/var/lib/docker/volumes数据库、上传文件等持久化数据高删了数据就没了容器日志/var/lib/docker/containers/*/*-json.log应用疯狂打日志低但要注意别删正在写的这里有个反直觉的点构建缓存经常是隐形大户。现在默认用 BuildKit 构建每次docker build都会留下大量中间层日积月累几十 GB 很常见而docker images里根本看不到它们。我见过一台构建机镜像列表加起来不到 5GB但/var/lib/docker占了 60GB一查全是 buildkit 缓存。另一个容易被忽略的是容器日志。Docker 默认的 json-file 日志驱动不做轮转一个疯狂打日志的应用几天就能写出几十 GB 的-json.log。这类文件在docker system df里甚至不完全体现得单独去containers目录下看。1.2 为什么docker system df的数字和du对不上这是新手最容易困惑的地方。docker system df显示总占用 20GB但du -sh /var/lib/docker却是 45GB差了 25GB 去哪了原因主要有两个。第一docker system df统计的是 Docker 自己管理的对象而du统计的是文件系统实际占用两者口径不同。第二已删除但被进程占用的文件——某个容器删了但它打开的文件句柄还被残留进程持有文件在du里还算着实际空间没释放。这种情况用lsof L1能查出来。提示判断磁盘到底被谁占着别只信docker system df。先du -sh /var/lib/docker/*看大头在哪再针对性处理比盲目prune高效得多。1.3 一键清理到底该“清”什么、不该“清”什么设计清理脚本前先划清边界。我的原则是默认只清可再生的东西持久化数据一律不碰。可安全清理的停止的容器、悬空镜像dangling image即none标签的、未被任何容器引用的构建缓存、已退出的容器日志。需要谨慎的未被使用的镜像可能下次部署要用、未被引用的卷可能是备份数据。绝对不能自动清的正在运行的容器、正在被挂载的卷、业务数据库的数据目录。这个边界决定了脚本的默认行为。一键清理不等于“一键删光”而是“一键清掉那些删了也不心疼的东西”。后面第 3 节的脚本就是按这个原则设计的。2. 清理命令的取舍哪些能进脚本哪些必须手动Docker 自带的清理命令就那么几个但每个的行为差异很大能不能放进“一键”脚本里得逐个掂量。这一节把常用命令的适用场景和坑讲清楚避免你写出来的脚本把生产环境搞挂。2.1docker system prune的默认行为与-a的杀伤力docker system prune是最常被提起的命令但它默认只清理停止的容器、悬空镜像、未被使用的网络、悬空构建缓存。注意默认不删未被容器使用的镜像。加上-a之后行为就变了所有未被任何容器使用的镜像都会被删。这意味着你本地存着准备回滚的旧版本镜像一执行就没了。我在测试环境用-a没问题但生产构建机上从来不敢直接加。# 保守版只清停止的容器、悬空镜像、悬空缓存 docker system prune -f # 激进版连未被使用的镜像一起清慎用 docker system prune -a -f还有一个--volumes参数加上会连未被引用的卷一起删。这个参数我建议永远不要放进自动脚本卷里可能是数据库数据、上传文件删了就是事故。2.2docker builder prune单独处理构建缓存既然构建缓存是大头就该单独给它一条命令而不是混在system prune里。docker builder prune专门清 BuildKit 缓存默认清悬空的加-a清全部。# 清理悬空构建缓存 docker builder prune -f # 清理全部构建缓存下次构建会全量重建慢 docker builder prune -a -f我的做法是脚本里默认用不带-a的版本把“全清”作为一个可选参数暴露出来。因为全清之后下一次 CI 构建会明显变慢这个代价要让使用者自己决定。2.3 容器日志的清理truncate而不是rm容器日志不能直接rm。原因很简单日志文件正被容器进程以追加模式打开着你rm掉进程还持有句柄空间不释放而且新日志会写到一个“已删除”的 inode 上越写越乱。正确做法是用truncate把文件截断为 0# 找到所有容器日志并截断 find /var/lib/docker/containers -name *-json.log -exec truncate -s 0 {} \;truncate -s 0把文件大小设为 0但保留 inode进程继续写也没问题。这是清理日志的标准姿势比rm安全得多。注意截断日志前最好确认对应容器不是正在排查问题的对象日志清了就找不回来了。生产环境建议先配置日志轮转而不是靠事后截断。2.4 命令组合的推荐顺序清理顺序也有讲究。我的经验是先清构建缓存和日志这俩最安全、收益最大再清停止的容器最后才考虑镜像。因为镜像清理可能触发重新拉取放在最后能减少不必要的网络开销。推荐顺序截断容器日志释放最快风险最低docker builder prune -f清构建缓存docker container prune -f清停止的容器docker image prune -f清悬空镜像视情况docker image prune -a -f清未使用镜像需人工确认这个顺序写进脚本配合每步前后的磁盘占用对比就能清楚看到每一步释放了多少空间。3. 写一个真正能用的清理脚本网上很多“一键清理”脚本就是一行docker system prune -a -f --volumes看着爽用起来险。我要的是一个默认安全、可观测、可回滚的脚本。这一节把脚本拆开讲每一段为什么这么写都说清楚。3.1 脚本的整体结构与安全开关先看整体框架。核心设计有三个默认不删卷、默认不删未使用镜像、执行前打印将要清理的内容并支持--dry-run。#!/usr/bin/env bash set -euo pipefail # 默认配置 DRY_RUNfalse CLEAN_IMAGESfalse CLEAN_VOLUMESfalse LOG_TRUNCATEtrue # 参数解析 while [[ $# -gt 0 ]]; do case $1 in --dry-run) DRY_RUNtrue; shift ;; --images) CLEAN_IMAGEStrue; shift ;; --volumes) CLEAN_VOLUMEStrue; shift ;; --no-log) LOG_TRUNCATEfalse; shift ;; *) echo 未知参数: $1; exit 1 ;; esac doneset -euo pipefail是脚本健壮性的基础遇错退出、未定义变量报错、管道错误不吞掉。这三个选项能挡掉一大半“脚本跑一半出错还继续执行”的坑。--dry-run是我强烈建议每个清理脚本都有的开关。清理是不可逆操作先让用户看一眼“将要删什么”比事后追悔强太多。3.2 每一步的磁盘占用对比与输出光清理不够还得让用户看到效果。脚本里封装一个函数在每步前后记录/var/lib/docker的大小docker_size() { du -sh /var/lib/docker 2/dev/null | awk {print $1} } step() { local desc$1; shift local before after before$(docker_size) if [[ $DRY_RUN true ]]; then echo [DRY-RUN] 将执行: $desc else echo 执行: $desc $ fi after$(docker_size) echo 清理前: $before 清理后: $after }这样每执行一步终端上就能看到“清理前 45G清理后 32G”直观且有说服力。du在大目录上会慢一点但相比清理本身耗时可忽略。3.3 日志截断、缓存清理、容器清理的具体实现把前面讲的命令按顺序串起来# 1. 截断容器日志 if [[ $LOG_TRUNCATE true ]]; then step 截断容器日志 bash -c \ find /var/lib/docker/containers -name *-json.log -exec truncate -s 0 {} \; fi # 2. 清理构建缓存 step 清理悬空构建缓存 docker builder prune -f # 3. 清理停止的容器 step 清理停止的容器 docker container prune -f # 4. 清理悬空镜像 step 清理悬空镜像 docker image prune -f # 5. 可选清理未使用镜像 if [[ $CLEAN_IMAGES true ]]; then step 清理未使用镜像 docker image prune -a -f fi # 6. 可选清理未引用卷默认关闭 if [[ $CLEAN_VOLUMES true ]]; then step 清理未引用卷 docker volume prune -f fi注意第 1 步用bash -c包了一层因为find ... -exec里有分号直接作为参数传给step函数会被 shell 提前解析。这种细节不处理脚本跑起来就会报语法错误。3.4 权限、路径与跨平台兼容的注意事项脚本默认假设/var/lib/docker是 Docker 数据目录但这不是绝对的。有人改过>DOCKER_ROOT$(docker info -f {{.DockerRootDir}} 2/dev/null || echo /var/lib/docker)用docker info拿真实路径拿不到再回退到默认值。这样脚本在改过配置的机器上也能正常工作。权限方面清理命令基本都需要 root 或 docker 组权限。脚本开头可以加个检查if ! docker info /dev/null 21; then echo 无法访问 Docker请用 sudo 或加入 docker 组 exit 1 fi至于跨平台macOS 上 Docker Desktop 的数据在虚拟机里/var/lib/docker路径不存在日志截断那步会失败。所以脚本里对find那步加了容错路径不存在就跳过不中断整体流程。4. 清理之后那些脚本救不了你的场景脚本能解决 80% 的磁盘问题但剩下 20% 往往更棘手。这一节讲几个我真实踩过的坑都是“跑完清理脚本磁盘还是满”的情况排查思路比命令本身更值钱。4.1 已删除文件仍被进程占用lsof定位法最典型的现象清理脚本跑完docker system df显示占用已经很小但df -h还是 95%。这时候八成是已删除文件被进程持有。排查方法# 找出被删除但仍被占用的文件 lsof L1 2/dev/null | grep -i docker | head -20输出里会显示进程 PID、文件大小和(deleted)标记。找到之后重启对应进程通常是某个容器或 dockerd 本身就能释放空间。我遇到过一次某个容器被docker rm了但它 fork 的子进程还在跑持有几十 GB 的日志文件句柄du看不到df却实实在在占着。提示lsof L1里的L1表示“链接数小于 1”也就是已删除但还开着的文件。这个技巧不只对 Docker 有用任何磁盘“账对不上”的场景都适用。4.2 overlay2 目录膨胀但镜像不多容器可写层在作怪有时候docker images没几个docker system df的镜像占用也不大但/var/lib/docker/overlay2就是很大。这通常是容器的可写层在膨胀——容器里跑了写文件的应用比如临时缓存、未清理的临时文件。定位方法# 查看各容器可写层大小 docker ps -s --format table {{.Names}}\t{{.Size}}Size列就是可写层大小。如果某个容器显示2GB (virtual 5GB)说明它写了 2GB 数据到可写层。这类数据在容器删除前不会释放清理脚本也清不掉只能进容器里删文件或者重建容器。4.3 卷里的数据最不该被“一键”的部分卷是持久化数据的载体数据库、上传文件、配置都在里面。docker volume prune删的是“未被任何容器引用”的卷听起来安全但有个陷阱容器停了但没删卷仍被引用prune 不会删容器删了卷就变成未引用prune 会删。也就是说你为了“清理”删掉一个停止的容器它的卷就暴露在 prune 的删除范围内了。如果那个卷里是数据库数据而你又恰好跑了带--volumes的清理数据就没了。我的做法是卷永远手动处理。先docker volume ls看列表再docker volume inspect name确认用途确认无用才docker volume rm。这一步绝不放进自动脚本。4.4 清理后磁盘没降文件系统层面的延迟释放还有一种情况清理命令执行成功docker system df数字也降了但df -h迟迟不降。这可能是文件系统层面的延迟尤其是某些文件系统对已删除空间的回收不是实时的。通常等几分钟或触发一次 sync 就会恢复sync如果等了很久还不降再回到 4.1 的lsof排查。别急着重启机器先确认是不是真的没释放。5. 从“一键清理”到“不再需要清理”清理是治标配置才是治本。一个健康的 Docker 环境不该频繁依赖清理脚本。这一节讲几个从源头控制磁盘增长的配置做完之后清理脚本从“每周必跑”变成“偶尔跑一次”。5.1 给容器日志加上轮转上限这是收益最大的一项配置。Docker 默认的 json-file 驱动不轮转必须显式配置。在/etc/docker/daemon.json里加{ log-driver: json-file, log-opts: { max-size: 50m, max-file: 3 } }含义是单个日志文件最大 50MB最多保留 3 个也就是单容器日志上限 150MB。改完systemctl restart docker生效但只对新建的容器生效已有容器需要重建。这个配置我建议所有环境都加上。50m/3 对大多数应用够用日志量大的可以调到 100m/5。关键是有了上限就不会出现单个日志文件几十 GB 的情况。5.2 构建缓存的容量限制BuildKit 支持给缓存设上限避免无限增长。在daemon.json里配置{ builder: { gc: { enabled: true, defaultKeepStorage: 10GB } } }这样构建缓存超过 10GB 会自动回收不用等手动清理。defaultKeepStorage的值按机器磁盘大小调整构建机可以给大一点个人机器 5-10GB 足够。5.3 定期任务的编排思路即使有了上面的配置定期清理仍有必要作为兜底。用 cron 或 systemd timer 都行关键是频率别太高、日志要留。# 每周日凌晨 3 点执行清理输出到日志 0 3 * * 0 /usr/local/bin/docker-clean.sh /var/log/docker-clean.log 21频率上我倾向每周一次太频繁没意义磁盘不会一周涨满太稀疏又可能来不及。日志一定要留出问题时能回溯“上次清理删了什么、释放了多少”。5.4 监控告警比事后清理更重要最后一点也是很多人忽略的与其等磁盘满了再清理不如提前告警。一个简单的监控脚本磁盘使用率超过 80% 就发通知比事后救火从容得多。#!/usr/bin/env bash THRESHOLD80 USAGE$(df / | awk NR2 {gsub(%,); print $5}) if [[ $USAGE -ge $THRESHOLD ]]; then echo 磁盘使用率 ${USAGE}%超过阈值 ${THRESHOLD}% # 这里接通知渠道 fi这个脚本配合 cron 每小时跑一次成本极低但能让你在磁盘真正告急前介入。清理脚本是消防员监控才是防火墙。我个人在实际操作中的体会是Docker 磁盘问题从来不是“删一下就完事”的技术活而是“搞清楚什么能删、什么不能删、怎么让它别再涨”的系统活。一开始我也图省事直接docker system prune -a -f --volumes一把梭结果有次把测试环境的数据库卷清了重建数据花了大半天。从那以后我的清理脚本就再也没碰过卷也加上了--dry-run。真正省心的状态是日志轮转配好、缓存上限设好、监控告警挂好然后清理脚本安安静静躺在 cron 里一周跑一次日志里记着它释放了多少空间——你几乎忘了它的存在但它一直在兜底。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询