Docker日志轮转:防止日志撑满VPS磁盘

2 分钟阅读·Matthieu·linuxdisk-managementloggingdocker-composedocker|

Docker默认的日志驱动会无限制存储数据。一个高流量容器可以在几天内填满50GB的VPS磁盘。本教程配置全局日志轮转、Compose服务级覆盖、自动清理和磁盘使用监控。

Docker的默认日志配置没有大小限制。容器写入stdout或stderr的每一行都会永久存储在磁盘上。在只有25GB或50GB存储空间的VPS上,一个日志量大的容器就能在几天内耗尽所有可用空间。

本教程解决这个问题。你将配置全局日志轮转,在Docker Compose中设置服务级限制,自动化磁盘清理,并设置监控,确保问题不会在毫无防备时出现。

所有命令已在Debian 12和Ubuntu 24.04上使用Docker Engine 28.x/29.x测试通过。

前提条件:

  • 一台运行Debian 12或Ubuntu 24.04且已安装Docker的VPS
  • 拥有sudo用户的SSH访问权限
  • 对Docker和Docker Compose有基本了解

为什么Docker容器日志会撑满磁盘?

Docker默认的日志驱动是json-file。它捕获容器写入stdout和stderr的所有内容,然后以JSON格式存储在/var/lib/docker/containers/<container-id>/<container-id>-json.log中。默认没有最大大小限制,也没有轮转机制。文件会一直增长,直到磁盘满为止。

一个以INFO级别记录日志的Node.js应用每天大约产生50MB数据。一个中等流量的反向代理可以产生200MB甚至更多。在50GB的VPS上,这意味着一个未管理的容器可以在几周内消耗掉所有空闲空间。

当磁盘满了,所有东西同时崩溃:容器无法写入,数据库崩溃,SSH会话可能冻结,你甚至无法登录去修复问题。

如何检查当前磁盘使用情况

在做任何修改之前,先评估一下现状:

df -h /var/lib/docker

这会显示Docker数据目录使用了多少空间。然后获取Docker层面的详细分解:

docker system df

预期输出:

TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          5         3         1.2GB     450MB (37%)
Containers      8         4         3.8GB     3.1GB (81%)
Local Volumes   3         2         500MB     120MB (24%)
Build Cache     12        0         800MB     800MB (100%)

"Containers"行显示的是日志文件大小。如果这个数字异常大,日志就是问题所在。

查看每个容器的详细分解:

docker system df -v

这会列出每个容器及其日志大小,帮你找到罪魁祸首。

如何直接定位最大的日志文件

如果docker system df确认了问题,找出具体哪些日志在占用空间:

sudo find /var/lib/docker/containers/ -name "*-json.log" -exec ls -sh {} + | sort -rh | head -10

这会列出最大的10个容器日志文件及其大小。

紧急处置:立即回收磁盘空间

如果磁盘已满或接近满,在配置轮转之前先解决眼前的问题。

清空特定容器的日志文件(无需停止容器):

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

<container-id>替换为docker ps --no-trunc -q输出的实际容器ID。

一次清空所有Docker日志文件:

sudo sh -c 'truncate -s 0 /var/lib/docker/containers/*/*-json.log'

验证空间已回收:

df -h /var/lib/docker

这只是临时修复。日志会继续增长。后续章节介绍永久解决方案。

如何在Docker daemon.json中配置日志轮转?

daemon.json文件为所有新容器设置默认日志选项。Docker的json-file驱动支持max-size(轮转前单个日志文件的最大大小)和max-file(保留的轮转文件数量)。log-opts中的所有值必须是字符串,即使是数字也要加引号。

创建或编辑daemon配置:

sudo nano /etc/docker/daemon.json

如果文件不存在就创建。如果已有配置(如自定义镜像仓库或DNS),在现有配置旁边添加log-driverlog-opts键。

各VPS磁盘大小的推荐配置

根据VPS磁盘大小选择合适的值:

VPS磁盘大小 max-size max-file 每容器最大日志量 说明
25 GB 5m 3 15 MB 磁盘紧张,保持日志最小化
50 GB 10m 5 50 MB 标准VPS,均衡保留
100 GB 25m 5 125 MB 磁盘充裕,较长保留期
200 GB+ 50m 5 250 MB 大磁盘,延长调试窗口

对于50GB的VPS(最常见的选择),使用以下配置:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "5"
  }
}

保存文件,然后重启Docker:

sudo systemctl restart docker

验证新默认值已生效:

