6月6日 20:29
Docker 容器日志怎么管理?轮转、结构化和聚合方案
容器日志管理不只是 docker logs ——那只能看单个容器的标准输出。生产环境需要日志轮转防止磁盘撑满、日志聚合实现集中查询、结构化日志方便检索。这篇从本地管理到集中式方案逐步展开。
docker logs 的局限
bashdocker logs myapp # 查看日志 docker logs -f myapp # 实时跟踪 docker logs --tail 100 myapp # 最近 100 行
问题:
- 容器删了日志就没了
- 多容器没法一起搜
- 没有日志轮转,磁盘会被撑满
- 没有结构化字段,搜索靠 grep
本地日志轮转:防止磁盘撑满
json-file 驱动的轮转配置
yamlservices: app: image: myapp:latest logging: driver: json-file options: max-size: "10m" # 单个日志文件最大 10MB max-file: "3" # 最多保留 3 个文件
这样每个容器最多占 30MB 日志(10MB × 3 个文件)。超过 10MB 自动轮转,超过 3 个文件自动删除最老的。
local 驱动:更省磁盘
yamlservices: app: logging: driver: local options: max-size: "10m" max-file: "5"
local 驱动用压缩存储,同样内容比 json-file 省 50% 空间。而且日志格式更易读。
全局配置
不想每个容器都写 logging 配置?在 daemon.json 里设全局默认:
json// /etc/docker/daemon.json { "log-driver": "local", "log-opts": { "max-size": "10m", "max-file": "3" } }
重启 Docker 后所有容器都用这个配置。强烈建议加上——我见过太多服务器因为 Docker 日志占满磁盘而崩溃。
结构化日志:让检索更高效
非结构化日志只能全文搜索。结构化日志可以按字段过滤:
python# Python - 用 structlog 输出 JSON import structlog logger = structlog.get_logger() logger.info("user_login", user_id=123, ip="1.2.3.4")
javascript// Node.js - 用 pino 输出 JSON const pino = require('pino')() pino.info({ userId: 123, action: 'login' }, 'User logged in')
输出示例:
json{"level":"info","time":1704067200,"userId":123,"action":"login","msg":"User logged in"}
在日志聚合平台里可以按 userId=123 或 action=login 精确过滤,不用全文搜索。
日志级别管理
bash# 动态调整日志级别(不需要重启容器) # Spring Boot curl -X POST http://localhost:8080/actuator/loggers/com.example \ -d '{"configuredLevel": "DEBUG"}' # Node.js(需要应用支持) # 通过环境变量控制 LOG_LEVEL=debug node app.js
生产环境默认 INFO 级别,排查问题时临时切 DEBUG,不需要重新部署。
日志聚合:集中式管理
轻量方案:Grafana Loki
Loki 只索引标签不索引正文,存储成本是 ELK 的 1/10:
yamlservices: loki: image: grafana/loki:2.9.0 ports: - "3100:3100" promtail: image: grafana/promtail:2.9.0 volumes: - /var/lib/docker/containers:/var/lib/docker/containers:ro - ./promtail.yml:/etc/promtail/config.yml command: -config.file=/etc/promtail/config.yml grafana: image: grafana/grafana:10.3.0 ports: - "3000:3000"
Promtail 自动从 Docker 容器目录读取日志,推送到 Loki。Grafana 查询:
logql{container_name="myapp"} |= "error" | json | level="error"
重量方案:EFK Stack
需要全文搜索、复杂聚合时用 EFK(Elasticsearch + Fluentd + Kibana):
yamlservices: elasticsearch: image: elasticsearch:8.12.0 environment: - discovery.type=single-node - xpack.security.enabled=false volumes: - es_data:/usr/share/elasticsearch/data fluentd: image: fluent/fluentd:v1.16 volumes: - ./fluentd/conf:/fluentd/etc ports: - "24224:24224" kibana: image: kibana:8.12.0 ports: - "5601:5601"
容器配置 Fluentd 日志驱动:
yamlservices: app: logging: driver: fluentd options: fluentd-address: localhost:24224 tag: myapp
EFK 最少需要 4GB 内存。团队小于 10 人用 Loki 就够了。
日志管理最佳实践
| 检查项 | 建议 |
|---|---|
| 日志轮转 | 全局配 max-size: 10m, max-file: 3 |
| 日志级别 | 生产用 INFO,排查切 DEBUG |
| 结构化 | 用 JSON 格式输出 |
| 敏感信息 | 不在日志里打印密码、token |
| 聚合 | 小团队用 Loki,大团队用 EFK |
| 持久化 | 关键日志用 Volume 存储,不依赖容器可写层 |
| 监控 | 对 ERROR 日志设置告警 |
起步建议:先配好本地日志轮转(10 分钟的事),再按需加 Loki 聚合。