Skip to content

Docker 日志管理

容器日志管理是运维监控的重要组成部分。本文档介绍 Docker 日志的收集、存储、分析和最佳实践。

📝 日志基础概念

Docker 日志驱动

Docker 支持多种日志驱动:

驱动描述使用场景
json-file默认驱动,JSON 格式开发和小型部署
syslog系统日志集成现有日志系统
journaldsystemd 日志systemd 系统
fluentdFluentd 日志收集大规模日志收集
awslogsAWS CloudWatchAWS 环境
gcplogsGoogle Cloud LoggingGCP 环境
none禁用日志不需要日志的容器

查看当前日志配置

bash
# 查看 Docker 守护进程日志配置
docker info | grep -A 10 "Logging Driver"

# 查看容器日志配置
docker inspect container-name | grep -A 10 "LogConfig"

📊 基础日志操作

查看容器日志

bash
# 查看容器日志
docker logs container-name

# 实时查看日志
docker logs -f container-name

# 查看最近的日志
docker logs --tail 100 container-name

# 查看指定时间范围的日志
docker logs --since "2024-01-01T00:00:00" container-name
docker logs --until "2024-01-01T23:59:59" container-name

# 显示时间戳
docker logs -t container-name

# 组合使用
docker logs -f --tail 50 --since "1h" container-name

日志格式化

bash
# 查看 JSON 格式日志
docker logs --details container-name

# 使用 jq 格式化日志
docker logs container-name 2>&1 | jq '.'

# 提取特定字段
docker logs container-name 2>&1 | jq '.log'

🔧 日志驱动配置

全局日志配置

json
# /etc/docker/daemon.json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3",
    "labels": "production_status",
    "env": "os,customer"
  }
}
bash
# 重启 Docker 服务使配置生效
sudo systemctl restart docker

容器级别日志配置

bash
# 运行时指定日志驱动
docker run -d \
  --log-driver json-file \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  --name web nginx

# 使用 syslog 驱动
docker run -d \
  --log-driver syslog \
  --log-opt syslog-address=tcp://192.168.1.100:514 \
  --log-opt tag="{{.Name}}/{{.FullID}}" \
  --name app my-app

Docker Compose 日志配置

yaml
services:
  web:
    image: nginx
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
        labels: "service=web,environment=production"
    
  api:
    image: my-api
    logging:
      driver: "syslog"
      options:
        syslog-address: "tcp://log-server:514"
        tag: "api-{{.Name}}"
    
  db:
    image: postgres:15
    logging:
      driver: "fluentd"
      options:
        fluentd-address: "fluentd:24224"
        tag: "database.{{.Name}}"

🏗️ 集中化日志收集

ELK Stack 部署

yaml
# docker-compose.elk.yml
version: '3.8'

services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
    container_name: elasticsearch
    environment:
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - xpack.security.enabled=false
    ports:
      - "9200:9200"
    volumes:
      - elasticsearch-data:/usr/share/elasticsearch/data
    
  logstash:
    image: docker.elastic.co/logstash/logstash:8.11.0
    container_name: logstash
    ports:
      - "5044:5044"
      - "9600:9600"
    volumes:
      - ./logstash/pipeline:/usr/share/logstash/pipeline
      - ./logstash/config:/usr/share/logstash/config
    depends_on:
      - elasticsearch
    
  kibana:
    image: docker.elastic.co/kibana/kibana:8.11.0
    container_name: kibana
    ports:
      - "5601:5601"
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    depends_on:
      - elasticsearch
    
  filebeat:
    image: docker.elastic.co/beats/filebeat:8.11.0
    container_name: filebeat
    user: root
    volumes:
      - ./filebeat/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
      - logstash

volumes:
  elasticsearch-data:

Filebeat 配置

yaml
# filebeat/filebeat.yml
filebeat.inputs:
- type: container
  paths:
    - '/var/lib/docker/containers/*/*.log'
  processors:
    - add_docker_metadata:
        host: "unix:///var/run/docker.sock"

output.logstash:
  hosts: ["logstash:5044"]

logging.level: info
logging.to_files: true
logging.files:
  path: /var/log/filebeat
  name: filebeat
  keepfiles: 7
  permissions: 0644

Logstash 配置

ruby
# logstash/pipeline/logstash.conf
input {
  beats {
    port => 5044
  }
}

filter {
  if [container][name] {
    mutate {
      add_field => { "service_name" => "%{[container][name]}" }
    }
  }
  
  # 解析 JSON 日志
  if [message] =~ /^\{.*\}$/ {
    json {
      source => "message"
    }
  }
  
  # 添加时间戳
  date {
    match => [ "timestamp", "ISO8601" ]
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "docker-logs-%{+YYYY.MM.dd}"
  }
  
  stdout {
    codec => rubydebug
  }
}

📈 Fluentd 日志收集

Fluentd 部署