docker info --format '{{.LoggingDriver}}'

预期输出:

json-file

运行一个容器并检查其配置,确认日志选项对新容器生效:

docker run -d --name log-test alpine echo "test" && docker inspect --format '{{.HostConfig.LogConfig}}' log-test && docker rm log-test

预期输出:

{json-file map[max-file:5 max-size:10m]}

修改daemon.json后对正在运行的容器有什么影响?

已有容器保持原来的日志配置。新设置仅对重启后创建的容器生效。正在运行的容器必须重新创建才能使用新的默认值。

如果你使用Docker Compose:

docker compose down && docker compose up -d

对于独立容器,停止并删除它们,然后重新启动。--force-recreate标志也可以:

docker compose up -d --force-recreate

验证特定容器是否使用了新配置:

docker inspect --format '{{.HostConfig.LogConfig}}' <container-name>

预期输出:

{json-file map[max-file:5 max-size:10m]}

如何在Docker Compose中设置日志限制?

Docker Compose中的服务级日志配置会覆盖daemon.json的默认值。这允许你对日志量大的服务设置更严格的限制,而给安静的服务更多空间。

在任意服务下添加logging块:

services:
  web:
    image: nginx:alpine
    logging:
      driver: json-file
      options:
        max-size: "5m"
        max-file: "3"
    ports:
      - "80:80"

  api:
    image: node:22-alpine
    logging:
      driver: json-file
      options:
        max-size: "20m"
        max-file: "5"
    ports:
      - "3000:3000"

为了避免在每个服务中重复logging块,使用YAML anchor(锚点):

x-logging: &default-logging
  logging:
    driver: json-file
    options:
      max-size: "10m"
      max-file: "5"

services:
  web:
    image: nginx:alpine
    <<: *default-logging
    ports:
      - "80:80"

  api:
    image: node:22-alpine
    <<: *default-logging
    ports:
      - "3000:3000"

  worker:
    image: myapp/worker:latest
    logging:
      driver: json-file
      options:
        max-size: "50m"
        max-file: "3"

worker服务用自己的限制覆盖了anchor。其他所有服务使用共享配置。

应用配置:

docker compose up -d --force-recreate

检查:

docker inspect --format '{{.HostConfig.LogConfig}}' web

json-file、local和journald日志驱动有什么区别?

Docker自带三种在主机上存储日志的驱动。每种都有不同的取舍。json-file驱动是默认选项。local驱动是Docker推荐的磁盘效率更高的替代方案。journald驱动与systemd的journal集成。

