强曰为道

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

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-fileJSON 文件(默认)
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

特性ELKLoki
资源占用高(需要 Java)低(Go 实现)
索引方式全文索引仅索引标签
存储成本
查询语言KQL/LuceneLogQL
与 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 容器监控方案。