Docker 容器日志撑爆磁盘:定位、止血与根治实战
上周一台跑了大半年的云服务器突然报警:磁盘使用率 98%,几个在线服务陆续写不进数据。登录上去一看,根分区只剩不到 1GB,而业务数据本身并不大。最后查到的元凶很典型:某个容器的 JSON 日志文件膨胀到了 40 多 GB。本文把这次排查的完整过程写下来,同样的套路适用于绝大多数“磁盘莫名其妙满了”的容器服务器。
一、先定位:到底是谁占了磁盘
不要一上来就删文件,先确认三处:
第一,看整体磁盘占用。用 df -h 看哪个分区满了,再用 du -sh /var/lib/docker/* 粗筛。如果 /var/lib/docker 占了大头,基本可以锁定容器相关文件。
第二,看 Docker 自己的统计。docker system df 会列出镜像、容器、数据卷各自占用的空间,能快速排除“镜像太多”这种误判。这次排查中镜像只占 3GB,容器可写层也不大,问题不在这里。
第三,查容器日志文件。Docker 默认的 json-file 日志驱动会把容器标准输出原样写进 /var/lib/docker/containers/<容器 ID>/<容器 ID>-json.log,而且默认不轮转、不封顶。一个常年打印调试日志的服务,几个月就能写出几十 GB。用 du -sh /var/lib/docker/containers/*/*-json.log 排序,一眼就能看到罪魁祸首。
二、紧急止血:不断服务清日志
磁盘只剩 1GB 时,首要任务是先腾出空间,但千万别直接 rm 掉正在被 Docker 占用的日志文件。删除后文件句柄还在,空间不会释放,重启容器前磁盘依然是满的。
正确做法是截断文件:
echo "" > /var/lib/docker/containers/<容器 ID>/<容器 ID>-json.log
或者用 truncate -s 0 指定该文件。截断后空间立即释放,容器不用重启,服务不中断。生产环境上这是最稳的止血方式,我当时就是靠这一招先把磁盘使用率压回了 60% 以下。
顺带提醒:业务自己的应用日志如果也写在数据卷里,同样先确认句柄问题再处理,rm 对运行中进程打开的文件一律无效。
三、根治:给日志加上限
止血只是争取时间,不改配置的话日志还会继续长。根治分两种情况。
对于新容器,在 daemon.json 里全局配置日志轮转:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
意思是单个日志文件最多 10MB,最多保留 3 个轮转文件,单个容器日志上限 30MB。改完重启 Docker 服务生效。注意这个配置只对之后创建的容器生效,已经跑起来的容器不受影响。
对于已运行的容器,docker update 改不了日志驱动参数,必须重建容器。如果是用 Compose 管理的服务,在 docker-compose.yml 里给对应服务加 logging 配置:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
然后 docker compose up -d 重建即可,数据卷不受影响,服务只中断几秒。重建前先确认服务没有把重要状态只存在容器可写层里,否则重建会丢数据。
四、预防:别让问题再悄悄长大
第一,定期巡检。把 docker system df 和磁盘使用率放进日常巡检或监控告警,磁盘超过 80% 就告警,而不是等到 98% 才处理。
第二,定期清理无用资源。docker system prune 会清掉停止的容器、无用的网络和悬空镜像,但清之前确认没有要保留的已停容器,生产机上建议加 --volumes 之外的保守参数,别顺手把数据卷清了。
第三,管住日志源头。很多日志爆炸的根因是应用把 DEBUG 级别日志长期开着,或者某个接口在循环里打日志。日志轮转是兜底,真正治本是把不必要的日志级别调回 INFO,并给高频接口的日志加采样或节流。
这次事故从报警到恢复只用了十几分钟,但如果没有轮转兜底,下一次磁盘写满只是时间问题。容器日志这件事,记住三句话就够了:定位先看 containers 下的 json.log,止血用截断不用删除,根治靠 max-size 和 max-file 封顶。
- 点赞
- 收藏
- 关注作者
评论(0)