Docker 完全指南 / 13 - 日志管理
13 - 日志管理
掌握 Docker 日志驱动、日志轮转配置,以及 ELK 和 Loki 集中日志方案。
13.1 Docker 日志机制
Docker 默认使用 json-file 日志驱动,将容器的标准输出(stdout)和标准错误(stderr)以 JSON 格式写入宿主机文件。
日志文件位置
# 日志文件路径
/var/lib/docker/containers/<container-id>/<container-id>-json.log
# 查看日志文件大小
ls -lh /var/lib/docker/containers/<container-id>/<container-id>-json.log
# 统计所有容器日志大小
for c in $(docker ps -q); do
name=$(docker inspect --format '{{.Name}}' $c | sed 's/\///')
size=$(ls -lh /var/lib/docker/containers/$c/*-json.log | awk '{print $5}')
echo "$name: $size"
done
13.2 常用日志命令
# 查看全部日志
docker logs my-container
# 实时跟踪
docker logs -f my-container
# 最近 N 行
docker logs --tail 100 my-container
# 时间过滤
docker logs --since 2024-01-01T00:00:00 my-container
docker logs --since 30m my-container
docker logs --until 1h ago my-container
# 显示时间戳
docker logs -t my-container
# 组合使用
docker logs -f --tail 50 --since 10m -t my-container
# JSON 格式化查看
docker logs my-container 2>&1 | jq .
13.3 日志驱动详解
可用日志驱动
| 驱动 | 说明 | docker logs 支持 |
|---|---|---|
json-file | JSON 文件(默认) | ✅ |
local | 优化的本地日志 | ✅ |
syslog | 发送到 syslog | ❌ |
journald | 发送到 systemd journal | ✅ |
fluentd | 发送到 fluentd | ❌ |
awslogs | 发送到 AWS CloudWatch | ❌ |
gcplogs | 发送到 Google Cloud Logging | ❌ |
gelf | 发送到 Graylog (GELF) | ❌ |
splunk | 发送到 Splunk | ❌ |
etwlogs | 发送到 Windows ETW | ❌ |
none | 禁用日志 | ❌ |
配置 json-file 驱动
// /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "5",
"compress": "true",
"tag": "{{.Name}}/{{.ID}}"
}
}
| 参数 | 说明 | 默认值 |
|---|---|---|
max-size | 单个日志文件最大大小 | 无限制 |
max-file | 日志文件数量 | 1 |
compress | 压缩轮转的日志 | false |
tag | 日志标签模板 | 容器 ID |
配置 local 驱动
// /etc/docker/daemon.json
{
"log-driver": "local",
"log-opts": {
"max-size": "10m",
"max-file": "5",
"compress": "true"
}
}
local 驱动比 json-file 更高效,使用更紧凑的二进制格式。
单容器日志配置
# 为单个容器指定日志驱动
docker run -d \
--log-driver=json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
--name web nginx:alpine
# 禁用日志
docker run -d --log-driver=none --name silent-app my-app:latest
13.4 日志标签模板
{
"log-opts": {
"tag": "{{.Name}}/{{.ID}}/{{.ImageName}}"
}
}
| 模板变量 | 说明 |
|---|---|
{{.ID}} | 容器 ID |
{{.Name}} | 容器名称 |
{{.ImageID}} | 镜像 ID |
{{.ImageName}} | 镜像名称 |
{{.DaemonName}} | Docker daemon 名称 |
13.5 ELK 集中日志方案
架构概览
ELK 日志方案:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 容器 A │ │ 容器 B │ │ 容器 C │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
┌────┴─────────────┴─────────────┴───┐
│ Filebeat / Logstash │
│ (日志采集器) │
└────────────────┬───────────────────┘
│
┌────────────────┴───────────────────┐
│ Elasticsearch │
│ (日志存储和索引) │
└────────────────┬───────────────────┘
│
┌────────────────┴───────────────────┐
│ Kibana │
│ (日志可视化) │
└────────────────────────────────────┘
Docker Compose 实现
services:
# ---- 应用服务 ----
app:
image: my-app:latest
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
labels:
- "co.elastic.logs/module=app"
# ---- Elasticsearch ----
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
volumes:
- es-data:/usr/share/elasticsearch/data
ports:
- "9200:9200"
deploy:
resources:
limits:
memory: 1G
# ---- Kibana ----
kibana:
image: docker.elastic.co/kibana/kibana:8.12.0
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
ports:
- "5601:5601"
depends_on:
- elasticsearch
# ---- Filebeat (日志采集) ----
filebeat:
image: docker.elastic.co/beats/filebeat:8.12.0
user: root
volumes:
- ./filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
depends_on:
- elasticsearch
volumes:
es-data:
filebeat.yml 配置
filebeat.inputs:
- type: container
paths:
- '/var/lib/docker/containers/*/*.log'
processors:
- add_docker_metadata:
host: "unix:///var/run/docker.sock"
processors:
- decode_json_fields:
fields: ["message"]
target: "json"
overwrite_keys: true
output.elasticsearch:
hosts: ["elasticsearch:9200"]
indices:
- index: "docker-logs-%{+yyyy.MM.dd}"
logging.level: info
logging.to_files: true
13.6 Loki 日志方案(轻量级)
为什么选择 Loki
| 特性 | ELK | Loki |
|---|---|---|
| 资源占用 | 高(需要 Java) | 低(Go 实现) |
| 索引方式 | 全文索引 | 仅索引标签 |
| 存储成本 | 高 | 低 |
| 查询语言 | KQL/Lucene | LogQL |
| 与 Grafana 集成 | 需要插件 | 原生支持 |
| 适用场景 | 复杂搜索 | 标签过滤 |
Docker Compose 实现
services:
# ---- Loki (日志存储) ----
loki:
image: grafana/loki:2.9.0
ports:
- "3100:3100"
volumes:
- ./loki-config.yml:/etc/loki/local-config.yaml
- loki-data:/loki
command: -config.file=/etc/loki/local-config.yaml
# ---- Promtail (日志采集) ----
promtail:
image: grafana/promtail:2.9.0
volumes:
- ./promtail-config.yml:/etc/promtail/config.yml
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
command: -config.file=/etc/promtail/config.yml
# ---- Grafana (可视化) ----
grafana:
image: grafana/grafana:10.2.0
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana-data:/var/lib/grafana
volumes:
loki-data:
grafana-data:
loki-config.yml
auth_enabled: false
server:
http_listen_port: 3100
common:
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
schema_config:
configs:
- from: 2020-10-24
store: boltdb-shipper
object_store: filesystem
schema: v11
index:
prefix: index_
period: 24h
limits_config:
reject_old_samples: true
reject_old_samples_max_age: 168h
promtail-config.yml
server:
http_listen_port: 9080
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: docker
docker_sd_configs:
- host: unix:///var/run/docker.sock
refresh_interval: 5s
relabel_configs:
- source_labels: ['__meta_docker_container_name']
regex: '/(.*)'
target_label: 'container'
- source_labels: ['__meta_docker_container_log_stream']
target_label: 'stream'
- source_labels: ['__meta_docker_container_label_com_docker_compose_service']
target_label: 'service'
pipeline_stages:
- docker: {}
- json:
expressions:
level: level
msg: message
- labels:
level:
13.7 Syslog 驱动
# 发送到远程 syslog 服务器
docker run -d \
--log-driver=syslog \
--log-opt syslog-address=tcp://syslog-server:514 \
--log-opt syslog-facility=daemon \
--log-opt tag="{{.Name}}" \
nginx:alpine
# 发送到本地 syslog
docker run -d \
--log-driver=syslog \
--log-opt syslog-address=unix:///dev/log \
nginx:alpine
13.8 日志分析技巧
从 JSON 日志提取字段
# 查看错误级别日志
docker logs my-app 2>&1 | jq -r 'select(.level=="error") | .message'
# 按时间范围过滤
docker logs my-app 2>&1 | jq -r 'select(.time > "2024-01-01") | [.time, .level, .message] | @tsv'
# 统计错误数量
docker logs my-app 2>&1 | jq -r 'select(.level=="error")' | wc -l
磁盘空间管理
# 查看所有容器日志总大小
find /var/lib/docker/containers -name "*-json.log" -exec ls -lh {} \;
# 清空容器日志(不停止容器)
truncate -s 0 /var/lib/docker/containers/<container-id>/<container-id>-json.log
# 批量清空所有容器日志
for log in /var/lib/docker/containers/*/*.log; do
truncate -s 0 "$log"
done
要点回顾
| 要点 | 核心内容 |
|---|---|
| 默认驱动 | json-file,务必配置 max-size 和 max-file |
| 日志轮转 | daemon.json 全局配置,或单容器 --log-opt |
| ELK 方案 | 全功能但资源占用高,适合大规模环境 |
| Loki 方案 | 轻量级,标签索引,与 Grafana 原生集成 |
| 日志清理 | 定期清理日志文件或配置自动轮转 |
注意事项
磁盘写满风险: 不限制日志大小可能导致磁盘写满,影响宿主机和所有容器。
日志驱动选择: 使用非 json-file 驱动时,
docker logs命令可能不可用。根据需求权衡。
敏感信息: 避免在日志中输出密码、API Key 等敏感信息。
下一步
→ 14 - 监控方案:学习 cAdvisor、Prometheus 容器监控方案。