Docker 不限制日志文件大小磁盘会被撑爆
Docker 日志无限增长:磁盘被撑爆的隐患与根治方案
你是否遇到过服务器磁盘突然被占满,却找不到大文件?罪魁祸首很可能就是 Docker 容器的日志。默认情况下,Docker 不会对容器输出的 stdout/stderr 日志做任何大小或数量限制。一旦容器持续运行并输出大量日志,日志文件就会像滚雪球一样膨胀,直到占满整个磁盘。这不仅会导致服务异常,还会影响同一台主机上的其他应用,甚至使系统陷入不可用状态。本教程将带你理解这个问题,并提供几套从临时急救到永久防范的完整解决方案。
1. 为什么 Docker 日志会撑爆磁盘?
当我们使用 docker logs 查看容器输出时,背后依赖的是日志驱动(logging driver)。如果不做任何配置,Docker 默认使用 json-file 驱动,并将日志写入宿主机的 /var/lib/docker/containers/<容器ID>/<容器ID>-json.log 文件中。这种纯追加式的写入方式,加上没有大小轮转机制,意味着:
- 应用日志输出越多,文件越大。
- 文件只增不减,除非手动删除或重启容器(旧版 Docker 中重启会清空日志,高版本中重启不会自动清空)。
- 多个容器同时产生大量日志时,
/var/lib/docker所在分区会迅速被填满。
2. 如何发现“巨型”日志文件?
在排查磁盘空间问题时,可以先确认容器日志的占用情况。
2.1 检查 Docker 根目录磁盘使用
docker system df
该命令会显示镜像、容器、数据卷的总大小。重点关注 Local Volumes 与 Build Cache 之外,若 Containers 的总大小异常,可能就是日志在作祟。
2.2 找出占用最大的容器日志文件
可以直接进入 Docker 数据目录查看:
du -sh /var/lib/docker/containers/*/*-json.log | sort -hr | head -10
如果输出文件大小动辄上 GB,就需要立即处理。
2.3 使用脚本批量查看
如果希望看到容器名与日志大小的对应关系,可以执行以下命令:
sudo find /var/lib/docker/containers -name "*-json.log" -exec ls -lh {} \; | awk '{print $5, $9}'
再结合 docker ps -a 中的容器 ID 进行映射。
3. 立即释放磁盘空间(紧急止血)
当磁盘已经接近写满,需要马上恢复服务可用性,不建议直接删除日志文件,因为 Docker 仍持有文件句柄,删除后空间不会真正释放。
正确做法:
-
清空日志内容而不删除文件
sudo truncate -s 0 /var/lib/docker/containers/<容器ID>/<容器ID>-json.log将
<容器ID>替换为实际值。执行后日志文件会被清零,且 Docker 仍然可以正常写入,磁盘空间立即回收。 -
重启容器(谨慎使用) 在部分配置下,
docker restart会重新创建日志文件,旧的日志文件会被重命名(如-json.log.1)。但默认的json-file驱动如果没有设置max-size,新文件仍会无限增长,且旧文件不会自动删除,依然占用空间。所以重启后用truncate把旧文件也清掉更稳妥。
4. 永久限制日志大小:三种方案
要根治问题,必须对日志添加大小和文件数量限制。可以从 Docker 守护进程级别或容器级别进行配置。
4.1 修改 Docker Daemon 全局配置(影响所有容器)
编辑 Docker 守护进程配置文件 /etc/docker/daemon.json(如不存在则新建),加入以下内容:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
max-size:单个日志文件的最大大小,超过后自动轮转。常用单位k、m、g。max-file:保留的轮转日志文件数量。例如设置为3,会保留-json.log、-json.log.1、-json.log.2,旧文件会被自动删除。
保存后重启 Docker 服务:
sudo systemctl restart docker
注意: 该配置仅对新创建的容器生效,已运行的容器不会自动继承。需重建容器才可应用。
4.2 在 docker run 或 docker-compose 中为单个容器指定
如果你不想影响全局,或需要针对不同容器设置不同大小,可以在启动容器时添加日志选项。
命令行方式:
docker run -d \
--log-opt max-size=50m \
--log-opt max-file=5 \
--name myapp \
your-image
Docker Compose 方式:
在 docker-compose.yml 的 service 下添加 logging 字段:
services:
myapp:
image: your-image
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "5"
然后重新创建容器:
docker-compose up -d
4.3 使用其他日志驱动
如果业务场景不需要使用 docker logs 查看日志,或者希望将日志统一发送到集中式平台(如 ELK、Loki、syslog),可以切换日志驱动,从根源上避免本地文件膨胀。常用替代驱动:
syslog:将日志发送到宿主机的 syslog 服务。journald:写入 systemd journal,由 journal 自行管理轮转。fluentd/gelf/loki:直接发送到远程日志收集系统,本地不保留文件。
修改 Daemon 全局驱动,或容器级 --log-driver 即可。例如使用 journald:
docker run --log-driver=journald your-image
但要注意,切换后 docker logs 将无法查看日志。
5. 如何安全清理已有容器的历史日志?
假设你已经通过以上配置重建了容器,但宿主机上还残留着过去未轮转的巨型日志文件,可以按以下步骤清理:
- 找出所有容器 ID 对应的废弃日志文件(这些容器可能已被删除,但日志仍留在磁盘):
sudo find /var/lib/docker/containers/ -name "*-json.log*" -type f - 确认这些文件对应的容器是否还在运行。对于已停止或已经不再需要的容器,可以直接删除整个容器目录(需先删除容器):
docker rm <容器ID> # 然后手动删除残留目录 sudo rm -rf /var/lib/docker/containers/<容器ID> - 对于仍在使用中的容器,应使用
truncate -s 0清空文件,而不是直接rm,否则可能导致 Docker 写入错误。
6. 设置监控与告警(避免未来再次踩坑)
-
磁盘使用监控
在服务器上部署简单的磁盘监控,如node_exporter+Prometheus+Grafana,或使用云厂商的监控报警服务。当/var/lib/docker所在分区使用率超过 80% 时告警。 -
日志轮转自动验证
定期检查容器的日志选项是否生效:docker inspect --format='{{.HostConfig.LogConfig}}' <容器名>输出中应显示
max-size和max-file配置。 -
使用 cron 任务兜底
编写一个脚本,每日查找大于特定阈值(如 500M)的-json.log文件并发出通知,或自动进行truncate(谨慎操作,确保不影响业务)。
7. 总结
Docker 默认无限制日志写入是一个极易被忽略但后果严重的配置缺陷。你应当始终:
- 在生产环境中 设置日志大小限制,无论全局还是单容器。
- 定期审计 宿主机磁盘占用,尤其是
/var/lib/docker/containers下的日志文件。 - 将日志收集与容器引擎分离,使用集中式日志平台彻底摆脱本地文件依赖。
遵循以上实践,既能避免磁盘被撑爆的危机,又能建立一套可观测、可维护的日志体系。下次在启动容器时,别忘了加上 --log-opt max-size=100m —— 这小小的参数可以为你省去半夜处理磁盘故障的噩梦。