Docker 日志管理
一、Docker 日志概述
1.1 日志的重要性
| 维度 | 说明 |
|---|---|
| 故障排查 | 通过日志定位应用报错、崩溃原因 |
| 性能监控 | 分析访问日志、慢查询、错误率 |
| 安全审计 | 记录操作行为、异常访问、入侵检测 |
| 业务分析 | 用户行为分析、数据统计 |
| 合规要求 | 日志留存满足法律法规要求 |
1.2 Docker 日志的两层架构
┌─────────────────────────────────────────────────────────────────┐
│ 应用层日志 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 容器内的应用:Nginx / Java / Python / Node.js │ │
│ │ 日志输出到:stdout / stderr(标准输出) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Docker 引擎层日志 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 日志驱动(Logging Driver)捕获 stdout/stderr | │
│ │ 并写入到最终目的地(文件 / syslog / 远程服务) │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
核心原则:容器内的应用应将日志写入 stdout / stderr,而不是写到文件。
二、日志驱动(Logging Driver)
2.1 什么是日志驱动
Docker 日志驱动负责捕获容器的 stdout/stderr,并将其输出到指定目的地。
2.2 常见日志驱动
| 驱动 | 说明 | 适用场景 |
|---|---|---|
| json-file | 默认驱动,将日志写入 JSON 文件 | 单机、开发环境 |
| journald | 写入系统 journald 日志 | 使用 systemd 的 Linux 系统 |
| syslog | 转发到 syslog 服务 | 传统日志基础设施 |
| fluentd | 转发到 Fluentd 日志收集器 | 统一日志收集(推荐) |
| loki | 转发到 Grafana Loki | 云原生日志聚合 |
| awslogs | 转发到 AWS CloudWatch | AWS 环境 |
| gelf | 转发到 Graylog | 集中日志管理 |
| splunk | 转发到 Splunk | 企业级日志分析 |
| none | 禁用日志收集 | 不需要日志的场景 |
2.3 查看当前日志驱动
# 查看 Docker 守护进程默认驱动
docker info | grep "Logging Driver"
# 查看容器的日志驱动
docker inspect --format='{{.HostConfig.LogConfig.Type}}' container-name
# 查看容器日志驱动详情
docker inspect container-name | grep -A 5 LogConfig
三、json-file 驱动(默认)
3.1 基本用法
# 默认使用 json-file
docker run -d --name web nginx
# 日志文件位置
# /var/lib/docker/containers/<container-id>/<container-id>-json.log
3.2 查看日志
# 查看所有日志
docker logs web
# 查看最近 100 行
docker logs --tail 100 web
# 实时跟踪日志(类似 tail -f)
docker logs -f web
# 查看最近 30 分钟的日志
docker logs --since 30m web
# 查看特定时间范围的日志
docker logs --since "2024-01-01T10:00:00" --until "2024-01-01T12:00:00" web
# 查看并显示时间戳
docker logs -t web
# 组合使用
docker logs -f --tail 100 -t web
3.3 日志轮转配置
方式一:全局配置(/etc/docker/daemon.json)
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m", // 单个日志文件最大 100MB
"max-file": "5", // 保留 5 个文件
"compress": "true" // 压缩旧日志
}
}
方式二:容器级别配置
docker run -d \
--log-driver json-file \
--log-opt max-size=50m \
--log-opt max-file=3 \
--log-opt compress=true \
--name web \
nginx
方式三:Docker Compose
version: '3.8'
services:
web:
image: nginx
logging:
driver: json-file
options:
max-size: "50m"
max-file: "3"
compress: "true"
四、其他日志驱动配置
4.1 journald(systemd 日志)
# 容器配置
docker run -d \
--log-driver journald \
--log-opt tag="{{.Name}}" \
--name web \
nginx
# 查看 journald 日志
journalctl -u docker CONTAINER_NAME=web -f
journalctl CONTAINER_NAME=web --since "1 hour ago"
4.2 fluentd(推荐)
# 容器配置
docker run -d \
--log-driver fluentd \
--log-opt fluentd-address=localhost:24224 \
--log-opt tag="docker.{{.Name}}" \
--name web \
nginx
Docker Compose 配置:
version: '3.8'
services:
web:
image: nginx
logging:
driver: fluentd
options:
fluentd-address: "fluentd:24224"
tag: "docker.{{.Name}}"
fluentd-async: "true"
fluentd-sub-second-precision: "true"
Fluentd 配置文件示例:
# fluentd.conf
<source>
@type forward
port 24224
bind 0.0.0.0
</source>
<match docker.**>
@type elasticsearch
host elasticsearch
port 9200
index_name docker-logs
logstash_format true
</match>
4.3 Loki(Grafana 生态)
# 容器配置
docker run -d \
--log-driver loki \
--log-opt loki-url="http://loki:3100/loki/api/v1/push" \
--log-opt labels="container_name" \
--name web \
nginx
4.4 AWS CloudWatch
docker run -d \
--log-driver awslogs \
--log-opt awslogs-region=us-east-1 \
--log-opt awslogs-group=myapp-logs \
--log-opt awslogs-stream=web \
--name web \
nginx
4.5 none(禁用日志)
# 完全不收集日志
docker run -d --log-driver none --name web nginx
五、Docker logs 命令详解
5.1 命令格式
docker logs [OPTIONS] CONTAINER
5.2 常用选项
| 选项 | 说明 | 示例 |
|---|---|---|
-f / --follow | 实时跟踪日志输出 | docker logs -f web |
--tail | 显示最后 N 行 | docker logs --tail 100 web |
--since | 显示指定时间之后的日志 | docker logs --since 2h web |
--until | 显示指定时间之前的日志 | docker logs --until 1h web |
-t / --timestamps | 显示时间戳 | docker logs -t web |
--details | 显示额外详细信息 | docker logs --details web |
5.3 时间格式
# 相对时间
docker logs --since 30m web # 30 分钟前
docker logs --since 2h web # 2 小时前
docker logs --since 7d web # 7 天前
# 绝对时间(RFC3339)
docker logs --since "2024-01-01T10:00:00Z" web
docker logs --until "2024-01-01T12:00:00Z" web
# 使用日期字符串
docker logs --since "2024-01-01 10:00" web
5.4 高级用法
# 查看错误日志(grep)
docker logs web 2>&1 | grep ERROR
# 统计错误次数
docker logs web 2>&1 | grep -c ERROR
# 查看最近 10 分钟的异常日志
docker logs --since 10m web 2>&1 | grep -E "ERROR|FATAL|Exception"
# 导出日志到文件
docker logs web > web.log 2>&1
# 查看特定容器的实时日志并过滤
docker logs -f web | grep --line-buffered "Request"
六、容器内应用日志规范
6.1 为什么日志要输出到 stdout/stderr
| 优点 | 说明 |
|---|---|
| 标准化 | 所有容器统一方式,无需配置 |
| 自动收集 | Docker 引擎自动捕获 |
| 与驱动无关 | 切换日志驱动不影响应用 |
| 简单 | 应用无需关心日志存储位置 |
6.2 各语言输出日志到 stdout
Java(SpringBoot):
# application.yml
logging:
file:
name: /dev/stdout # 或 /dev/console
level:
root: INFO
<!-- logback-spring.xml -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
Python:
import logging
import sys
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
stream=sys.stdout # 输出到 stdout
)
Node.js:
// 直接使用 console.log 默认输出到 stdout
console.log('Info message');
console.error('Error message'); // 输出到 stderr
// 使用 winston
const winston = require('winston');
const logger = winston.createLogger({
transports: [
new winston.transports.Console()
]
});
Nginx:
# nginx.conf
access_log /dev/stdout;
error_log /dev/stderr;
6.3 结构化日志(推荐)
结构化日志便于日志收集系统解析和查询:
// JSON 格式日志(推荐)
{"time":"2024-01-01T10:00:00Z","level":"INFO","message":"User logged in","user_id":123,"ip":"192.168.1.1"}
{"time":"2024-01-01T10:00:01Z","level":"ERROR","message":"Database connection failed","error":"timeout","retry":3}
七、日志轮转与清理
7.1 日志文件位置与清理
# Docker 日志文件位置
/var/lib/docker/containers/<container-id>/<container-id>-json.log
# 查看日志文件大小
du -sh /var/lib/docker/containers/*/*.log
# 查看日志文件数量
ls -la /var/lib/docker/containers/*/*.log | wc -l
7.2 手动清理日志
# 清空单个容器日志
truncate -s 0 /var/lib/docker/containers/<container-id>/<container-id>-json.log
# 清空所有容器日志
sudo sh -c 'find /var/lib/docker/containers -name "*-json.log" -exec truncate -s 0 {} \;'
# 使用 docker system prune 清理
docker system prune -a --volumes
7.3 日志大小限制全局配置
// /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "5",
"compress": "true"
},
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.size=20G" // 限制总存储大小
]
}
7.4 日志清理脚本
#!/bin/bash
# clean-docker-logs.sh
echo "开始清理 Docker 日志..."
# 获取所有容器日志文件
LOG_FILES=$(find /var/lib/docker/containers -name "*-json.log")
for file in $LOG_FILES; do
size=$(du -h $file | cut -f1)
echo "清理: $file ($size)"
truncate -s 0 $file
done
echo "日志清理完成。"
# 清理 Docker 系统资源
docker system prune -f
echo "Docker 系统资源清理完成。"
八、日志集中收集架构
8.1 ELK 架构
应用容器 → stdout/stderr
↓
Docker json-file 驱动
↓
Filebeat(日志采集器)
↓
Elasticsearch(存储 + 索引)
↓
Kibana(可视化查询)
docker-compose.yml:
version: '3.8'
services:
filebeat:
image: elastic/filebeat:8.10
volumes:
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
depends_on:
- elasticsearch
elasticsearch:
image: elasticsearch:8.10
environment:
- discovery.type=single-node
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
volumes:
- es-data:/usr/share/elasticsearch/data
kibana:
image: kibana:8.10
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
volumes:
es-data:
filebeat.yml:
filebeat.inputs:
- type: container
paths:
- /var/lib/docker/containers/*/*.log
processors:
- add_docker_metadata: ~
output.elasticsearch:
hosts: ['elasticsearch:9200']
index: "docker-logs-%{+yyyy.MM.dd}"
setup.kibana:
host: "kibana:5601"
8.2 Loki + Grafana 架构
version: '3.8'
services:
promtail:
image: grafana/promtail:2.9
volumes:
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./promtail.yml:/etc/promtail/promtail.yml:ro
loki:
image: grafana/loki:2.9
volumes:
- loki-data:/loki
grafana:
image: grafana/grafana:10.2
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
loki-data:
8.3 Fluentd 采集架构
version: '3.8'
services:
fluentd:
image: fluent/fluentd-kubernetes-daemonset:v1-debian-elasticsearch
volumes:
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- ./fluentd.conf:/fluentd/etc/fluentd.conf:ro
ports:
- "24224:24224"
elasticsearch:
image: elasticsearch:8.10
environment:
- discovery.type=single-node
kibana:
image: kibana:8.10
ports:
- "5601:5601"
九、日志分析与排障
9.1 常见日志分析场景
# 1. 查看容器启动失败原因
docker logs <container-id>
# 2. 实时监控错误
docker logs -f <container> 2>&1 | grep -i error
# 3. 分析访问量
docker logs web 2>&1 | grep -c "GET /api"
# 4. 查看特定时间段的错误
docker logs --since "1h" web 2>&1 | grep -c "ERROR"
# 5. 查看慢请求
docker logs web 2>&1 | grep -E "duration=[0-9]{4,}ms"
# 6. 统计错误类型
docker logs web 2>&1 | grep "ERROR" | cut -d' ' -f4 | sort | uniq -c | sort -nr
9.2 日志级别最佳实践
| 级别 | 适用场景 | 示例 |
|---|---|---|
| ERROR | 系统错误、需要立即关注 | 数据库连接失败 |
| WARN | 潜在问题、降级处理 | 接口超时、重试 |
| INFO | 关键业务操作 | 用户登录、订单创建 |
| DEBUG | 开发调试信息 | 参数值、中间状态 |
| TRACE | 详细追踪信息 | 完整调用链 |
十、常见问题与解决
Q1:docker logs 命令卡住或很慢
原因:日志文件过大,读取耗时。
解决:
# 限制输出行数
docker logs --tail 100 web
# 只查最近的
docker logs --since 10m web
Q2:容器日志占满磁盘空间
解决:
# 1. 配置日志轮转
# 在 daemon.json 中配置 max-size 和 max-file
# 2. 清理日志
truncate -s 0 /var/lib/docker/containers/*/*-json.log
# 3. 使用 logrotate
# /etc/logrotate.d/docker
/var/lib/docker/containers/*/*.log {
daily
rotate 7
compress
delaycompress
missingok
copytruncate
}
Q3:容器日志中中文乱码
原因:字符编码不一致。
解决:
# 设置容器字符编码
docker run -d \
-e LANG=C.UTF-8 \
-e LC_ALL=C.UTF-8 \
--name app \
myimage
Q4:日志时间不是本地时间
解决:
# 容器内设置时区
docker run -d \
-e TZ=Asia/Shanghai \
-v /etc/localtime:/etc/localtime:ro \
--name app \
myimage
Q5:如何查看已停止容器的日志
# 加上 -a 查看所有容器
docker logs -a <container-id>
# 或使用
docker logs <container-id> 2>&1
十一、日志管理最佳实践
| 实践 | 说明 |
|---|---|
| 应用输出到 stdout/stderr | 不要写文件到容器内 |
| 配置日志轮转 | 设置 max-size 和 max-file,防止磁盘占满 |
| 使用结构化日志 | JSON 格式,便于系统解析和查询 |
| 集中日志收集 | 生产环境使用 ELK/Loki 等工具 |
| 设置日志级别 | 生产环境用 INFO/WARN,避免 DEBUG 过度输出 |
| 监控日志大小 | 设置告警,防止日志堆积 |
| 定期清理 | 配置自动化清理策略 |
十二、命令速查表
| 操作 | 命令 |
|---|---|
| 查看容器日志 | docker logs <容器> |
| 实时跟踪日志 | docker logs -f <容器> |
| 查看最近 N 行 | docker logs --tail 100 <容器> |
| 查看最近时间日志 | docker logs --since 30m <容器> |
| 查看带时间戳日志 | docker logs -t <容器> |
| 查看容器日志驱动 | docker inspect -f '{{.HostConfig.LogConfig.Type}}' <容器> |
| 查看 Docker 默认日志驱动 | docker info \| grep "Logging Driver" |
| 清空容器日志 | truncate -s 0 /var/lib/docker/containers/*/*-json.log |
| 清理 Docker 资源 | docker system prune -a |
十三、总结
Docker 日志管理三原则:
应用输出到 stdout/stderr(标准化)
配置日志轮转(防磁盘占满)
集中日志收集(生产必做,ELK/Loki)
默认
json-file驱动适合开发测试,生产环境推荐fluentd或loki驱动对接统一日志平台。