找出 /var/lib/docker/containers/ 下体积排名前 10 的日志文件

找出 /var/lib/docker/containers/ 下体积排名前 10 的日志文件

SEO内容优化wl5net2026-11-14 9:01:03A+A-
约 4378 字 · 预计阅读 9 分钟

解决 Docker 容器日志撑爆磁盘 的最快终极方案,就是直接修改 /etc/docker/daemon.json 全局限制单日志文件体积与保留数量,并立刻使用 truncate 命令对已超大的日志做安全清零,绝不能粗暴执行 rm -rf。

找出 /var/lib/docker/containers/ 下体积排名前 10 的日志文件

在生产环境中,90% 的宿主机磁盘告警都是因为开发人员把日志级别调成 DEBUG 且未对 Docker 的 json-file 日志驱动做体积限制。容器在后台疯狂吐日志,几百 GB 的磁盘几小时就被占满,最后导致数据库挂掉、服务响应超时。

下面这套方案是我在数十个生产集群中验证过的排查与治理流程,按步骤操作即可永久解决该问题。


一、 核心方案速览

解决 Docker 日志膨胀问题遵循“先止血、再根治、后防复发”的原则:

  1. 应急止血(立刻释放空间):不要停容器,直接用 truncate -s 0 截断日志文件,空间秒级释放。
  2. 全局根治(永久生效):配置 Docker Daemon 的 log-opts,限制每个容器的日志大小(如 100M)和滚转数量(如 3 个)。
  3. 单容器覆盖(灵活调整):在 docker-compose.yml 或 docker run 中对高频输出服务单独设置日志策略。
  4. 清理残余(兜底防守):清理悬挂镜像(dangling images)与未使用的数据卷(volumes)。

二、 实操细节深度拆解

1. 应急止血:安全清空已有超大日志

