Docker日志轮转:防止日志撑满VPS磁盘
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-driver和log-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-size和max-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日志管理方案有四个层次:
- 全局轮转 在
/etc/docker/daemon.json中通过max-size和max-file防止所有容器的日志无限增长。 - 服务级覆盖 在Docker Compose中给日志量大的服务设置更严格的限制。
- 自动清理 通过systemd timer运行
docker system prune清除已停止的容器、未使用的镜像和构建缓存。 - 磁盘监控 通过告警脚本在问题演变为故障之前发现它们。
设置完成后,验证它是否正常工作:对你的容器运行docker inspect检查配置,运行docker system df确认当前使用量,等待prune timer至少触发一次。检查journalctl -u docker-prune.service确认它已运行。
版权所有 2026 Virtua.Cloud。保留所有权利。 本内容为 Virtua.Cloud 团队原创作品。 未经书面许可,禁止复制、转载或再分发。