yaml
# docker-compose.fluentd.yml
services:
  fluentd:
    image: fluent/fluentd:v1.16-debian-1
    container_name: fluentd
    ports:
      - "24224:24224"
      - "24224:24224/udp"
    volumes:
      - ./fluentd/conf:/fluentd/etc
      - ./fluentd/logs:/var/log/fluentd
    
  app:
    image: my-app
    logging:
      driver: fluentd
      options:
        fluentd-address: localhost:24224
        tag: "app.{{.Name}}"

Fluentd 配置

xml
<!-- fluentd/conf/fluent.conf -->
<source>
  @type forward
  port 24224
  bind 0.0.0.0
</source>

<filter app.**>
  @type parser
  key_name log
  <parse>
    @type json
  </parse>
</filter>

<match app.**>
  @type elasticsearch
  host elasticsearch
  port 9200
  index_name docker-logs
  type_name _doc
  <buffer>
    @type file
    path /var/log/fluentd/buffer
    flush_mode interval
    flush_interval 10s
  </buffer>
</match>

<match **>
  @type stdout
</match>

🔍 日志分析和查询

Elasticsearch 查询

bash
# 基础查询
curl -X GET "localhost:9200/docker-logs-*/_search?pretty" \
  -H 'Content-Type: application/json' \
  -d '{
    "query": {
      "match": {
        "service_name": "web"
      }
    }
  }'

# 时间范围查询
curl -X GET "localhost:9200/docker-logs-*/_search?pretty" \
  -H 'Content-Type: application/json' \
  -d '{
    "query": {
      "bool": {
        "must": [
          {"match": {"service_name": "api"}},
          {"range": {"@timestamp": {"gte": "now-1h"}}}
        ]
      }
    }
  }'

Kibana 仪表板

json
{
  "version": "8.11.0",
  "objects": [
    {
      "id": "docker-logs-dashboard",
      "type": "dashboard",
      "attributes": {
        "title": "Docker Logs Dashboard",
        "hits": 0,
        "description": "Docker container logs monitoring",
        "panelsJSON": "[{\"version\":\"8.11.0\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{},\"panelRefName\":\"panel_1\"}]"
      }
    }
  ]
}

🚨 日志告警

Elastalert 配置

yaml
# elastalert/rules/error_alert.yml
name: Docker Error Alert
type: frequency
index: docker-logs-*
num_events: 5
timeframe:
  minutes: 5

filter:
- terms:
    level: ["error", "ERROR", "Error"]

alert:
- "email"
- "slack"

email:
- "admin@example.com"

slack:
webhook_url: "https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"
slack_channel_override: "#alerts"
slack_username_override: "ElastAlert"

Prometheus + Grafana 监控

yaml
# docker-compose.monitoring.yml
services:
  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
    
  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana-data:/var/lib/grafana
    
  node-exporter:
    image: prom/node-exporter
    ports:
      - "9100:9100"
    
  cadvisor:
    image: gcr.io/cadvisor/cadvisor
    ports:
      - "8080:8080"
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro

volumes:
  grafana-data:

🛠️ 日志管理工具

日志轮转脚本

bash
#!/bin/bash
# log-rotation.sh

# 压缩旧日志
find /var/lib/docker/containers -name "*.log" -size +100M -exec gzip {} \;

# 删除超过 30 天的日志
find /var/lib/docker/containers -name "*.log.gz" -mtime +30 -delete

# 清理 Docker 日志
docker system prune -f --filter "until=720h"

日志备份脚本

bash
#!/bin/bash
# backup-logs.sh

DATE=$(date +%Y%m%d)
BACKUP_DIR="/backup/docker-logs"

# 创建备份目录
mkdir -p $BACKUP_DIR

# 备份容器日志
for container in $(docker ps -q); do
  container_name=$(docker inspect --format='{{.Name}}' $container | sed 's/\///')
  docker logs $container > $BACKUP_DIR/${container_name}_${DATE}.log
done

# 压缩备份
tar -czf $BACKUP_DIR/docker-logs-${DATE}.tar.gz $BACKUP_DIR/*.log
rm $BACKUP_DIR/*.log

🚀 最佳实践

1. 结构化日志

javascript
// 应用程序中使用结构化日志
const winston = require('winston');

const logger = winston.createLogger({
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json()
  ),
  transports: [
    new winston.transports.Console()
  ]
});

logger.info('User login', {
  userId: '12345',
  ip: '192.168.1.100',
  userAgent: 'Mozilla/5.0...'
});

2. 日志级别管理

yaml
services:
  app:
    image: my-app
    environment:
      - LOG_LEVEL=info
      - DEBUG=app:*
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

3. 敏感信息过滤

ruby
# logstash 过滤敏感信息
filter {
  mutate {
    gsub => [
      "message", "password=[^&\s]*", "password=***",
      "message", "token=[^&\s]*", "token=***"
    ]
  }
}

4. 性能优化

yaml
services:
  app:
    image: my-app
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
        compress: "true"  # 压缩日志文件

5. 监控日志存储

bash
# 监控日志磁盘使用
df -h /var/lib/docker

# 清理日志
docker system prune --volumes -f

通过合理的日志管理策略,您可以有效监控容器化应用的运行状态,快速定位和解决问题。