特性 json-file local journald
默认轮转 有(总共100MB) 由journald管理
压缩 可选(compress: "true" 默认开启 由journald管理
docker logs支持
日志格式 JSON(可读) 二进制(内部格式) 二进制(journald)
外部工具访问 简单(纯文本文件) 不支持 通过journalctl
默认max-size 无限制 每文件20MB 在journald.conf中设置
默认max-file 1(不轮转) 5个文件 不适用

何时使用哪种驱动

json-file是安全的默认选择。它到处都能用,支持docker logs,日志文件是纯JSON格式,任何工具都能解析。加上max-sizemax-file配置,对大多数VPS场景来说足够了。

local驱动在磁盘效率上更优。压缩默认开启,轮转内置(5个文件,每个20MB = 每容器100MB),无需额外配置。代价是:日志文件使用内部二进制格式。直接读取文件的外部日志收集器(如文件模式的Filebeat)无法解析它们。如果你只通过docker logs读取日志或通过Docker日志插件传输,可以切换到local

journald适合你已经在用systemd的journal管理所有其他服务的日志,并且希望容器日志也集中在同一个地方的情况。轮转由journald自身的配置(/etc/systemd/journald.conf)处理。你用journalctl而不是docker logs来读取日志(不过docker logs仍然可用)。

如何切换到local驱动

编辑/etc/docker/daemon.json

{
  "log-driver": "local",
  "log-opts": {
    "max-size": "10m",
    "max-file": "5"
  }
}

重启Docker:

sudo systemctl restart docker

检查:

docker info --format '{{.LoggingDriver}}'

预期输出:

local

重新创建容器以应用新驱动:

docker compose up -d --force-recreate

如何使用journald驱动

编辑/etc/docker/daemon.json

{
  "log-driver": "journald"
}

重启Docker:

sudo systemctl restart docker

读取特定容器的日志:

sudo journalctl CONTAINER_NAME=mycontainer --no-pager -n 50

实时跟踪日志:

sudo journalctl CONTAINER_NAME=mycontainer -f

journald的轮转在/etc/systemd/journald.conf中控制。关键设置:

[Journal]
SystemMaxUse=500M
SystemMaxFileSize=50M
MaxRetentionSec=7day

编辑后,重启journald:

sudo systemctl restart systemd-journald

如何用cron或systemd timer自动化Docker清理?

日志轮转防止单个容器无限增长。但Docker还会积累已停止的容器、未使用的镜像、悬空的构建缓存和孤立的网络。docker system prune可以清理这些。

docker system prune具体删除什么?

默认情况下,docker system prune删除:

  • 所有已停止的容器
  • 所有未被运行中容器使用的网络
  • 所有悬空镜像(dangling images,未被任何容器引用的无标签镜像)
  • 所有未使用的构建缓存

不会删除:

  • 正在运行的容器
  • 命名卷(named volumes,你的数据库数据是安全的)
  • 仍被引用的带标签镜像
  • 正在运行的容器使用的镜像

--all标志会额外删除所有未使用的镜像(不仅是悬空的)。--volumes标志会将匿名卷加入清理范围。谨慎使用--volumes:它会销毁匿名卷中的数据。

方案一:cron定时任务

创建每周清理任务:

sudo crontab -e

添加:

0 3 * * 0 /usr/bin/docker system prune -f >> /var/log/docker-prune.log 2>&1

这会在每周日03:00运行。-f标志跳过确认提示。输出写入日志文件供审计。

验证crontab已保存:

sudo crontab -l

方案二:systemd timer(推荐)

Systemd timer比cron更可靠。它们记录到journal,能处理错过的运行(如果服务器当时关机),且更容易监控。

创建service单元:

sudo nano /etc/systemd/system/docker-prune.service
[Unit]
Description=Docker system prune
Wants=docker.service
After=docker.service

[Service]
Type=oneshot
ExecStart=/usr/bin/docker system prune -f --filter "until=168h"

--filter "until=168h"标志只清理7天前的对象。这能保护你可能还想检查的近期已停止容器。

创建timer:

sudo nano /etc/systemd/system/docker-prune.timer
[Unit]
Description=Run Docker prune weekly

[Timer]
OnCalendar=Sun *-*-* 03:00:00
Persistent=true
RandomizedDelaySec=1800

[Install]
WantedBy=timers.target

Persistent=true意味着如果服务器在计划时间关机,任务会在下次启动时运行。RandomizedDelaySec在你有多台服务器时分散负载。

启用并启动timer:

sudo systemctl daemon-reload
sudo systemctl enable --now docker-prune.timer

enable使其在重启后存活。--now立即启动。

验证timer已激活:

sudo systemctl status docker-prune.timer

检查下次运行时间:

sudo systemctl list-timers docker-prune.timer

手动测试:

sudo systemctl start docker-prune.service

检查结果:

sudo journalctl -u docker-prune.service --no-pager -n 20

如何安全清理Docker卷?

卷(volume)保存持久数据:数据库、上传文件、配置。在这里要小心操作。

列出所有卷及其使用情况:

docker volume ls

仅显示未挂载到任何容器的卷:

docker volume ls -f dangling=true

删除悬空卷:

docker volume prune -f

这只删除当前没有被任何容器(无论运行还是停止)使用的卷。挂载到已停止容器的命名卷是安全的。

验证剩余内容:

docker volume ls

不要在docker system prune --volumes之后立即运行docker volume prune。带--volumes的system prune已经处理了卷清理,两者同时运行是多余的。

删除已确认不需要的特定卷:

docker volume rm <volume-name>

删除前务必检查卷中的数据:

docker volume inspect <volume-name>

Mountpoint字段显示数据在磁盘上的位置。你可以检查其内容:

sudo ls -la $(docker volume inspect --format '{{.Mountpoint}}' <volume-name>)

如何监控VPS上的Docker磁盘使用?

自动化监控防止意外发生。本节设置一个阈值告警,检查Docker磁盘使用量并在超过限制时发送警告。

快速手动检查

随时运行这两个命令查看快照:

df -h /var/lib/docker
docker system df

查看每个容器和每个镜像的详细分解:

docker system df -v

自动告警脚本

创建监控脚本:

sudo nano /usr/local/bin/docker-disk-alert.sh
#!/bin/bash
# Alert when Docker's partition exceeds a usage threshold
THRESHOLD=80
MAILTO="admin@example.com"

USAGE=$(df /var/lib/docker | awk 'NR==2 {gsub(/%/,""); print $5}')

if [ "$USAGE" -ge "$THRESHOLD" ]; then
    DOCKER_DF=$(docker system df 2>&1)
    DISK_DF=$(df -h /var/lib/docker 2>&1)
    TOP_LOGS=$(find /var/lib/docker/containers/ -name "*-json.log" -exec ls -sh {} + 2>/dev/null | sort -rh | head -5)

    BODY="Docker disk usage on $(hostname) is at ${USAGE}%.

Disk usage:
${DISK_DF}

Docker breakdown:
${DOCKER_DF}

Largest log files:
${TOP_LOGS}"

    echo "$BODY" | mail -s "ALERT: Docker disk at ${USAGE}% on $(hostname)" "$MAILTO"
    logger -t docker-disk-alert "Docker disk usage at ${USAGE}% - alert sent"
fi

设置权限:

sudo chmod 750 /usr/local/bin/docker-disk-alert.sh

验证权限:

ls -la /usr/local/bin/docker-disk-alert.sh

预期输出:

-rwxr-x--- 1 root root 612 Mar 19 12:00 /usr/local/bin/docker-disk-alert.sh

该脚本需要mailutils(或mailx)来发送邮件。如果未安装,先安装:

sudo apt install -y mailutils

测试脚本:

sudo /usr/local/bin/docker-disk-alert.sh

如果磁盘使用率低于阈值,什么都不会发生。要测试告警路径,临时将脚本中的THRESHOLD=1,运行后再改回去。

使用systemd timer调度告警

创建service:

sudo nano /etc/systemd/system/docker-disk-alert.service
[Unit]
Description=Check Docker disk usage

[Service]
Type=oneshot
ExecStart=/usr/local/bin/docker-disk-alert.sh

创建timer:

sudo nano /etc/systemd/system/docker-disk-alert.timer
[Unit]
Description=Check Docker disk usage every 6 hours

[Timer]
OnCalendar=*-*-* 00/6:00:00
Persistent=true

[Install]
WantedBy=timers.target

启用:

sudo systemctl daemon-reload
sudo systemctl enable --now docker-disk-alert.timer

检查:

sudo systemctl list-timers docker-disk-alert.timer

故障排除

磁盘已满,Docker无法启动

如果Docker因磁盘完全满而无法启动:

sudo truncate -s 0 /var/lib/docker/containers/*/*-json.log
sudo systemctl start docker

然后立即按上述方法配置日志轮转。

daemon.json语法错误导致Docker无法启动

如果daemon.json包含无效JSON,Docker将无法启动。验证文件:

sudo python3 -m json.tool /etc/docker/daemon.json

如果输出格式化的JSON,语法正确。如果输出错误,修复指示的行。

检查Docker的错误信息:

sudo journalctl -u docker.service -n 20 --no-pager

配置轮转后日志仍在增长

轮转只对新容器生效。已有容器保持原来的配置。重新创建它们:

docker compose up -d --force-recreate

验证新配置已生效:

docker inspect --format '{{.HostConfig.LogConfig}}' <container-name>

docker system prune没有释放太多空间

docker system prune不会动正在运行的容器、它们的日志或命名卷。如果空间问题出在日志上,清空日志文件或配置轮转。如果是卷的问题,在确认不会丢失所需数据后使用docker volume prune

检查什么在占用空间:

sudo du -sh /var/lib/docker/*

这会按Docker子系统分解使用量:containers(日志)、overlay2(镜像/层)、volumes和其他。

总结

VPS上完整的Docker日志管理方案有四个层次:

  1. 全局轮转/etc/docker/daemon.json中通过max-sizemax-file防止所有容器的日志无限增长。
  2. 服务级覆盖 在Docker Compose中给日志量大的服务设置更严格的限制。
  3. 自动清理 通过systemd timer运行docker system prune清除已停止的容器、未使用的镜像和构建缓存。
  4. 磁盘监控 通过告警脚本在问题演变为故障之前发现它们。

设置完成后,验证它是否正常工作:对你的容器运行docker inspect检查配置,运行docker system df确认当前使用量,等待prune timer至少触发一次。检查journalctl -u docker-prune.service确认它已运行。


版权所有 2026 Virtua.Cloud。保留所有权利。 本内容为 Virtua.Cloud 团队原创作品。 未经书面许可,禁止复制、转载或再分发。

准备好亲自尝试了吗?

通过日志轮转保持Docker VPS整洁。

查看 VPS 方案