6月6日 20:23

Docker 容器资源怎么监控?从 docker stats 到 Prometheus 的完整方案

容器跑着跑着内存爆了、CPU 拉满了、磁盘写满了——这些问题的根源都能通过资源监控提前发现。Docker 提供了从命令行到 API 多层级的资源监控手段,这篇按从简单到复杂的顺序讲清楚。

docker stats:最快上手

bash
# 查看所有运行中容器的资源使用 docker stats # 只看特定容器 docker stats myapp # 只输出一次(不刷新) docker stats --no-stream # 只看特定指标 docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"

输出示例:

shell
NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O myapp 12.5% 256MiB / 4GiB 6.25% 1.2MB / 340kB 45MB / 0B redis 0.3% 8MiB / 512MiB 1.56% 56kB / 32kB 0B / 0B

MEM USAGE / LIMIT 最关键——如果 Usage 接近 Limit,说明容器快 OOM 了。BLOCK I/O 高说明磁盘读写频繁,可能是日志写入太多或数据库查询没走缓存。

局限docker stats 只能看实时数据,没有历史趋势。要看趋势得上 Prometheus + Grafana。

设置资源限制:监控的前提

监控资源使用的目的是发现问题,而限制资源是防止问题蔓延:

yaml
# docker-compose.yml services: app: image: myapp:latest deploy: resources: limits: cpus: '2.0' # 最多用 2 核 memory: 4G # 内存上限 4GB reservations: cpus: '0.5' # 保底 0.5 核 memory: 1G # 保底 1GB
bash
# 命令行方式 docker run -d --name myapp \ --cpus=2.0 \ --memory=4g \ --memory-swap=4g \ # 禁止 swap myapp:latest

--memory-swap=4g 等于 --memory 的值意味着容器不能用 swap——全部用物理内存。OOM 时容器会被直接杀掉而不是被 swap 拖慢。

CPU 限制的两种方式

bash
# 1. --cpus:限制 CPU 时间占比(推荐) docker run --cpus=1.5 myapp # 最多用 1.5 核 # 2. --cpu-period + --cpu-quota:更精细的控制 docker run --cpu-period=100000 --cpu-quota=50000 myapp # quota/period = 50000/100000 = 0.5 核

日常用 --cpus 就够了。--cpu-period/--cpu-quota 只在需要非整数的精确控制时用。

cgroups:底层数据源

Docker 的资源限制和监控都基于 Linux cgroups。直接读 cgroups 文件可以拿到最原始的数据:

bash
# 容器的内存使用(字节) cat /sys/fs/cgroup/memory/docker/<container-id>/memory.usage_in_bytes # 容器的 CPU 使用(纳秒) cat /sys/fs/cgroup/cpu/docker/<container-id>/cpuacct.usage # 内存限制 cat /sys/fs/cgroup/memory/docker/<container-id>/memory.limit_in_bytes

一般不需要直接读 cgroups——docker stats 和 Prometheus 已经做了封装。但如果容器内的监控工具和宿主机的 docker stats 数据对不上,可能是 cgroups 版本差异(cgroups v1 vs v2)导致的。

Docker API:程序化监控

bash
# 获取容器资源使用统计(JSON 格式) curl --unix-socket /var/run/docker.sock \ http://localhost/containers/<container-id>/stats?stream=false

返回的 JSON 包含 CPU、内存、网络、磁盘 IO 的详细数据。适合自建监控脚本:

python
import docker client = docker.from_env() for container in client.containers.list(): stats = container.stats(stream=False) cpu_delta = stats['cpu_stats']['cpu_usage']['total_usage'] - \ stats['precpu_stats']['cpu_usage']['total_usage'] system_delta = stats['cpu_stats']['system_cpu_usage'] - \ stats['precpu_stats']['system_cpu_usage'] cpu_percent = (cpu_delta / system_delta) * 100 if system_delta > 0 else 0 mem_usage = stats['memory_stats']['usage'] / 1024 / 1024 mem_limit = stats['memory_stats']['limit'] / 1024 / 1024 print(f"{container.name}: CPU={cpu_percent:.1f}%, MEM={mem_usage:.0f}/{mem_limit:.0f}MB")

cAdvisor + Prometheus:生产级监控

docker stats 和 API 只适合临时查看。需要历史趋势和告警时,用 cAdvisor 采集 + Prometheus 存储 + Grafana 展示:

yaml
services: 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" prometheus: image: prom/prometheus:latest volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml ports: - "9090:9090"

cAdvisor 自动采集所有容器的 CPU、内存、网络、磁盘指标,Prometheus 每 15 秒拉取一次。Grafana 里导入 Dashboard ID 11600 即可看到完整的容器资源监控面板。

OOM 处理:内存爆了怎么办

容器因内存不足被杀时,docker inspect 会记录原因:

bash
docker inspect <container-id> --format '{{.State.OOMKilled}}' # true 说明是 OOM 杀的

处理步骤:

  1. 查看内存限制是否太小——docker stats 看 MEM USAGE 是否经常顶到 LIMIT
  2. 分析内存泄漏——进入容器 docker exec -it <id> sh,用 topps aux --sort=-%mem 找内存大户
  3. 临时解决:加大内存限制 docker update --memory=8g <container-id>
  4. 根本解决:修复应用的内存泄漏

监控方案选择

场景方案成本
临时排查docker stats零成本
自建脚本监控Docker API + Python
小团队日常监控cAdvisor + Prometheus + Grafana
生产环境完整监控栈 + Alertmanager

起步建议:先用 docker stats 日常看一眼,发现问题了再搭 Prometheus 长期监控。

标签:Docker