Docker 日志磁盘爆满排障:从清理到轮转预警

Docker 日志磁盘爆满排障封面

Docker 日志占满磁盘时,最直接的表现可能不是“日志报错”,而是容器突然重启、数据库写入失败、网站上传图片失败,甚至 SSH 登录后发现根分区已经 100%。这篇指南会教你如何确认日志是不是罪魁祸首,如何在不中断业务的前提下清理旧日志,再把日志轮转和容量预警补上,避免同样的问题过几天再次出现。

下面的方法适合运行在 [VPS](https://cn.hostease.com/vps/)([虚拟专用服务器](https://cn.hostease.com/vps/))或独服([独立服务器](https://cn.hostease.com/dedicated-server/))上的常见 Docker 环境。本文默认你使用 Linux 服务器,并且 Docker 日志驱动仍是常见的 json-file。如果你的业务同时有 Nginx、数据库、应用文件上传和备份任务,也建议把本文当作一次完整的磁盘排障流程,而不是只复制一条清理命令。

先判断:磁盘到底被 Docker 日志占了多少

处理磁盘告警时,不建议一上来就删除文件。正确顺序是先看分区,再定位目录,最后确认具体容器。这样做的原因很简单:如果真正占空间的是数据库备份或上传目录,盲目清理 Docker 日志并不能解决问题,还可能掩盖根因。

df -h
sudo du -h --max-depth=1 /var/lib/docker 2>/dev/null | sort -h
sudo du -h --max-depth=1 /var/lib/docker/containers 2>/dev/null | sort -h | tail -20

df -h 用来看哪个分区满了,常见问题是根分区 / 使用率超过 90%。第二条命令检查 Docker 数据目录总量,第三条命令会列出日志和容器元数据占用较大的目录。如果 /var/lib/docker/containers 已经达到几十 GB,日志失控的概率就很高。

接下来可以把目录名和容器对应起来。Docker 的容器目录通常以容器 ID 开头,日志文件常见名称是 <container-id>-json.log。用下面的命令可以查看日志文件大小最大的容器:

sudo find /var/lib/docker/containers -name '*-json.log' -type f -printf '%s %p
'   | sort -nr   | head -10   | awk '{printf "%.2f GB  %s
", $1/1024/1024/1024, $2}'

如果某个日志文件已经超过 5GB,就要继续确认它对应哪个容器。可以截取路径中的容器 ID 前 12 位,再用 docker ps -a --no-trunc 对照:

docker ps -a --no-trunc --format '{{.ID}}  {{.Names}}  {{.Image}}  {{.Status}}'

这一步能帮助你区分“正常高流量日志”和“异常刷屏日志”。例如,一个访问量较高的 Nginx 容器每天产生 1GB 访问日志并不罕见;但一个业务容器每秒输出同一条异常堆栈,2 小时写满 20GB,就不是单纯加大磁盘能解决的问题。关于服务器容量和性能排查的基础思路,也可以参考 服务器相关文章,先把存储、CPU 和内存三个维度分开看。

定位 Docker 日志占用来源

临时止血:安全清理日志,不破坏容器目录

确认是 Docker 日志占满磁盘后,临时止血的目标是释放空间,而不是删除容器。不要直接 rm -rf /var/lib/docker/containers/xxx,这样可能破坏 Docker 对容器元数据的管理。对 json-file 日志,较稳妥的做法是把日志文件截断为 0 字节:

sudo truncate -s 0 /var/lib/docker/containers/<container-id>/<container-id>-json.log

如果有多个日志文件都很大,可以先只处理最大的 1-3 个,释放出足够空间后再慢慢排查业务原因。批量处理前建议先输出清单并人工确认:

sudo find /var/lib/docker/containers -name '*-json.log' -size +1G -print

确认无误后,再执行批量截断:

sudo find /var/lib/docker/containers -name '*-json.log' -size +1G -exec sudo truncate -s 0 {} \;

为什么使用 truncate 而不是删除文件?因为容器进程可能仍然持有日志文件句柄。删除文件后,磁盘空间未必立刻释放,Docker 也可能继续向已删除但仍被进程占用的文件写入;截断则是在保留文件路径和句柄的同时把内容清空,更适合临时恢复空间。

清理后要立即验证两件事:分区是否降到安全范围,异常日志是否还在高速增长。

df -h
sudo find /var/lib/docker/containers -name '*-json.log' -type f -printf '%s %p
'   | sort -nr | head -10

如果 5 分钟内同一个日志文件又涨了几百 MB,说明业务容器仍在持续输出异常。此时只做日志轮转不够,还需要进入容器日志查看具体报错:

docker logs --tail 200 <container-name>

常见根因包括数据库连接失败后无限重试、API 凭据错误导致每秒打印异常、健康检查路径写错、调试级别日志误开到生产环境。对于外贸站点或业务后台,建议把这类排障纳入日常运维流程;如果你正在评估运行环境,可以结合 VPS(虚拟专用服务器)主机 的磁盘、快照和监控能力一起规划。

长期方案:给 Docker 配置日志轮转

临时清理只是把水舀出去,日志轮转才是给水桶装上溢流口。Docker 使用 json-file 日志驱动时,可以通过 /etc/docker/daemon.json 设置单个日志文件大小和保留份数。一个适合多数中小型网站的起点是:单文件 100MB,保留 3 份。

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json >/dev/null <<'EOF'
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"
  }
}
EOF
sudo systemctl restart docker

