本文为 Docker配置最佳实践系列第 3 篇,聚焦:监控告警体系、生产环境检查清单、故障排查手册、备份与恢复。


监控告警体系

Prometheus + Grafana监控

version: '3.8'

services:
  prometheus:
    image: prom/prometheus:latest
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus-data:/prometheus
    ports:
      - "9090:9090"

  grafana:
    image: grafana/grafana:latest
    volumes:
      - grafana-data:/var/lib/grafana
    ports:
      - "3000:3000"

  cadvisor:
    image: gcr.io/cadvisor/cadvisor:latest
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
    ports:
      - "8080:8080"

  node-exporter:
    image: prom/node-exporter:latest
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    ports:
      - "9100:9100"

volumes:
  prometheus-data:
  grafana-data:
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'docker'
    static_configs:
      - targets: ['host.docker.internal:9323']
  - job_name: 'cadvisor'
    static_configs:
      - targets: ['cadvisor:8080']
  - job_name: 'node'
    static_configs:
      - targets: ['node-exporter:9100']

告警规则配置

groups:
  - name: docker_alerts
    interval: 30s
    rules:
      - alert: ContainerHighCPU
        expr: rate(container_cpu_usage_seconds_total[5m]) > 0.8
        for: 5m

      - alert: ContainerHighMemory
        expr: (container_memory_usage_bytes / container_spec_memory_limit_bytes) > 0.9
        for: 5m

      - alert: ContainerRestarting
        expr: rate(container_restart_count[15m]) > 0
        for: 5m

      - alert: DiskSpaceHigh
        expr: (node_filesystem_avail_bytes / node_filesystem_size_bytes) < 0.1
        for: 5m

      - alert: DockerDaemonDown
        expr: up{job="docker"} == 0
        for: 1m

健康检查配置

FROM nginx:latest
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost/ || exit 1
services:
  web:
    image: nginx
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost"]
      interval: 30s
      timeout: 3s
      retries: 3
docker ps --format "table {{.Names}}\t{{.Status}}"
docker inspect --format='{{json .State.Health}}' container_name | jq

自定义监控脚本

#!/bin/bash
# /usr/local/bin/docker-monitor.sh

check_containers() {
    local unhealthy=$(docker ps --filter "health=unhealthy" --format "{{.Names}}")
    if [ -n "$unhealthy" ]; then
        echo "警告:以下容器不健康:"
        echo "$unhealthy"
    fi
}

check_resources() {
    docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemPerc}}" | \
        awk 'NR>1 {
            cpu=$2; mem=$3;
            gsub(/%/, "", cpu); gsub(/%/, "", mem);
            if (cpu > 80 || mem > 90) print "警告: "$1" CPU:"cpu"% 内存:"mem"%"
        }'
}

check_disk() {
    local usage=$(df /var/lib/docker | awk 'NR==2 {print $5}' | sed 's/%//')
    if [ $usage -gt 80 ]; then
        echo "警告:Docker磁盘使用率 ${usage}%"
        docker system prune -f
    fi
}

while true; do
    check_containers
    check_resources
    check_disk
    sleep 60
done

生产环境检查清单

部署前检查

x内核版本满足要求

x开启IP转发:net.ipv4.ip_forward=1

x配置合适ulimit

x磁盘空间至少50GB

xNTP时间同步

xdaemon.json配置完整

x镜像加速与日志轮转已配置

x存储驱动overlay2、live-restore开启

x自定义网络与防火墙规则完善

