Docker 日志为什么能把磁盘写爆?讲透 json-file、local、journald、日志轮转与 docker logs

Docker 服务器运行一段时间以后,经常会出现一种非常奇怪的情况。
你执行:
bash
df -h
发现:
text
/dev/vda1
100G
Used 96G
磁盘马上就满了。
然后继续检查:
bash
docker system df
发现:
text
Images:
10GB
Volumes:
8GB
Build Cache:
4GB
加起来怎么看:
text
都没有 96GB。
继续执行:
bash
sudo du -h \
--max-depth=1 \
/var/lib/docker \
| sort -h
突然发现:
text
70GB
/var/lib/docker/containers
这时候大概率就要看看:
Docker Container 日志。
很多人第一次会非常疑惑:
我的程序又没有往
/app/logs写几十 GB,Docker 为什么自己占了这么多?
原因就在于:
只要你的程序不断向 stdout / stderr 输出,Docker 就可能在宿主机替你保存这些日志。
而 Docker 当前默认的 Logging Driver 仍然是:
text
json-file
更关键的是:
默认 json-file 不开启日志轮转。
Docker 官方当前甚至专门提醒,默认 json-file 日志如果不设置 Rotation,日志量大的 Container 可能持续占用磁盘,最终导致 Host Disk Exhaustion。官方现在建议,如果没有兼容性要求,可以考虑 local Logging Driver 来减少这种风险。
今天继续基于:
text
https://github.com/Rodert/DockerHub
讲透 Docker 日志里的这些概念:
text
stdout / stderr
docker logs
Logging Driver
json-file
local
journald
max-size
max-file
Log Rotation
daemon.json
Compose logging
Remote Logging
Dual Logging Cache
如果只想先记住一句话:
Docker 日志把磁盘写满,真正的问题通常不是"日志太多"这么简单,而是"你没有给日志设计生命周期"。
一、Docker 为什么能看到应用日志?
假设你的 Go 程序:
go
log.Println("server started")
Node:
javascript
console.log("server started");
Java:
java
System.out.println("server started");
这些信息最终都会写向:
text
stdout
或者:
text
stderr。
Docker Container 启动时:
text
Application
↓
stdout / stderr
↓
Docker
↓
Logging Driver
所以你才能执行:
bash
docker logs myapp
看到:
text
server started
request received
database connected
...
也就是说:
docker logs 看到的并不是 Docker 凭空生成的日志。
它主要来源于:
text
Container 主进程及其子进程写出的 stdout / stderr。
二、Logging Driver 是什么?
Docker 并不是只能把日志写成一个文件。
它提供了一层:
Logging Driver。
简单理解:
text
Container stdout/stderr
│
↓
Logging Driver
│
┌────┼─────────┐
↓ ↓ ↓
json-file local journald
...
不同 Driver 决定:
Container 输出的日志最终交给谁保存。
Docker 当前内置的 Logging Driver 包括:
text
json-file
local
journald
syslog
fluentd
gelf
awslogs
splunk
gcplogs
...
。
三、默认是什么?
执行:
bash
docker info \
--format '{{.LoggingDriver}}'
你大概率会看到:
text
json-file
或者直接:
bash
docker info
找:
text
Logging Driver:
json-file
Docker 官方当前仍然把:
text
json-file
作为 Docker Engine 的默认 Logging Driver。
四、json-file 到底写的是什么?
假设应用输出:
text
hello docker
Docker 的 json-file 并不是简单写:
text
hello docker
而是保存成类似:
json
{
"log": "hello docker\n",
"stream": "stdout",
"time": "2026-10-16T02:30:00.123456789Z"
}
里面至少记录:
text
日志正文
stdout / stderr
时间戳。
Docker 当前官方 json-file 文档就是这样定义其存储格式的。
五、怎么找到一个 Container 真正的日志文件?
不要:
text
自己去 /var/lib/docker
一个目录一个目录猜。
直接让 Docker 告诉你:
bash
docker inspect \
myapp \
--format '{{.LogPath}}'
在经典 Linux Rootful Docker + json-file 环境中,你经常会看到类似:
text
/var/lib/docker/containers/<container-id>/<container-id>-json.log
然后:
bash
sudo ls -lh \
"$(docker inspect -f '{{.LogPath}}' myapp)"
可能突然发现:
text
-rw-r----- 1 root root 38G ...-json.log
问题:
找到了。
六、为什么一个日志文件能长到几十 GB?
因为 json-file 当前默认:
text
max-size = -1
也就是:
不限制大小。
同时:
text
max-file
默认:
text
1
而且只有配置了:
text
max-size
以后,max-file 的 Rotation 才真正有意义。
换句话说:
如果你什么都没配置:
text
应用输出 1GB
↓
日志 1GB
输出 10GB
↓
日志 10GB
输出 100GB
↓
只要磁盘还有空间
继续写。
七、这就是 Docker 磁盘被写爆最常见的原因之一
比如你的 API:
每秒:
text
100 行日志。
每行:
text
500 Bytes。
一天大约:
text
100 × 500 × 86400
≈
4.3GB。
一个 Container:
text
一天 4GB。
十个 Container:
text
一天 40GB。
三天:
text
系统盘:
没了。
所以:
Debug Log 在生产开启,并不是只有"日志有点多"的问题。
它可能直接:
把整台 Docker Host 干掉。
八、磁盘满了以后会发生什么?
很多人以为:
text
最多 docker logs 写不了了。
实际上系统盘 100% 后:
text
MySQL
可能无法写数据
Docker
可能无法创建 Layer
Container
可能无法启动
应用
可能无法创建临时文件
系统日志
也可能无法写
SSH
某些操作可能异常。
所以:
Log Explosion 是基础设施故障。
不是单纯:
text
日志管理不优雅。
九、最简单的解决方案:日志轮转
例如 /etc/docker/daemon.json:
json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "20m",
"max-file": "5"
}
}
意思:
text
单个日志文件:
最多约 20MB
最多:
5 个日志文件
粗略理解:
text
每个 Container
大约最多保留 100MB 左右日志。
旧日志:
text
随着 Rotation
逐渐被淘汰。
Docker 官方当前支持 max-size 和 max-file 控制 json-file 自动轮转。
十、注意 daemon.json 里的值要写字符串
不要:
json
{
"log-opts": {
"max-file": 5
}
}
Docker 当前官方要求:
text
daemon.json 中 log-opts 的值
都应该使用字符串。
所以:
json
{
"log-opts": {
"max-size": "20m",
"max-file": "5"
}
}
。
这个小细节:
特别容易配错。
十一、改完以后怎么办?
Linux:
bash
sudo systemctl restart docker
然后:
bash
docker info \
--format '{{.LoggingDriver}}'
检查。
但是这里有一个非常关键的坑:
修改 Docker Daemon 默认日志配置,不会自动修改已经存在的 Container。
Docker 官方当前明确说明,新的 Default Logging Driver / Log Options:
text
只会作用于之后新创建的 Container。
已有 Container:
text
不会自动继承新配置。
。
十二、所以为什么我改了 daemon.json,老 Container 还是继续写 30GB?
因为它:
text
还是老 Container。
创建时:
text
Logging Config
已经确定了。
你只是:
bash
docker restart myapp
并没有:
text
Recreate Container。
这和前面讲环境变量一模一样:
restart 不是 recreate。
十三、Compose 环境要重新创建 Container
例如:
bash
docker compose up \
-d \
--force-recreate
让新的 Container:
text
重新读取 Docker Daemon 默认 Logging Config。
当然生产环境:
text
要按服务逐步重建。
不要:
text
为了改日志
一把把所有数据库都重启。
十四、也可以只给某个 Compose Service 配日志
例如:
yaml
services:
api:
image:
mycompany/api:1.8.3
logging:
driver:
json-file
options:
max-size:
"20m"
max-file:
"5"
Compose 当前 logging 支持:
text
driver
options
针对具体 Service 设置 Logging Driver。
这样:
不需要修改整台 Docker Host 的默认策略。
十五、什么情况下适合全局配置?
比如:
text
一台服务器
全部都是你自己的业务 Container。
你希望:
text
所有新 Container
默认都有日志上限。
直接:
text
/etc/docker/daemon.json
配置:
很舒服。
十六、什么时候适合 Compose 单独配置?
比如:
text
Nginx
日志很多
API
普通
Worker
特别多
MySQL
又是另一种。
你希望:
text
不同 Service
不同保留策略。
例如:
yaml
services:
nginx:
logging:
driver:
json-file
options:
max-size:
"50m"
max-file:
"5"
api:
logging:
driver:
json-file
options:
max-size:
"20m"
max-file:
"5"
worker:
logging:
driver:
json-file
options:
max-size:
"100m"
max-file:
"3"
这就可以:
按业务设计日志预算。
十七、我更推荐的另一个 Driver:local
Docker 官方当前直接建议:
如果主要考虑避免磁盘耗尽,可以考虑使用
localLogging Driver。
因为:
text
local
默认:
就带自动 Rotation。
当前默认配置大约是:
text
单文件:
20MB
文件数量:
5
总保留:
约 100MB / Container
Rotated Logs:
默认压缩
。
这和 json-file:
text
默认无限增长
形成非常明显的区别。
十八、全局改 local
可以:
json
{
"log-driver": "local"
}
然后:
bash
sudo systemctl restart docker
之后创建的新 Container:
text
默认使用 local。
Docker 当前官方说明,local 针对磁盘使用和性能做了优化,并默认开启 Rotation 和压缩。
十九、也可以进一步限制
json
{
"log-driver": "local",
"log-opts": {
"max-size": "20m",
"max-file": "5"
}
}
或者:
yaml
services:
api:
logging:
driver:
local
options:
max-size:
"20m"
max-file:
"5"
逻辑:
text
非常直接。
二十、local 和 json-file 最大区别是什么?
可以简单比较:
| 项目 | json-file | local |
|---|---|---|
| Docker 默认 | 是 | 否 |
| 默认 Rotation | 否 | 是 |
| 默认单文件限制 | 无限制 | 20MB |
| 默认文件数 | 1 | 5 |
| 默认压缩 Rotation | 否 | 是 |
docker logs |
支持 | 支持 |
| 适合磁盘保护 | 需要自己配置 | 开箱相对安全 |
Docker 当前保持 json-file 为默认值,主要有历史兼容以及 Kubernetes Runtime 等方面的兼容考虑;官方现在明确建议普通场景考虑 local 避免日志导致磁盘耗尽。
二十一、那是不是所有服务器都应该马上改 local?
也不能这么武断。
如果你现有:
text
日志采集系统
运维脚本
监控脚本
直接读取:
text
*-json.log
你突然改:
text
local
这些东西:
text
可能全部失效。
尤其某些老环境:
text
Fluent Bit / Filebeat
可能直接 tail Docker JSON File。
所以:
Logging Driver 也是基础设施接口。
不能只因为:
text
local 看起来更先进
就直接全量修改生产。
二十二、更重要的是:官方不建议你直接操作日志底层文件
无论:
text
json-file
还是:
text
local
Docker 官方都明确警告:
这些日志文件由 Docker Daemon 自己管理,不应该使用外部工具直接操作。
直接编辑、删除、替换这些文件:
text
可能干扰 Docker Logging System
并导致不可预期行为。
。
二十三、所以磁盘满了不要第一反应这样
bash
rm -f \
/var/lib/docker/containers/*/*-json.log
虽然:
text
磁盘可能马上回来了。
但这种方式:
绕过了 Docker 对 Log File 的管理。
和我们之前说:
text
不要 rm overlay2
本质思维是一样的。
二十四、那已经 50GB 的日志怎么办?
第一步先确认是谁。
bash
docker inspect \
--format '{{.LogPath}}' \
myapp
再:
bash
sudo ls -lh \
"$(docker inspect -f '{{.LogPath}}' myapp)"
或者批量:
bash
docker ps -aq \
| while read id
do
name=$(docker inspect \
-f '{{.Name}}' "$id")
log=$(docker inspect \
-f '{{.LogPath}}' "$id")
if [ -f "$log" ]; then
size=$(du -h "$log" \
| cut -f1)
echo \
"$size $name $log"
fi
done
先:
找 Top Logger。
二十五、不要只解决文件,要解决"谁一直在疯狂打印"
比如发现:
text
api:
38GB
继续:
bash
docker logs \
--tail 100 \
api
一看:
text
DEBUG sql...
DEBUG sql...
DEBUG sql...
DEBUG sql...
原来:
text
Production
居然开着:
text
DEBUG SQL。
真正根因:
应用日志级别配置错误。
如果你只是:
text
每天清日志
明天:
text
继续 38GB。
二十六、日志爆炸通常有几个根因
最常见:
text
DEBUG / TRACE
在生产开启
还有:
text
循环报错。
比如:
text
Redis connection failed
Retry...
Redis connection failed
Retry...
Redis connection failed
Retry...
一秒:
text
打印几百行。
这和前面讲:
text
Retry Storm
是一样的。
程序:
text
没有 Backoff
不仅:
text
请求打爆依赖
还:
顺便把日志盘写爆。
二十七、还有一种特别恐怖:打印大对象
比如:
javascript
console.log(JSON.stringify(requestBody));
用户上传:
text
50MB JSON。
一条日志:
text
50MB。
如果:
text
请求 / 响应 Body
全打印,
日志量:
可以比业务流量还夸张。
所以生产日志:
text
不应该无脑 dump 整个请求。
二十八、密码和 Token 也不能进日志
例如:
text
Authorization:
Bearer xxx
password=123456
cookie=...
如果你的应用:
text
Debug 打印整个 Header
那么 Docker Log:
就变成 Secret Database。
所以 Logging Design 不只有:
text
磁盘大小。
还有:
信息安全。
二十九、docker logs 有哪些特别实用的参数?
不要:
bash
docker logs myapp
面对一个运行半年 Container:
text
从第一行全部打印。
可以:
bash
docker logs \
--tail 200 \
myapp
只看最后:
text
200 行。
三十、实时追踪
bash
docker logs \
-f \
myapp
相当于:
text
follow。
适合:
text
刚刚发布
盯启动日志。
三十一、加时间
bash
docker logs \
-t \
myapp
可以显示:
text
Docker Log Timestamp。
特别是应用自己的日志:
text
没有时间戳
时:
text
很有帮助。
三十二、只看最近一段时间
比如:
bash
docker logs \
--since 10m \
myapp
看:
text
最近 10 分钟。
或者:
bash
docker logs \
--since \
2026-10-16T09:00:00 \
myapp
线上事故:
特别实用。
三十三、Compose 也一样
bash
docker compose logs \
-f \
--tail 100 \
api
可以只看:
text
api Service。
多个 Service:
bash
docker compose logs \
-f \
api \
worker
比:
text
一个个查 Container Name
方便。
三十四、为什么 docker logs 有时候看不到任何内容?
第一种可能:
text
程序根本没写 stdout/stderr。
例如应用:
text
所有日志写 /app/logs/app.log。
那么:
bash
docker logs
当然:
text
几乎为空。
因为:
Docker Logging Driver 看不到你自己写的业务文件。
三十五、这就是为什么 Container 应用通常推荐 stdout/stderr
传统服务器:
text
/var/log/myapp/app.log
很合理。
Container:
text
App
↓
stdout/stderr
↓
Container Runtime
↓
Logging System
往往:
更符合容器模型。
这样:
text
应用
不用负责:
text
日志文件路径
Rotation
磁盘保留
跨服务器收集。
这些:
text
交给平台。
三十六、不要让日志轮转做两遍
比如 Java:
text
Logback
自己:
text
每天切文件
保留 30 天
然后:
text
Docker
又:
text
对 stdout 做 Rotation。
如果你的应用:
text
同时写文件 + stdout
甚至:
日志保存两份。
一份:
text
/app/logs
一份:
text
Docker Logging Driver。
磁盘:
text
直接翻倍。
所以生产日志架构:
一定要明确谁是日志生命周期负责人。
三十七、一个简单 Container 日志原则
我比较喜欢:
text
Application:
只负责输出结构化日志到 stdout/stderr
Docker:
负责短期本地 Buffer / Rotation
Loki / ELK / Cloud Logging:
负责长期查询、存储和分析。
这样:
text
责任非常清楚。
三十八、那 local Driver 能代替 Loki 吗?
不能。
local:
text
解决的是单机 Container 日志存储。
比如:
text
保留最近 100MB。
它不是:
text
集中日志查询平台。
如果你有:
text
20 台服务器
100 个 Container
想查:
用户 12345 昨天 14:32 在哪台机器报错?
你不可能:
text
SSH 20 台服务器
然后 docker logs。
这时候:
需要 Centralized Logging。
三十九、比如 Loki 架构
text
Container
↓
stdout/stderr
↓
Collector
↓
Loki
↓
Grafana
或者:
text
Container
↓
Fluent Bit
↓
Elasticsearch / OpenSearch
↓
Kibana
或者云平台:
text
Container
↓
Cloud Logging
Docker Logging Driver:
只是日志链的第一环。
四十、journald 是什么?
如果你的 Linux Host 本身使用:
text
systemd
通常就有:
text
systemd-journald。
Docker 可以:
json
{
"log-driver": "journald"
}
把 Container Log:
text
直接写到 journald。
Docker 当前内置支持 journald Logging Driver。
四十一、使用 journald 后怎么查?
Host:
bash
journalctl
例如可以围绕:
text
Container Name
Container ID
Tag
筛选。
这样:
text
Docker 日志
和:
text
Host System Journal
进入统一体系。
如果你的公司:
text
本来就用 journald
+
Promtail / Fluent Bit
那么:
很自然。
四十二、journald 有什么优势?
最大的一个优势:
生命周期统一交给系统日志体系。
你不用:
text
单独维护 json-file。
可以使用:
text
journald Storage Policy
Systemd Journal Rotation
Central Collector。
对于:
text
标准 Linux 运维体系
很舒服。
四十三、但 journald 不是绝对更好
如果你现有工具:
text
只会 tail Docker JSON Log。
切换:
text
journald
意味着:
text
采集链也要跟着改。
所以 Logging Driver 选择:
要看整套 Logging Pipeline。
不是:
text
哪一个名字更高级。
四十四、Remote Logging Driver 又是什么?
比如:
text
awslogs
fluentd
gelf
splunk
可以让 Docker:
text
直接把日志交给远端服务。
例如:
yaml
services:
api:
logging:
driver:
awslogs
options:
awslogs-region:
us-east-1
Docker 当前 Compose 支持通过 logging.driver/options 使用这些 Driver。
四十五、用了 Remote Driver 以后 docker logs 还能不能看?
这是一个很有意思的问题。
Docker 当前提供:
Dual Logging Cache。
对于部分不支持本地读取的远程 Logging Driver:
text
Docker 默认可以维护一份本地缓存,
让 docker logs 仍然可用。
如果不需要:
text
本地 docker logs 调试。
也可以通过:
text
cache-disabled
关闭这份缓存,减少 Host Storage Usage。
四十六、但 local/json-file/journald 不走 Dual Logging
因为它们本身:
text
就支持读取日志。
Docker 当前官方明确说明,对于:
text
local
json-file
journald
这类 Driver:
text
不使用 Dual Logging Cache。
。
所以:
不要把"docker logs 能看"理解成"肯定还有额外一份 json-file"。
具体取决于 Driver。
四十七、怎么查看某个 Container 用什么 Driver?
例如:
bash
docker inspect \
myapp \
--format '{{.HostConfig.LogConfig.Type}}'
可能:
text
json-file
或者:
text
local
再看配置:
bash
docker inspect \
myapp \
--format '{{json .HostConfig.LogConfig}}'
比如:
json
{
"Type": "json-file",
"Config": {
"max-size": "20m",
"max-file": "5"
}
}
这比:
text
"我记得 daemon.json 配过"
更靠谱。
因为:
Container 实际配置才是真相。
四十八、可以写一个简单 Log Doctor
例如:
bash
#!/usr/bin/env bash
NAME="$1"
docker inspect \
"$NAME" \
--format '
Container: {{.Name}}
Driver:
{{.HostConfig.LogConfig.Type}}
Options:
{{json .HostConfig.LogConfig.Config}}
LogPath:
{{.LogPath}}
'
如果输出:
text
Driver:
json-file
Options:
{}
LogPath:
/var/lib/docker/containers/...-json.log
工具可以直接判断:
text
⚠️ json-file without rotation
这就是:
一个非常实用的风险检测。
四十九、Rodert/DockerHub 很适合增加 Log Doctor
例如:
text
docker-doctor logs
输出:
text
Docker Default Driver:
json-file
Default Rotation:
NOT CONFIGURED 🔴
Top Container Logs:
api
38.2GB 🔴
worker
7.4GB 🟠
nginx
810MB 🟡
mysql
120MB 🟢
然后提示:
text
Host is at risk of
disk exhaustion.
五十、还能检测"改了默认配置,但 Container 还是旧策略"
比如:
Docker Daemon:
text
Default:
local
Container:
text
api:
json-file
no max-size
输出:
text
🟠 Stale Logging Configuration
Docker daemon default:
local
Running container:
json-file
The container was created
before the daemon logging
configuration changed.
Recreate the container
to apply the new default.
这个问题:
非常高频。
Docker 官方当前明确说明,Daemon Logging 配置只作用于新创建的 Container。
五十一、还可以检查日志增长速度
比如:
text
09:00
1.2GB
10:00
2.1GB
11:00
3.1GB
工具计算:
text
Growth:
~1GB/hour
如果磁盘剩:
text
12GB
就可以提示:
text
Estimated disk exhaustion:
~12 hours
这种:
比等 df 100% 再报警有用得多。
五十二、真正应该监控的是增长率
磁盘:
text
70%
不一定危险。
如果:
text
一年从 69% 长到 70%
问题不大。
但是:
text
40%
↓
70%
只用了 2 小时
极其危险。
日志问题:
特别适合监控 Rate。
例如:
text
bytes/hour
而不是只:
text
当前 GB。
五十三、Log Rotation 并不等于 Log Retention
这是另一个值得区分的概念。
Rotation:
text
控制本地文件怎么切。
比如:
text
20MB × 5。
Retention:
text
业务需要保存多久?
可能要求:
text
7 天
30 天
180 天。
Docker Local Log:
text
更适合短期调试。
真正长期 Retention:
应该交给日志平台。
不要:
text
为了保存半年日志
把 max-file 调成 10000。
让 Docker Host:
变成你的日志仓库。
五十四、为什么本地只保留一小段反而合理?
因为:
text
Container Host
最重要任务是:
跑业务。
不是:
text
永久归档日志。
如果远程日志系统:
text
Loki
保存:
text
30 天。
Host:
text
只留最近 100MB / 几小时
用来:
text
远程平台挂了时临时排错。
已经足够。
五十五、日志级别也应该动态治理
开发:
text
DEBUG
可能合理。
生产:
text
INFO
WARN
ERROR
更常见。
真正排故障:
text
临时提高某一个模块 DEBUG
然后:
text
过一段时间恢复。
而不是:
text
为了方便以后排查
生产永远 TRACE。
这和:
text
Container 永远装 vim/curl
一个思路。
调试能力不能无限侵占生产成本。
五十六、结构化日志比纯文本更适合集中分析
例如:
text
user 123 request failed
可以升级成:
json
{
"level": "error",
"user_id": 123,
"request_id": "abc",
"message": "request failed"
}
以后:
text
Loki
Elasticsearch
OpenSearch
能根据:
text
user_id
request_id
level
搜索。
而不是:
text
grep 一大段文本。
五十七、但是 JSON 日志不要 JSON 套 JSON 再套 Docker JSON
应用本身输出:
json
{"level":"info","msg":"hello"}
json-file Driver 又包成:
json
{
"log":
"{\"level\":\"info\",\"msg\":\"hello\"}\n",
"stream":
"stdout",
"time":
"..."
}
这是:
text
Docker Storage Format。
日志采集器:
text
通常可以解析。
但设计日志 Pipeline 时:
要明确哪一层负责解包。
不然最终 Kibana 里:
text
log
只是:
text
一整个转义后的 JSON String。
五十八、Container Log Timestamp 和应用 Timestamp 也可能重复
应用:
text
2026-10-16T02:30:00Z INFO hello
Docker:
text
又保存一个:
2026-10-16T02:30:00.123Z
现在:
text
一条日志
两个时间。
这不一定错误。
应用时间:
text
代表业务真正产生日志的时刻。
Docker 时间:
text
代表 Runtime 接收到 stdout 的时刻。
通常差:
text
很小。
真正日志平台:
可以选择一个作为主时间字段。
五十九、这和上一篇"容器时间差 8 小时"又连起来了
如果应用:
text
Asia/Shanghai
Docker Log Timestamp:
text
UTC。
你可能看到:
text
应用日志:
10:30:00
Docker timestamp:
02:30:00Z
乍一看:
text
差 8 小时。
其实:
是同一时刻。
所以结构化日志:
text
最好带时区 / Offset。
不要只输出:
text
2026-10-16 10:30:00
六十、日志和业务数据应该分开看待
例如 MySQL:
text
数据库文件
属于:
Critical Data。
日志:
text
通常是 Observability Data。
两者:
text
Retention
Backup
Storage
策略完全不同。
不要:
text
MySQL 数据和应用日志
都放一个 50GB 系统盘
然后:
text
日志把盘写满
↓
MySQL 也写不进去。
这是:
典型的 Failure Domain 设计问题。
六十一、生产 Host 最少应该做磁盘报警
例如:
text
Disk Usage > 80%
报警。
text
> 90%
高优先级。
同时:
text
Docker Log Growth Rate
异常:
text
也报警。
如果等到:
text
100%
才知道:
已经晚了。
六十二、一个推荐的单机 Compose 日志配置
比如:
yaml
services:
api:
image:
mycompany/api:1.8.3
restart:
unless-stopped
logging:
driver:
local
options:
max-size:
"20m"
max-file:
"5"
worker:
image:
mycompany/worker:1.8.3
restart:
unless-stopped
logging:
driver:
local
options:
max-size:
"50m"
max-file:
"5"
这意味着:
text
API:
约 100MB 级别本地日志预算
Worker:
约 250MB 级别本地日志预算
并且:
text
local
默认压缩 rotated logs。
。
六十三、如果必须兼容 json-file
就明确设置 Rotation:
yaml
services:
api:
logging:
driver:
json-file
options:
max-size:
"20m"
max-file:
"5"
compress:
"true"
当前 json-file 支持:
text
max-size
max-file
compress
其中:
text
compress
默认是:
text
false。
。
六十四、max-file 单独写为什么没效果?
例如:
json
{
"log-opts": {
"max-file": "5"
}
}
你以为:
text
最多 5 个文件。
但 json-file:
text
如果没有 max-size
根本:
text
没有触发 Rotation 的边界。
Docker 当前官方明确说明,max-file 只有配合:
text
max-size
使用才有效。
这也是一个特别容易忽略的坑。
六十五、所以最小有效组合是
json
{
"log-opts": {
"max-size": "20m",
"max-file": "5"
}
}
不是:
text
只配 max-file。
六十六、Docker Desktop 怎么配?
Docker Desktop:
text
Settings
↓
Docker Engine
编辑 Daemon JSON。
Docker 当前官方文档对于 Docker Desktop 的 Logging Driver 配置也是通过 Docker Engine Settings 修改 Daemon 配置。
当然:
text
Mac / Windows Desktop
底层存储位置:
和 Linux /var/lib/docker 不一样。
所以不要照抄:
text
Linux Host 日志文件路径
去 Desktop 找。
六十七、什么时候不要使用 docker logs 作为唯一日志系统?
如果:
text
只有一个个人项目
当然:
text
docker logs -f
非常舒服。
但一旦:
text
多 Host
多 Container
需要搜索
需要告警
需要审计
需要长期保存
docker logs:
就只是 Debug Interface。
而不是:
完整 Logging Platform。
六十八、一个成熟日志链路应该像这样
text
Application
│
↓
stdout / stderr
│
↓
Docker Runtime
│
├── Local Rotation
│
└── Log Collector
│
↓
Central Storage
│
↓
Search / Dashboard
│
↓
Alert
每一层:
都有自己的职责。
六十九、不要让应用和 Docker 都无限保存
错误架构:
text
Application:
30 天文件日志
Docker:
json-file 无限
Filebeat:
再缓存一份
Loki:
再保存 30 天
结果:
text
同一条日志
可能本地存两三份。
真正成熟:
明确唯一长期存储层。
七十、Rodert/DockerHub 可以增加 Log Doctor
比如:
text
Docker Log Doctor
Default Driver:
json-file
Default max-size:
UNLIMITED 🔴
Containers without rotation:
8
Largest logs:
api
38.2 GB 🔴
worker
7.8 GB 🟠
nginx
950 MB
然后自动建议:
text
Option A:
configure json-file rotation
Option B:
use local logging driver
Option C:
ship logs to centralized storage
这就非常实用。
七十一、还能做 Compose Analyzer
输入:
yaml
services:
api:
image:
myapi
检测 Host:
text
Default Logging Driver:
json-file
Rotation:
none
提示:
text
🟠 Unbounded Container Logging
The service inherits Docker's
json-file logging driver without
max-size/max-file.
High-output containers may
exhaust host disk space.
七十二、如果 Compose 已经配置
yaml
logging:
driver:
json-file
options:
max-file:
"5"
提示:
text
⚠️ Incomplete Rotation
json-file max-file only takes
effect when max-size is configured.
Consider adding:
max-size: "20m"
这个检查:
非常容易救生产事故。
七十三、如果已经使用 local
输出:
text
🟢 Local Logging Driver
Rotation:
enabled
Compression:
enabled
Configured:
20m × 5
至少:
text
磁盘无限增长风险
低很多。
当然还是需要:
text
Host Disk Monitoring。
因为:
text
100 个 Container × 100MB
=
10GB。
规模:
仍然会累加。
七十四、日志预算应该按 Host 算
假设服务器:
text
50GB 系统盘。
Docker:
text
20 个 Container。
如果每个:
text
500MB 日志上限。
理论:
text
10GB。
再加:
text
Images:
15GB
Volumes:
10GB
Build Cache:
5GB
系统:
text
已经 40GB。
所以:
单 Container Log Limit 不能脱离整机容量设计。
七十五、日志文件越多也会占 inode
即使:
text
每个文件很小。
如果应用:
text
自己每秒创建一个 log file
最终:
text
几百万文件。
前面我们讲 overlay2:
text
df -h
可能还有空间,
但:
bash
df -i
已经:
text
100%。
所以日志治理:
不只是字节大小,也要看文件数量。
七十六、最好的生产日志不是"尽量少打印"
也不是:
text
什么都别记。
真正目标:
高信噪比。
例如异常:
应该带:
text
request_id
user_id
error code
dependency
latency
stack trace
而不是:
text
每一个 for 循环打印一行。
日志:
应该为故障定位服务,而不是记录程序的每一次呼吸。
七十七、一个推荐的排错顺序
发现:
text
Docker Disk Full
第一:
bash
df -h
df -i
确认:
text
空间还是 inode。
第二:
bash
docker system df -v
看:
text
Image / Container / Volume / Cache。
第三:
bash
docker inspect \
--format '{{.LogPath}}' \
<container>
看:
text
Container Log。
第四:
bash
docker inspect \
--format '{{json .HostConfig.LogConfig}}' \
<container>
确认:
text
Driver + Rotation。
第五:
bash
docker logs \
--tail 200 \
<container>
看:
为什么它打印这么多。
这样:
text
从结果
一路查到根因。
七十八、而不是直接执行
bash
rm -rf \
/var/lib/docker/containers/*
或者:
bash
truncate ...
Docker 官方现在对 json-file 和 local 都明确警告,这些存储由 Docker Daemon 自己管理,外部直接操作可能干扰 Logging System。
所以:
磁盘管理仍然应该优先走 Docker 支持的配置和生命周期。
七十九、最终一个简单可靠的单机方案
如果是:
text
个人服务器
小团队
Docker Compose
而且没有:
text
复杂日志采集兼容要求。
我会优先考虑:
json
{
"log-driver": "local",
"log-opts": {
"max-size": "20m",
"max-file": "5"
}
}
然后:
text
应用:
stdout/stderr
Docker:
短期 Rotation
Grafana Loki / 其他平台:
有需要再接集中日志。
Docker 当前官方本身也把 local 作为防止 Host 磁盘被日志耗尽的推荐方向。
八十、如果不能改 Driver
那至少:
json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "20m",
"max-file": "5",
"compress": "true"
}
}
让:
text
无限增长
变成:
有边界增长。
这一步:
text
对一台长期运行 Docker 的服务器
非常值得做。
总结
Docker 日志真正的链路是:
text
Application
↓
stdout / stderr
↓
Docker Logging Driver
↓
Local / Remote Storage
当前 Docker Engine 默认 Logging Driver 仍然是:
text
json-file。
最大的问题是:
默认 json-file 不做日志轮转,max-size 默认相当于无限制。
所以一个高输出 Container:
text
完全可以把几十 GB
甚至几百 GB 系统盘
慢慢写满。
Docker 官方当前也明确警告了这一风险,并建议普通场景考虑:
text
local
Logging Driver,因为它默认自带 Rotation 和压缩。
如果继续使用:
text
json-file
最少建议明确配置:
json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "20m",
"max-file": "5"
}
}
而且必须记住:
改 daemon.json 以后,旧 Container 不会自动获得新 Logging 配置。
需要:
text
Recreate Container。
Docker 官方当前对此有明确说明。
排查时,不要第一时间进入:
text
/var/lib/docker
手工删文件。
应该先:
bash
docker info \
--format '{{.LoggingDriver}}'
docker inspect \
<container> \
--format '{{json .HostConfig.LogConfig}}'
docker inspect \
<container> \
--format '{{.LogPath}}'
搞清楚:
text
谁在记录日志
用什么 Driver
有没有 Rotation
到底哪个 Container 写得最多。
然后再回到应用:
text
为什么生产还在 DEBUG?
是不是错误循环?
有没有 Retry Storm?
有没有打印超大 Request Body?
有没有把 Secret 打进日志?
因为:
日志轮转负责防止服务器被写爆,而真正减少无效日志,仍然要回到应用本身。
如果只记住一句话:
Docker 日志不是"打印完就消失",stdout/stderr 最终会被 Logging Driver 接管;如果默认 json-file 没有限制,日志就是一个没有上限的磁盘消费者,所以任何长期运行的 Docker Host 都应该明确设计日志的 Rotation、Retention 和集中存储策略。
项目地址:
text
https://github.com/Rodert/DockerHub
这个项目下一步很适合继续增加:
text
Docker Log Doctor
Top Logger Ranking
json-file Unlimited Risk Detection
local Driver Advisor
Log Growth Rate Monitor
Compose Logging Analyzer
Disk Exhaustion Forecast
这样用户遇到:
"Docker 明明没有多少镜像,为什么系统盘还是被吃满了?"
就可以快速判断:
是不是某个 Container 正在悄悄写一个几十 GB 的日志文件。