适用场景
这类问题常见于单机 Docker、docker-compose 或没有接入集中日志系统的测试/生产节点。应用本身还在运行,但主机磁盘空间持续下降,最终出现接口异常、容器重启失败、镜像拉取失败、数据库写入失败等问题。
典型环境特征:
- Docker 使用默认
json-file日志驱动。 - 容器持续输出访问日志、调试日志、异常堆栈或健康检查日志。
/var/lib/docker单独挂载或直接占用根分区。- 没有配置 Docker 日志轮转。
现象描述
线上节点告警显示根分区或 Docker 数据分区空间不足:
df -h
示例输出:
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 50G 48G 2.0G 97% /
应用侧可能同时出现这些现象:
- 容器无法启动,提示
no space left on device。 docker pull、docker build失败。- 应用写临时文件、写 SQLite、写本地缓存失败。
- 日志平台没有明显异常,但机器磁盘增长很快。
可能原因
Docker 默认日志驱动通常是 json-file。如果不设置大小限制,容器标准输出和标准错误会持续写入宿主机文件:
/var/lib/docker/containers/<container-id>/<container-id>-json.log
常见诱因包括:
- 应用把 debug 日志直接打到 stdout。
- 访问日志按请求逐条打印,高峰期日志量暴涨。
- 异常重试循环不断打印堆栈。
- 健康检查、定时任务或 sidecar 输出过多。
- Docker daemon 没有配置
max-size和max-file。
排查思路
1. 先确认磁盘是否真的打满
df -h
df -ih
df -h 看磁盘容量,df -ih 看 inode。如果容量没满但 inode 满了,说明小文件数量过多;如果容量接近 100%,再继续定位大文件。
2. 找到 Docker 目录占用
sudo du -xh --max-depth=1 /var/lib/docker | sort -h
关键点:
-x表示不跨文件系统,避免扫到其他挂载点。--max-depth=1先看一级目录,不要一上来全盘扫描。- 如果
containers很大,优先怀疑容器日志。 - 如果
overlay2很大,可能是镜像、容器层或应用写入容器文件系统。
3. 定位最大的容器日志文件
sudo find /var/lib/docker/containers -name '*-json.log' -type f -printf '%s %p\n' \
| sort -nr \
| head -20 \
| awk '{printf "%.2fG %s\n", $1/1024/1024/1024, $2}'
如果系统的 find 不支持 -printf,可以使用:
sudo du -h /var/lib/docker/containers/*/*-json.log 2>/dev/null | sort -h | tail -20
看到某个 *-json.log 达到几 GB 或几十 GB,基本就能确认日志膨胀问题。
4. 根据日志路径反查容器
日志路径里包含完整容器 ID,例如:
/var/lib/docker/containers/9f8a1c.../9f8a1c...-json.log
取前 12 位反查容器:
docker ps -a --no-trunc | grep 9f8a1c
也可以用 inspect 直接确认日志文件:
docker inspect --format='{{.Name}} {{.LogPath}}' $(docker ps -aq)
如果容器很多,可以按日志大小输出容器名:
for id in $(docker ps -aq); do
log=$(docker inspect --format='{{.LogPath}}' "$id")
name=$(docker inspect --format='{{.Name}}' "$id" | sed 's#^/##')
[ -f "$log" ] && printf '%s %s %s\n' "$(du -h "$log" | awk '{print $1}')" "$id" "$name"
done | sort -h
定位示例
某台主机根分区使用率 97%。排查发现 Docker 的 containers 目录占用最大:
sudo du -xh --max-depth=1 /var/lib/docker | sort -h
输出节选:
1.8G /var/lib/docker/overlay2
39G /var/lib/docker/containers
42G /var/lib/docker
继续查看日志文件:
sudo du -h /var/lib/docker/containers/*/*-json.log 2>/dev/null | sort -h | tail -5
输出节选:
7.4G /var/lib/docker/containers/2b6.../2b6...-json.log
31G /var/lib/docker/containers/9f8.../9f8...-json.log
反查容器:
docker ps -a --no-trunc | grep 9f8
确认是 api-service 容器,查看最近日志:
docker logs --tail=100 api-service
发现应用在不断打印同一个数据库连接异常堆栈,每秒几十次,导致日志文件快速膨胀。
临时止血方案
1. 释放单个容器日志文件
不要直接 rm 正在被 Docker 打开的日志文件。删除文件后,进程仍可能持有文件句柄,空间不一定立即释放。推荐截断:
sudo truncate -s 0 /var/lib/docker/containers/<container-id>/<container-id>-json.log
截断后确认空间:
df -h
sudo du -sh /var/lib/docker/containers
2. 先降低应用日志量
如果日志持续高速写入,单纯截断只能短暂缓解。需要先处理源头:
docker logs --tail=200 <container-name>
docker restart <container-name>
重启只能解决部分临时异常。如果是配置错误、依赖不可用或异常循环,需要修复应用配置或依赖服务。
长期治理方案
1. 配置 Docker daemon 日志轮转
编辑 /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "5"
}
}
含义:
max-size: 单个日志文件最大 100 MB。max-file: 最多保留 5 个日志文件。- 单个容器最多保留约 500 MB 本地日志。
重启 Docker:
sudo systemctl restart docker
注意:重启 Docker 会影响容器,生产环境要先确认业务容器是否由 systemd、compose、swarm 或其他编排系统托管。
2. 让配置对已有容器生效
Docker daemon 的日志配置通常只对新创建的容器生效。已有容器可能需要重建:
docker compose up -d --force-recreate
或者单容器重新创建:
docker stop api-service
docker rm api-service
docker run -d --name api-service <原有参数> <image>
重建前必须确认启动参数、挂载卷、环境变量、端口映射都已记录,避免误删后无法恢复。
3. docker-compose 单服务配置日志限制
如果不能修改全局 daemon,可以在 docker-compose.yml 中为服务配置:
services:
api:
image: example/api:1.0
logging:
driver: json-file
options:
max-size: "100m"
max-file: "5"
更新后重建服务:
docker compose up -d --force-recreate api
4. 应用层减少无效日志
建议同时从应用层治理:
- 生产环境默认使用
INFO,不要长期打开DEBUG。 - 对重复异常增加限频或采样。
- 健康检查不要每次都输出完整请求日志。
- 大对象、请求体、响应体不要直接打印到 stdout。
- 关键错误保留上下文,但避免无限重试刷屏。
预防措施
1. 增加磁盘和日志文件告警
至少监控这些指标:
df -h /
sudo du -sh /var/lib/docker/containers
sudo du -sh /var/lib/docker/overlay2
在 Prometheus node_exporter 中,可以关注:
node_filesystem_avail_bytes: 文件系统可用空间。node_filesystem_files_free: inode 可用数量。node_filesystem_size_bytes: 文件系统总容量。
告警规则可以设置为根分区使用率超过 85% 预警、超过 95% 严重告警。
2. 发布检查项中加入日志轮转
新机器初始化时应检查:
docker info --format '{{.LoggingDriver}}'
cat /etc/docker/daemon.json
如果没有 max-size 和 max-file,就不应该直接承载长期运行服务。
3. 区分日志膨胀和容器层膨胀
如果大头在 containers,优先查 *-json.log。如果大头在 overlay2,要进一步确认是否有程序把上传文件、缓存、临时文件写到了容器内部文件系统。容器内可用下面的命令快速查看:
docker exec -it <container-name> sh -c "du -xh --max-depth=1 / | sort -h"
应用数据应写入明确的数据卷,而不是写在容器可写层里。
总结
Docker 容器日志打满磁盘的核心排查路径是:先用 df -h 确认磁盘压力,再用 du 判断 /var/lib/docker 的占用分布,最后定位 containers 目录下最大的 *-json.log 并反查容器。临时处理优先使用 truncate 截断日志,长期治理必须配置 max-size 和 max-file,并从应用侧减少重复异常日志。对生产环境来说,Docker 日志轮转应该是机器初始化和服务上线前的基础检查项。
Discussion
评论