3、Docker默认日志驱动:json-file驱动的原理、日志文件存储位置、日志轮转与大小限制配置

Docker 默认的日志驱动是 json-file,它负责将容器的标准输出(stdout)和标准错误(stderr)捕获并写入宿主机上的 JSON 格式文件。理解 json-file 驱动的工作机制、文件布局以及轮转策略,是构建可靠日志采集链路的基础。在生产环境中,若不对日志文件进行限制,磁盘将被迅速写满,导致容器或宿主机异常。本章将深入剖析 json-file 驱动的核心原理、存储路径、轮转配置,并结合实战案例给出最佳实践。

容器进程 stdout / stderr (应用日志输出) Docker Engine json-file 驱动 (日志采集与写入) 日志文件 /var/lib/docker/containers /<container-id>/ <container-id>-json.log (JSON 结构化行) 日志轮转配置 max-size: 10m max-file: 3 compress: true 日志采集器 Filebeat Fluentd Promtail daemon.json 配置 {"log-driver": "json-file", "log-opts": {...}} 风险提示 无轮转配置时 日志文件无限增长 磁盘耗尽风险

3.1 json-file 驱动的核心原理

json-file 驱动是 Docker 内置的默认日志驱动,其工作流程可以概括为:容器内进程的 stdout/stderr 输出 → Docker Engine 捕获 → 格式化为 JSON 行 → 写入宿主机文件。当容器启动时,Docker 守护进程会为每个容器创建一个独立的日志文件,并将容器内 PID 1 进程的所有标准输出和标准错误输出重定向到该文件。

每条日志记录在文件中以 JSON 对象的形式存储,包含三个关键字段:

  • log:实际的日志内容(字符串类型,可能包含转义字符)。
  • stream:标识日志来源,值为 stdout 或 stderr。
  • time:日志产生的时间戳,采用 RFC3339Nano 格式,精确到纳秒。

以下是一个典型的 json-file 日志行示例:

{"log":"2025-01-15 10:30:45.123 INFO  OrderService - 订单创建成功,订单号: 20250115103045\n","stream":"stdout","time":"2025-01-15T10:30:45.123456789Z"}

这种结构化存储方式为日志采集工具(如 Filebeat、Fluentd、Promtail)提供了天然的解析基础。采集器只需按行读取文件,并对每行执行 JSON 反序列化即可提取出原始日志内容、输出流类型和时间戳。

关键理解:json-file 驱动本身不提供日志的索引、搜索或聚合能力,它只负责“采集-落盘”。日志的检索和分析需要依赖外部系统(如 ELK、Loki)完成。因此,json-file 驱动适合作为日志链路中的“第一跳”存储。

3.2 日志文件存储位置与目录结构

json-file 驱动的日志文件默认存储在 Docker 数据根目录下的 containers 子目录中。Docker 数据根目录默认为 /var/lib/docker,但可以通过修改 Docker 守护进程配置(data-root 参数)进行更改。完整的日志文件路径为:

/var/lib/docker/containers/<container-id>/<container-id>-json.log

其中 <container-id> 是容器的完整 64 位 ID(非短 ID)。每个容器在创建时都会生成一个以完整 ID 命名的目录,该目录下除了日志文件外,还可能包含其他运行时文件(如 hostconfig.json、config.v2.json 等)。

要快速定位某个容器的日志文件路径,可以使用以下命令:

# 获取容器完整 ID
docker inspect --format='{{.Id}}' <container-name>

# 直接获取日志文件路径
docker inspect --format='{{.LogPath}}' <container-name>

下表总结了与日志存储相关的关键路径和用途:

路径/命令 说明
/var/lib/docker/containers/<id>/<id>-json.log 容器日志主文件,json-file 驱动写入的目标
/var/lib/docker/containers/<id>/<id>-json.log.1 轮转后的第一个备份文件(当 max-file > 1 时存在)
docker inspect --format='{{.LogPath}}' 查询指定容器的日志文件绝对路径
docker logs <container> 通过 Docker CLI 查看日志(底层读取 json-file 文件)
注意:不要直接删除或修改 /var/lib/docker/containers/ 下的文件,这可能导致 Docker 守护进程无法正确管理容器。如需清理日志,应通过配置轮转策略或使用 docker logs --tail 等安全方式操作。

3.3 日志轮转与大小限制配置

默认情况下,json-file 驱动不会对日志文件进行任何大小限制。这意味着只要容器持续输出日志,文件就会无限增长,最终可能占满磁盘分区。为避免这一风险,Docker 提供了三个核心配置项来控制日志轮转:

  • max-size:单个日志文件的最大大小。当文件达到该值后,Docker 会执行轮转操作。支持单位:k、m、g,例如 10m 表示 10 兆字节。
  • max-file:保留的日志文件最大数量(包括当前正在写入的文件)。当轮转产生的文件数超过该值时,最旧的文件会被删除。
  • compress:是否对轮转后的日志文件进行 gzip 压缩。默认值为 false,建议在生产环境开启以节省磁盘空间。

配置方式有两种:全局配置(作用于所有容器)和容器级配置(仅作用于指定容器)。

3.3.1 全局配置(daemon.json)

编辑 Docker 守护进程配置文件 /etc/docker/daemon.json,添加 log-driver 和 log-opts 字段:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3",
    "compress": "true"
  }
}