很多新手运维看到磁盘满了,习惯性执行 rm -rf /var/lib/docker/containers/*/*.log。这是巨大的坑!

如果容器进程还在运行,它依然持有该日志文件的句柄(File Descriptor),你删了文件,磁盘句柄不释放,df -h 看依然是 100%,必须重启 Docker 或容器才能释放空间,这直接导致服务中断。

正确做法:使用 truncate 截断文件内容,无需重启服务。

第一步:快速定位谁在疯狂写日志
# 找出 /var/lib/docker/containers/ 下体积排名前 10 的日志文件
du -d 2 -h /var/lib/docker/containers/ | sort -h -r | head -n 10
第二步:一键安全清空所有容器日志

运行以下命令,直接将所有 *-json.log 文件体积置为 0:

# 安全清空,句柄不会丢失,磁盘空间立刻释放
find /var/lib/docker/containers/ -name "*-json.log" -exec truncate -s 0 {} +

避坑点:如果不确定是哪个容器,可以结合 docker ps --no-truncate 查出的 Container ID 与路径中的哈希值进行比对。


2. 全局根治:修改 Docker Daemon 配置文件

止血之后,必须在 Docker 引擎层面限制日志增长,否则过几天磁盘依然会被撑爆。

编辑 Docker 全局配置文件(不存在则新建):

vim /etc/docker/daemon.json

写入以下配置:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"
  },
  "live-restore": true
}

参数实操解析:

  • "log-driver": "json-file":Docker 默认日志驱动。
  • "max-size": "100m":单个日志文件最大允许 100MB。超过后自动触发滚转(Rotate)。
  • "max-file": "3":最多保留 3 个日志文件(旧日志自动删除)。即:单个容器最多产生 100M * 3 = 300M 的日志。
  • "live-restore": true:极其重要! 允许 Docker 守护进程在重启或热加载配置时,容器保持运行状态,避免重启 Docker 导致服务短暂中断。

配置完成后,重载配置(不要直接 systemctl restart docker):

systemctl reload docker

避坑点:此全局配置仅对“新创建的容器”生效! 已经运行的旧容器必须销毁并重新创建(docker stop -> docker rm -> docker run)才能继承配置。


3. 局部覆盖:针对特定服务单挑配置

如果你有些关键服务(如 Java 网关、订单系统)需要保留更多上下文日志排查问题,而一些无所谓的服务(如 Redis、Nginx)日志可以压缩,可以在服务编排文件中单独指定。

在 docker-compose.yml 中配置:
version: '3.8'

services:
  payment-service:
    image: payment:v1.0.2
    container_name: payment-service
    restart: always
    logging:
      driver: "json-file"
      options:
        max-size: "500m"  # 关键业务允许留大一点
        max-file: "5"
    ports:
      - "8080:8080"

  redis-cache:
    image: redis:6.2-alpine
    container_name: redis-cache
    logging:
      driver: "json-file"
      options:
        max-size: "10m"   # 缓存数据库限制严格一点
        max-file: "1"
在 docker run 原生命令中配置:
docker run -d \
  --name app-test \
  --log-driver json-file \
  --log-opt max-size=50m \
  --log-opt max-file=2 \
  -p 80:80 \
  nginx:alpine

三、 高频关联问题与避坑指南

在治理 Docker 容器日志撑爆磁盘 的过程中,你一定会遇到以下几个衍生问题,顺带一并解决:

问题 1:已经执行了 rm -rf 删除了 log 文件,但磁盘空间依旧没有释放

  • 原因:进程持有删除文件的句柄,空间被 deleted 状态的文件占用。
  • 排查命令:
    lsof -N | grep deleted
    # 或直接查占用大量空间但已删除的文件
    lsof / | grep deleted | sort -nr -k7 | head -n 10
  • 解决办法: 找到输出列表里的 PID(进程ID)和 FD(文件描述符),直接向句柄文件写入空内容:
    # 假设 PID 为 12345,FD 为 4
    > /proc/12345/fd/4

    如果是非核心容器,直接重启该容器即可释放:docker restart <container_id>。


问题 2:应用在容器内部往 /app/logs/app.log 写日志,根本不吐给 stdout

很多老旧 Java 项目(Log4j/Logback)或 Python 项目没有遵循 云原生 12 因素(12-Factor App)标准,不把日志打到标准输出(stdout/stderr),而是直接写进容器内部的硬盘路径。这会导致 Docker 的 max-size 限制完全失效!

  • 排查方法:Docker 的 json-file 日志没变大,但是容器目录 /var/lib/docker/overlay2/ 异常巨大。
  • 终极解决方案:

    1. 推进开发改代码:把日志追加器(Appender)改成 ConsoleAppender,直接打到标准输出。
    2. 挂载宿主机目录 + 宿主机 logrotate:如果改不动代码,把容器内日志目录挂载出来,用 Linux 原生的 logrotate 治理。

    挂载与 logrotate 实操:

    # 运行容器时挂载日志目录
    docker run -d -v /data/logs/my-app:/app/logs my-app:latest

    在宿主机 /etc/logrotate.d/my-app 创建切割规则:

    /data/logs/my-app/*.log {
      daily
      rotate 7
      missingok
      notifempty
      compress
      copytruncate
    }

    注意:必须加上 copytruncate,原理也是清空源文件并保留句柄,避免应用崩溃。


问题 3:排除了日志,磁盘空间依旧被 Overlay2 占满

有时撑爆磁盘的不是 *-json.log,而是各种未清理的镜像碎片、废弃容器和数据卷。

  • 排查 Docker 内部空间分布:

    docker system df

    输出示例:

    TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
    Images          15        5         12.5GB    8.2GB (65%)
    Containers      5         5         2.1GB     0B (0%)
    Local Volumes   10        2         45.3GB    40.1GB (88%)
    Build Cache     0         0         0B        0B
  • 安全清理脚本(建议配置 Cron 定时任务):

    # 1. 清理无用容器、悬挂镜像(tag 为 <none> 的)、未使用的网络
    docker system prune -f
    
    # 2. 如果确定没有挂载重要数据的孤立卷,强制清理未使用的 Volume(慎用,确认无无价数据)
    docker volume prune -f
    
    # 3. 删除 7 天前的无用镜像
    docker image prune -a --filter "until=168h" -f

四、 结尾总结

面对 Docker 容器日志撑爆磁盘 这类问题,靠临时 rm 文件只能治标,并且极易引发句柄泄露的二次故障。真正落地可行的长效机制必须包含以下三步:

  1. 应急用 truncate -s 0 替代 rm,秒级恢复业务;
  2. 全局部署 /etc/docker/daemon.json 限制 max-size 与 max-file,从根源掐断日志无限膨胀的可能;
  3. 规范开发落地,强制应用使用标准输出(stdout),杜绝在容器内部盲目写本地日志文件。

自动化运维的本质就是把常见坑点写进初始化脚本。建议将上述 Daemon 的全局配置直接打包进 Jenkins/Ansible 的服务器初始化 Playbook 中,在新机器开荒阶段就将其封堵,从根本上绝后患。

点击这里复制本文地址 以上内容由未来技术网整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

网站地图 robots.txt 网站死链 关于本站 工信部备案号浙ICP备2026012592号-2 浙公网安备33102402000669号