这组配置的含义是:每个容器最多保留约 300MB 的 Docker 标准输出日志。即使 20 个容器同时运行,理论上 Docker 标准日志也会被控制在约 6GB 以内。实际空间还会受到镜像层、volume、应用自有日志和数据库文件影响,所以它不是完整容量规划,但能显著降低“单个容器刷屏写满系统盘”的风险。

有一个细节容易被忽略:daemon.json 的日志轮转配置通常只对新创建的容器生效。已经存在的容器可能需要重新创建,才能应用新的日志选项。对于用 Compose 管理的服务,可以在维护窗口执行:

docker compose down
docker compose up -d

如果你不能接受所有服务同时重启,可以逐个服务滚动重建。重建前先确认 Compose 文件、环境变量和 volume 都在版本管理或备份范围内,避免“日志问题修好了,服务配置丢了”。

也可以在单个容器运行时显式指定日志参数,适合测试或少量手工启动的服务:

docker run -d   --name app   --log-driver json-file   --log-opt max-size=100m   --log-opt max-file=3   nginx:stable

更推荐的方式仍然是写入 Docker daemon 级别配置,因为它能覆盖后续新建容器,减少团队成员漏写参数的概率。若你同时在做 Nginx 反向代理、应用容器和数据库分层,可以参考 Nginx 反向代理与负载均衡指南,把入口日志、应用日志和容器标准输出分开治理。

Docker 日志轮转策略示意

不要只看 Docker:把应用日志和 volume 一起纳入检查

有些团队配置了 Docker 日志轮转后,磁盘仍然不断上涨,原因是应用把日志写进了 volume 或容器内部文件,而不是标准输出。比如 Java 应用写 /app/logs/app.log,Nginx 写 /var/log/nginx/access.log,数据库写慢查询日志,这些并不会被 Docker 的 json-file 轮转规则自动管理。

建议每次磁盘排障都同时检查 volume 和业务目录:

docker system df
sudo du -h --max-depth=1 /var/lib/docker/volumes 2>/dev/null | sort -h | tail -20
sudo du -h --max-depth=1 /data 2>/dev/null | sort -h | tail -20

docker system df 可以看镜像、容器、缓存和 volume 的大致占用。不要看到 docker system prune -a 就直接执行,它会删除未使用镜像,可能影响回滚。生产环境中更稳妥的方式是先列出对象,再按服务窗口清理。

如果应用日志写在 volume 中,就要使用应用自己的轮转方式。例如 Nginx 可以依赖 logrotate,Java 应用可以在日志框架中设置单文件大小、保留天数和压缩策略。一个实用基线是:访问日志保留 7-14 天,错误日志保留 30 天,单文件超过 100-200MB 即轮转压缩;合规或审计要求更高的业务,应把日志转存到专门的日志平台,而不是长期堆在系统盘。

这里还涉及备份策略。日志文件通常不应该和业务数据使用同一套备份保留规则,否则备份包会越来越大,恢复时也会拖慢速度。对于 WordPress、数据库或用户上传目录,可以参考 WordPress 相关文章 中的备份和性能优化思路,把“必须恢复的数据”和“可重新生成的日志”分开处理。

建立容量预警:比磁盘 100% 后抢修更可靠

日志治理的最后一步是预警。一个简单可执行的标准是:磁盘使用率达到 80% 提醒,达到 90% 告警,达到 95% 进入应急处理。即使暂时没有完整监控平台,也可以用系统定时任务做最低限度的保护。

cat > /usr/local/bin/disk-check.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
THRESHOLD=85
USAGE=$(df / | awk 'NR==2 {gsub("%", "", $5); print $5}')
if [ "$USAGE" -ge "$THRESHOLD" ]; then
  logger -p local0.warn "disk usage ${USAGE}% on root partition"
fi
EOF
chmod +x /usr/local/bin/disk-check.sh
(crontab -l 2>/dev/null; echo "*/10 * * * * /usr/local/bin/disk-check.sh") | crontab -

这段脚本每 10 分钟检查一次根分区,并把超过阈值的事件写入系统日志。它不能替代专业监控,但能作为临时兜底。成熟一些的环境可以把告警接入邮件、企业聊天工具或监控系统,并增加三个维度:Docker 日志目录大小、volume 增长速度、过去 1 小时新增日志量。

排障完成后,我们建议记录一次变更单,至少写清楚四项内容:

  • 触发时间和峰值:例如 2026-07-30 10:20,根分区从 72% 升到 100%。
  • 最大文件来源:例如某业务容器 json.log 在 6 小时内增长到 18GB。
  • 已执行修复:例如截断日志、配置 max-size=100mmax-file=3、重建容器。
  • 后续验证标准:例如连续 24 小时根分区低于 75%,同一日志文件低于 300MB。

这份记录看似简单,但对团队协作很有价值。下一次再出现磁盘增长,就能快速判断是同一问题复发,还是新的服务产生了异常日志。

服务器容量预警与复盘记录

总结:把一次清理变成一套运维规范

Docker 日志占满磁盘的解决顺序可以概括为:先用 dfdu 确认空间来源,再用 truncate 临时释放大日志文件,随后配置 Docker 日志轮转,最后补上容量预警和复盘记录。不要只依赖扩容,因为异常日志会把更大的磁盘也写满;也不要只依赖清理命令,因为问题会在下一次异常刷屏时重新出现。

如果你的业务已经从单个测试容器发展到多个生产服务,建议把日志上限、备份目录、监控阈值和应急命令写进部署文档。对于需要稳定运行网站、接口或后台系统的用户,可以考虑选择支持快照、监控和中文技术支持的 Hostease 服务器方案,再配合本文的日志治理流程,把磁盘风险控制在可预期范围内。

发表评论