修改完成后,需要重启 Docker 服务使配置生效:

sudo systemctl restart docker
重要提示:全局配置仅对之后创建的容器生效,已存在的容器不会自动应用新配置。如需对已有容器生效,必须重建容器。

3.3.2 容器级配置(docker run)

在启动容器时,通过 --log-opt 参数单独指定日志选项:

docker run -d \
  --name order-service \
  --log-driver json-file \
  --log-opt max-size=5m \
  --log-opt max-file=2 \
  --log-opt compress=true \
  nginx:latest

这种方式适合对特定容器实施差异化策略,例如对日志量大的应用设置更小的轮转阈值,对重要应用保留更多历史文件。

3.4 实战案例:为高并发订单服务配置日志轮转

某电商平台在容器化改造后,发现订单服务(OrderService)的日志文件在 3 天内增长了 40GB,导致宿主机磁盘使用率超过 85%,触发告警。运维团队决定为该服务配置合理的日志轮转策略。

初始状态与约束:

  • 订单服务日日志量约 13GB,峰值时段(大促期间)可达 30GB/天。
  • 宿主机磁盘总容量 500GB,已使用 350GB,剩余空间有限。
  • 业务要求日志至少保留 3 天,以便排查历史订单问题。

分析过程:

根据日日志量 13GB 和保留 3 天的需求,需要约 40GB 的日志存储空间。但考虑到峰值时段可能达到 30GB/天,3 天峰值需要 90GB。为留出安全余量,运维团队决定将日志总占用控制在 30GB 以内,即:

max-size × max-file ≤ 30GB

同时,单文件不宜过大,否则日志采集工具(Filebeat)在读取大文件时可能产生内存压力。最终选择 max-size=10m、max-file=3,总容量为 30MB,远小于 30GB 的预算。但这样只能保留约 3 分钟的高峰日志,无法满足 3 天保留需求。

调整方案:

经过重新评估,运维团队决定采用“双轨策略”:

  1. Docker 层轮转:设置 max-size=100m、max-file=5,总容量 500MB,用于短期快速检索(最近几小时)。
  2. 外部采集归档:通过 Filebeat 将日志实时采集到 Elasticsearch,并配置索引生命周期管理(ILM),保留 3 天热数据、30 天温数据。

这样既保证了 Docker 层日志文件不会无限增长,又满足了业务对历史日志的查询需求。

实施步骤:

# 1. 修改 daemon.json 添加全局配置
cat <<EOF | sudo tee /etc/docker/daemon.json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "5",
    "compress": "true"
  }
}
EOF

# 2. 重启 Docker 使配置生效
sudo systemctl restart docker

# 3. 重建订单服务容器(应用新配置)
docker-compose up -d --force-recreate order-service

# 4. 验证配置是否生效
docker inspect --format='{{.HostConfig.LogConfig}}' order-service

结果对比:

指标 配置前 配置后
单容器日志最大占用 无限制(实际 40GB+) 500MB(100MB × 5)
磁盘使用率 85% 且持续增长 稳定在 70% 左右
日志可追溯时间 约 3 天(但磁盘风险高) Docker 层 2-3 小时 + ES 层 30 天
运维告警频率 每天 2-3 次磁盘告警 无磁盘告警

风险与代价:由于 Docker 层只保留最近 2-3 小时日志,如果 Elasticsearch 集群出现故障,可能导致中间时段日志丢失。为此,运维团队增加了 Filebeat 的持久化队列(queue.mem.events: 4096)和重试机制,降低数据丢失风险。

经验总结:Docker 日志轮转只是“第一道防线”,用于防止磁盘被写满。对于需要长期留存和检索的日志,必须配合外部日志系统。轮转参数应根据实际日志量、磁盘容量和业务保留需求综合计算,避免“一刀切”。

3.5 常见误区与最佳实践

在实际运维中,以下误区经常导致日志管理出现问题:

  • 误区一:只配置 max-size 不配置 max-file。这会导致轮转后的旧文件无限累积,最终同样占满磁盘。必须同时设置 max-file 限制文件数量。
  • 误区二:认为 docker logs 能查看轮转前的所有日志。docker logs 只能读取当前正在写入的日志文件,轮转后的历史文件无法通过该命令查看。
  • 误区三:修改 daemon.json 后不重启 Docker。配置不会热加载,必须重启守护进程才能生效。
  • 误区四:忽略 compress 选项。开启 gzip 压缩可以将轮转文件体积减少 70%-90%,对磁盘空间紧张的环境非常有效。

最佳实践建议:

  1. 在 daemon.json 中设置全局轮转策略,作为安全基线。
  2. 对日志量特别大的容器(如访问日志、审计日志),通过 --log-opt 单独收紧限制。
  3. 定期检查 /var/lib/docker/containers 目录大小,使用 du -sh /var/lib/docker/containers/* 快速定位异常容器。
  4. 将日志采集工具(Filebeat/Fluentd)配置为从 LogPath 读取文件,并开启文件句柄监听,确保轮转时采集不中断。
  5. 在测试环境验证轮转行为:使用 docker logs --tail 10 确认日志写入正常,使用 ls -lh 观察轮转文件生成。

通过合理配置 json-file 驱动的轮转参数,可以在不引入额外复杂度的前提下,有效控制 Docker 日志对磁盘空间的占用,为日志采集链路提供稳定、可控的数据源。