强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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 容器监控方案。