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

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 官方当前直接建议:

如果主要考虑避免磁盘耗尽,可以考虑使用 local Logging 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 的日志文件。

相关推荐
人生匆匆2 小时前
docker部署ceph集群
ceph·docker·容器
张张123y5 小时前
后端第九课:部署上线(把你的 FastAPI 后端真正跑起来给别人用)
python·docker·部署·fastapi
AscendKing9 小时前
Docker Compose 一键部署 SkyWalking 10.2.0 + Elasticsearch8.11,分布式链路追踪完整实战
elasticsearch·docker·skywalking·链路追踪·微服务监控
励志师code9 小时前
Kubernetes 集群节点生命周期管理实战:从扩容、调度管控到证书续期
运维·docker·容器·kubernetes·gpu算力·kubelet
voipmaker10 小时前
选煤厂智能化:UWB 人员定位系统 WebAPI 对接融合通信平台实战
物联网·实时互动·json·音视频·实时音视频·web·视频
木雷坞10 小时前
Docker Compose部署Metabase:应用库隔离与日期筛选看板
运维·docker·容器
怒放的生命199110 小时前
前端项目 Docker 打包实操指南
前端·docker·容器
tedcloud12310 小时前
Docker Compose 部署 Immich:手机照片自动备份与常见故障排查
运维·前端·docker·容器·智能手机·开源·流程图
古城小栈10 小时前
Docker Compose 云原生的底座
docker·云原生·容器