Docker 监控与调试
有效的监控和调试是保障容器化应用稳定运行的关键。本章将介绍 Docker 的监控工具、日志管理和故障排除技术。
监控概述
为什么需要监控?
容器化环境的监控与传统应用有所不同:
| 监控维度 | 传统应用 | 容器化应用 |
|---|---|---|
| 资源粒度 | 虚拟机/物理机 | 容器级别 |
| 生命周期 | 长期运行 | 动态创建销毁 |
| 网络模型 | 固定 IP | 动态网络 |
| 日志收集 | 文件系统 | 标准输出 |
监控指标类型
┌─────────────────────────────────────────────────────────┐
│ 监控指标层次 │
├─────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────┐ │
│ │ 应用指标 (Application) │ │
│ │ 请求量、响应时间、错误率 │ │
│ └─────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 容器指标 (Container) │ │
│ │ CPU、内存、网络、磁盘 IO │ │
│ └─────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 主机指标 (Host) │ │
│ │ 节点资源、内核状态、存储容量 │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
内置监控命令
容器资源统计
docker stats 是最常用的资源监控命令:
# 实时查看所有容器资源使用
docker stats
# 输出示例
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
a1b2c3d4e5f6 nginx 0.50% 10MiB / 1GiB 1.00% 1.2kB / 0B 0B / 0B 5
# 查看特定容器
docker stats nginx redis
# 不实时刷新(只显示一次)
docker stats --no-stream
# 自定义输出格式
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
# JSON 格式输出
docker stats --format json
指标说明:
| 指标 | 说明 |
|---|---|
| CPU % | CPU 使用百分比 |
| MEM USAGE / LIMIT | 内存使用量/限制 |
| MEM % | 内存使用百分比 |
| NET I/O | 网络接收/发送字节 |
| BLOCK I/O | 磁盘读写字节 |
| PIDS | 进程数 |
查看容器进程
# 查看容器内进程
docker top nginx
# 输出示例
UID PID PPID C STIME TTY TIME CMD
root 1234 567 0 10:00 ? 00:00:01 nginx: master process
# 显示完整命令
docker top nginx aux
# 查看所有容器的进程
for container in $(docker ps -q); do
echo "=== $(docker inspect --format='{{.Name}}' $container) ==="
docker top $container
done
查看容器事件
# 实时监听 Docker 事件
docker events
# 过滤特定事件类型
docker events --filter type=container
# 过滤特定容器
docker events --filter container=nginx
# 过滤事件动作
docker events --filter event=start --filter event=stop --filter event=die
# 时间范围过滤
docker events --since 1h
docker events --since "2024-01-01" --until "2024-01-02"
# 格式化输出
docker events --format '{{.Time}} {{.Type}} {{.Action}} {{.Actor.ID}}'
查看容器详情
# 查看完整配置
docker inspect nginx
# 查看特定信息
docker inspect --format='{{.State.Status}}' nginx
docker inspect --format='{{.State.Health.Status}}' nginx
docker inspect --format='{{.NetworkSettings.IPAddress}}' nginx
docker inspect --format='{{.HostConfig.Memory}}' nginx
# 查看所有容器的 IP
docker inspect --format='{{.Name}} {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -aq)
日志管理
查看容器日志
# 查看日志
docker logs nginx
# 实时跟踪日志
docker logs -f nginx
# 显示最后 100 行
docker logs --tail 100 nginx
# 显示时间戳
docker logs -t nginx
# 时间范围过滤
docker logs --since 2024-01-01 nginx
docker logs --since 2h nginx
docker logs --until 1h nginx
# 组合使用
docker logs -f --tail 100 --since 1h nginx
日志驱动配置
Docker 支持多种日志驱动:
| 驱动 | 说明 | 适用场景 |
|---|---|---|
| json-file | 默认,JSON 格式存储 | 单机开发 |
| local | 优化的本地日志 | 生产环境 |
| journald | 发送到 systemd journal | systemd 系统 |
| syslog | 发送到 syslog 守护进程 | 集中式日志 |
| fluentd | 发送到 Fluentd | 日志聚合 |
| awslogs | 发送到 AWS CloudWatch | AWS 环境 |
| none | 禁用日志 | 安全敏感场景 |
运行时指定日志驱动:
# 使用 journald
docker run -d --log-driver=journald nginx
# 使用 syslog
docker run -d --log-driver=syslog --log-opt syslog-address=tcp://192.168.1.1:514 nginx
# 禁用日志
docker run -d --log-driver=none nginx
# json-file 带限制
docker run -d \
--log-driver=json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
nginx
Docker Daemon 配置:
// /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3",
"labels": "production"
}
}
Docker Compose 配置:
services:
app:
image: myapp
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
日志聚合方案
使用 ELK Stack:
# docker-compose.yml
services:
elasticsearch:
image: elasticsearch:8.11.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false
ports:
- "9200:9200"
volumes:
- es-data:/usr/share/elasticsearch/data
logstash:
image: logstash:8.11.0
volumes:
- ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
ports:
- "5000:5000"
depends_on:
- elasticsearch
kibana:
image: kibana:8.11.0
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
depends_on:
- elasticsearch
app:
image: myapp
logging:
driver: "syslog"
options:
syslog-address: "tcp://logstash:5000"
tag: "myapp"
volumes:
es-data:
使用 Loki + Grafana:
services:
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
volumes:
- ./loki-config.yml:/etc/loki/local-config.yaml
promtail:
image: grafana/promtail:latest
volumes:
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- ./promtail-config.yml:/etc/promtail/config.yml
command: -config.file=/etc/promtail/config.yml
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true
volumes:
- grafana-data:/var/lib/grafana
volumes:
grafana-data:
Prometheus 监控
部署 Prometheus 栈
# docker-compose.yml
services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana-data:/var/lib/grafana
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
node-exporter:
image: prom/node-exporter:latest
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
volumes:
prometheus-data:
grafana-data:
Prometheus 配置:
# prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['prometheus:9090']
- job_name: 'cadvisor'
static_configs:
- targets: ['cadvisor:8080']
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
容器指标说明
cAdvisor 提供的主要指标:
| 指标 | 说明 |
|---|---|
container_cpu_usage_seconds_total | CPU 使用时间 |
container_memory_usage_bytes | 内存使用量 |
container_network_receive_bytes_total | 网络接收字节 |
container_network_transmit_bytes_total | 网络发送字节 |
container_fs_usage_bytes | 文件系统使用量 |
container_last_seen | 容器最后可见时间 |
常用 PromQL 查询:
# 容器 CPU 使用率
rate(container_cpu_usage_seconds_total{name!="",image!=""}[5m]) * 100
# 容器内存使用
container_memory_usage_bytes{name!="",image!=""}
# 容器网络流量
rate(container_network_receive_bytes_total[5m])
rate(container_network_transmit_bytes_total[5m])
# 按容器名汇总 CPU
sum(rate(container_cpu_usage_seconds_total{name!="",image!=""}[5m])) by (name)
# 内存使用率最高的容器
topk(5, container_memory_usage_bytes{name!=""})
# 容器数量
count(container_last_seen)
cAdvisor 直接使用
cAdvisor(Container Advisor)可以直接查看容器资源使用:
# 运行 cAdvisor
docker run -d \
--name=cadvisor \
-p 8080:8080 \
-v /:/rootfs:ro \
-v /var/run:/var/run:ro \
-v /sys:/sys:ro \
-v /var/lib/docker/:/var/lib/docker:ro \
gcr.io/cadvisor/cadvisor:latest
# 访问 Web UI
# http://localhost:8080
故障排除
容器启动失败
检查容器状态:
# 查看所有容器(包括已停止的)
docker ps -a
# 查看容器退出原因
docker inspect --format='{{.State.ExitCode}}' nginx
docker inspect --format='{{.State.Error}}' nginx
docker inspect --format='{{.State.OOMKilled}}' nginx
# 查看容器日志
docker logs nginx
常见退出码:
| 退出码 | 说明 |
|---|---|
| 0 | 正常退出 |
| 1 | 应用错误 |
| 137 | 被 SIGKILL 杀死(可能是 OOM) |
| 139 | 段错误(Segmentation Fault) |
| 143 | 被 SIGTERM 终止 |
进入容器调试
# 执行单个命令
docker exec nginx ls /app
# 进入交互终端
docker exec -it nginx bash
# 使用 sh(如果没有 bash)
docker exec -it nginx sh
# 以 root 用户进入
docker exec -it -u root nginx bash
# 指定工作目录
docker exec -it -w /app nginx bash
# 传递环境变量
docker exec -it -e DEBUG=true nginx bash
网络调试
# 查看容器网络配置
docker network inspect bridge
# 查看容器端口映射
docker port nginx
# 进入容器测试网络
docker exec -it nginx sh
# 在容器内执行
ping db
curl http://api:3000/health
nslookup db
# 使用专用网络调试容器
docker run --rm -it --network container:nginx nicolaka/netshoot
# 在调试容器中可以使用各种网络工具
dig db
curl -v http://localhost:80
tcpdump -i eth0
存储调试
# 查看容器挂载
docker inspect --format='{{json .Mounts}}' nginx | jq
# 查看数据卷
docker volume ls
docker volume inspect my-volume
# 查看容器磁盘使用
docker system df
# 查看容器文件系统变更
docker diff nginx
# 从容器复制文件出来
docker cp nginx:/app/logs ./logs
性能调试
# 实时监控资源
docker stats nginx
# 查看容器进程
docker top nginx
# 使用 perf 分析(需要特权)
docker run --rm -it --privileged --pid=container:nginx \
-v /lib/modules:/lib/modules \
nicolaka/netshoot perf top
# 使用 strace 跟踪系统调用
docker run --rm -it --privileged --pid=container:nginx \
nicolaka/netshoot strace -p 1
健康检查调试
# 查看健康检查状态
docker inspect --format='{{json .State.Health}}' nginx | jq
# 查看健康检查日志
docker inspect --format='{{range .State.Health.Log}}{{.Output}}{{end}}' nginx
# 手动执行健康检查命令
docker exec nginx curl -f http://localhost/health
实用调试工具
网络调试容器
# 创建包含各种网络工具的容器
docker run --rm -it --network my-network nicolaka/netshoot
# 常用命令
dig db # DNS 查询
curl -v http://api:3000 # HTTP 请求
nc -zv db 3306 # 端口测试
tcpdump -i eth0 # 抓包
ss -tuln # 查看端口
ip addr # 网络接口
存储调试容器
# 挂载数据卷进行调试
docker run --rm -it -v my-volume:/data alpine sh
# 在容器中查看数据
ls -la /data
cat /data/config.yml
容器资源限制调试
# 检查容器资源限制
docker inspect --format='Memory: {{.HostConfig.Memory}}, CPU: {{.HostConfig.NanoCpus}}' nginx
# 测试内存限制
docker run --rm -it --memory="100m" progrium/stress --vm 1 --vm-bytes 150M
# 测试 CPU 限制
docker run --rm -it --cpus="0.5" progrium/stress --cpu 4
监控告警
Prometheus 告警规则
# alert.rules.yml
groups:
- name: docker_alerts
rules:
- alert: ContainerDown
expr: absent(container_last_seen{name=~".+"})
for: 5m
labels:
severity: critical
annotations:
summary: "Container {{ $labels.name }} is down"
- alert: HighMemoryUsage
expr: container_memory_usage_bytes{name!=""} / container_spec_memory_limit_bytes{name!=""} * 100 > 90
for: 5m
labels:
severity: warning
annotations:
summary: "Container {{ $labels.name }} memory usage > 90%"
- alert: HighCPUUsage
expr: rate(container_cpu_usage_seconds_total{name!=""}[5m]) * 100 > 80
for: 5m
labels:
severity: warning
annotations:
summary: "Container {{ $labels.name }} CPU usage > 80%"
Alertmanager 配置
# alertmanager.yml
global:
smtp_smarthost: 'smtp.example.com:587'
smtp_from: '[email protected]'
smtp_auth_username: '[email protected]'
smtp_auth_password: 'password'
route:
receiver: 'team-email'
group_wait: 30s
group_interval: 5m
repeat_interval: 1h
receivers:
- name: 'team-email'
email_configs:
- to: '[email protected]'
可视化面板
Grafana Dashboard
推荐导入以下 Dashboard:
| Dashboard ID | 说明 |
|---|---|
| 179 | Docker Host and Container Monitoring |
| 11600 | Docker Container |
| 893 | Docker and system monitoring |
| 11467 | Docker Swarm monitoring |
自定义 Grafana 面板
容器 CPU 使用率面板:
# Query
sum(rate(container_cpu_usage_seconds_total{name=~"$container"}[5m])) by (name) * 100
# Legend
{{ name }}
容器内存使用面板:
# Query
container_memory_usage_bytes{name=~"$container"}
# Legend
{{ name }}
故障排除清单
容器无法启动
# 1. 检查日志
docker logs <container>
# 2. 检查状态
docker inspect <container>
# 3. 检查镜像
docker images
docker history <image>
# 4. 尝试交互运行
docker run -it --entrypoint sh <image>
# 5. 检查资源限制
docker stats --no-stream
容器性能问题
# 1. 查看资源使用
docker stats <container>
# 2. 检查进程
docker top <container>
# 3. 检查日志
docker logs --tail 1000 <container>
# 4. 进入容器检查
docker exec -it <container> sh
# 5. 使用性能工具
docker run --rm -it --pid=container:<container> nicolaka/netshoot top
网络连接问题
# 1. 检查网络配置
docker network inspect <network>
# 2. 检查容器 IP
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' <container>
# 3. 测试连通性
docker exec <container> ping <target>
# 4. 检查端口
docker port <container>
docker exec <container> ss -tuln
# 5. 检查 DNS
docker exec <container> nslookup <hostname>
小结
本章我们学习了:
- 内置监控命令:docker stats、docker top、docker events
- 日志管理:日志驱动、日志聚合方案
- Prometheus 监控:部署、指标、告警
- 故障排除:启动失败、网络问题、存储问题
- 调试工具:网络调试、性能调试
- 可视化面板:Grafana Dashboard 配置
练习
- 使用 docker stats 监控容器资源使用
- 配置日志驱动限制日志大小
- 部署 Prometheus + Grafana 监控栈
- 创建 Grafana 面板展示容器 CPU 和内存使用率
- 配置 Prometheus 告警规则,当容器内存使用超过 90% 时告警