x容器非root运行、漏洞扫描完成

    运行时检查

    #!/bin/bash
    # /usr/local/bin/docker-daily-check.sh
    
    echo "========== Docker每日健康检查 =========="
    echo "检查时间: $(date)"
    
    systemctl is-active docker && echo "✓ Docker服务运行正常" || echo "✗ Docker服务异常"
    
    total=$(docker ps -a | wc -l)
    running=$(docker ps | wc -l)
    echo "总容器数: $((total-1))"
    echo "运行中: $((running-1))"
    
    docker system df
    
    docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemPerc}}" | head -6
    
    journalctl -u docker --since "24 hours ago" | grep -i error | tail -5
    
    echo "========== 检查完成 =========="
    
    0 9 * * * /usr/local/bin/docker-daily-check.sh | mail -s "Docker每日检查报告" admin@example.com
    

    故障排查手册

    常见问题快速诊断

    # 容器无法启动
    docker logs container_name
    docker inspect container_name
    netstat -tlnp | grep <端口>
    docker stats container_name
    docker run -it --entrypoint /bin/sh image_name
    
    # 容器频繁重启
    docker inspect --format='{{.RestartCount}}' container_name
    docker inspect --format='{{.State.ExitCode}}' container_name
    dmesg | grep -i "out of memory"
    docker inspect --format='{{json .State.Health}}' container_name | jq
    
    # 网络不通
    docker inspect --format='{{.NetworkSettings.IPAddress}}' container_name
    docker network inspect bridge
    docker exec container1 ping container2
    docker exec container_name cat /etc/resolv.conf
    iptables -t nat -L -n | grep DOCKER
    systemctl status firewalld
    
    # 磁盘空间不足
    docker system df -v
    docker container prune -f
    docker image prune -a -f
    docker volume prune -f
    docker builder prune -a -f
    docker system prune -a --volumes -f
    

    性能问题排查

    # CPU
    docker stats --no-stream | sort -k3 -h -r | head -5
    docker exec -it container_name top
    docker exec container_name ps aux --forest
    
    # 内存
    watch -n 1 'docker stats --no-stream container_name'
    docker exec container_name cat /proc/meminfo
    docker exec container_name jmap -heap <PID>
    docker exec container_name jmap -dump:format=b,file=/tmp/heap.bin <PID>
    
    # IO
    docker stats --no-stream --format "table {{.Container}}\t{{.BlockIO}}"
    docker exec container_name iostat -x 1
    

    备份与恢复

    容器备份策略

    # 导出容器为镜像
    docker commit container_name backup_image:v1
    docker save -o backup_image_v1.tar backup_image:v1
    gzip backup_image_v1.tar
    docker load -i backup_image_v1.tar.gz
    
    # 备份卷
    docker run --rm \
        -v volume_name:/source \
        -v $(pwd):/backup \
        alpine tar czf /backup/volume_backup.tar.gz -C /source .
    
    # 恢复卷
    docker run --rm \
        -v volume_name:/target \
        -v $(pwd):/backup \
        alpine tar xzf /backup/volume_backup.tar.gz -C /target
    
    #!/bin/bash
    # /usr/local/bin/docker-backup.sh
    
    BACKUP_DIR="/backup/docker"
    DATE=$(date +%Y%m%d_%H%M%S)
    
    mkdir -p $BACKUP_DIR/$DATE
    
    docker images --format "{{.Repository}}:{{.Tag}}" > $BACKUP_DIR/$DATE/images.txt
    
    docker ps -a --format "{{.Names}}" | while read container; do
        docker inspect $container > $BACKUP_DIR/$DATE/${container}_config.json
    done
    
    docker volume ls --format "{{.Name}}" | while read volume; do
        docker run --rm -v $volume:/source -v $BACKUP_DIR/$DATE:/backup alpine \
          tar czf /backup/${volume}.tar.gz -C /source .
    done
    
    cp /etc/docker/daemon.json $BACKUP_DIR/$DATE/
    
    cd $BACKUP_DIR
    tar czf docker_backup_$DATE.tar.gz $DATE
    rm -rf $DATE
    find $BACKUP_DIR -name "docker_backup_*.tar.gz" -mtime +7 -delete
    
    echo "备份完成: $BACKUP_DIR/docker_backup_$DATE.tar.gz"
    

    灾难恢复演练

    #!/bin/bash
    BACKUP_FILE=$1
    if [ -z "$BACKUP_FILE" ]; then
        echo "用法: $0 <备份文件>"
        exit 1
    fi
    
    tar xzf $BACKUP_FILE
    BACKUP_DIR=$(basename $BACKUP_FILE .tar.gz | sed 's/docker_backup_//')
    
    cp $BACKUP_DIR/daemon.json /etc/docker/
    systemctl restart docker
    
    cat $BACKUP_DIR/images.txt | while read image; do
        docker pull $image
    done
    
    for volume_backup in $BACKUP_DIR/*.tar.gz; do
        volume_name=$(basename $volume_backup .tar.gz)
        docker volume create $volume_name
        docker run --rm -v $volume_name:/target -v $(pwd)/$BACKUP_DIR:/backup alpine \
          tar xzf /backup/$(basename $volume_backup) -C /target
    done
    
    echo "请根据配置文件手动重新创建容器"
    ls $BACKUP_DIR/*_config.json
    

    总结

    核心原则:

    1. 安全第一
    2. 性能优化
    3. 可观测性
    4. 高可用性
    5. 自动化

     更详细内容

    个人博客:https://itinstall.dev
    公众号:IT安装手册

    更多推荐