3、资源监控与基准测试:用数据说话,别靠感觉

说实话,我见过太多团队在容器资源规划上翻车了。要么给多了浪费钱,要么给少了频繁 OOM。你想想看,没有数据支撑的规划,不就是拍脑袋吗?

这一章,我们就来聊聊怎么用监控工具和压力测试,摸清你应用的「真实胃口」。我个人习惯把这步叫做「摸底考试」——考过了,你才知道该给多少资源。

3.1 实时监控三板斧:docker stats、cAdvisor、Prometheus

监控工具很多,但真正在生产环境里用得顺手的,其实就那么几个。我按使用场景给你排个序:

3.1.1 docker stats:快速上手,适合临时排查

这是 Docker 自带的命令,不需要额外安装。我经常在排查问题时先用它瞄一眼:

# 查看所有运行中容器的实时资源
docker stats

# 只看某个容器
docker stats <container_name>

# 输出为 JSON 格式,方便脚本处理
docker stats --no-stream --format "{{json .}}"

输出里能看到 CPU 百分比、内存使用、网络 I/O、磁盘 I/O。嗯,这里要注意:docker stats 显示的是瞬时值,不是平均值。你盯着看 5 秒和看 5 分钟,结果可能差很多。

避坑指南: 我曾经在生产环境用 docker stats 看一个 Java 应用,发现内存才用了 200MB,就放心地把 limit 设成了 512MB。结果业务高峰期直接 OOM——因为 JVM 的堆内存是动态分配的,低负载时根本看不出来。所以,docker stats 只能当「快照」,不能当「全貌」。

3.1.2 cAdvisor:容器级别的「体检报告」

cAdvisor 是 Google 开源的容器监控工具。它比 docker stats 强在哪?说白了,它能给你历史数据、图表、以及更细粒度的指标。

部署起来很简单:

docker run \
  --volume=/:/rootfs:ro \
  --volume=/var/run:/var/run:ro \
  --volume=/sys:/sys:ro \
  --volume=/var/lib/docker/:/var/lib/docker:ro \
  --volume=/dev/disk/:/dev/disk:ro \
  --publish=8080:8080 \
  --detach=true \
  --name=cadvisor \
  --privileged \
  --device=/dev/kmsg \
  gcr.io/cadvisor/cadvisor:latest

启动后访问 http://localhost:8080,就能看到每个容器的 CPU、内存、网络、文件系统使用情况。我个人习惯用它的「历史趋势图」来判断资源峰值——这比 docker stats 靠谱多了。

小技巧: cAdvisor 默认只保留 2 分钟的数据。如果你要做长期分析,记得配置存储后端(比如 InfluxDB 或 Prometheus)。我在项目中吃过这个亏,查历史数据时发现全丢了,气得我...嗯,后来老老实实配了 Prometheus。

3.1.3 Prometheus + Grafana:生产级监控方案

这才是真正的「大杀器」。Prometheus 负责采集和存储,Grafana 负责可视化。我建议你直接上这套组合拳:

# prometheus.yml 配置示例
scrape_configs:
  - job_name: 'cadvisor'
    static_configs:
      - targets: ['cadvisor:8080']
    metrics_path: '/metrics'
    scrape_interval: 15s

  - job_name: 'node_exporter'
    static_configs:
      - targets: ['node_exporter:9100']
    scrape_interval: 15s

用 docker-compose 一键部署:

version: '3'
services:
  prometheus:
    image: prom/prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"

  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin

有了 Prometheus,你可以查任意时间段的资源使用情况。比如:

# 查询过去 24 小时容器的 CPU 使用率(平均值)
rate(container_cpu_usage_seconds_total{name="my-app"}[5m])

# 查询内存使用峰值
max_over_time(container_memory_usage_bytes{name="my-app"}[24h])

为什么我推荐 Prometheus?因为它能帮你回答三个关键问题:

  • 峰值是多少?——决定资源上限
  • 平均值是多少?——决定资源预留
  • 波动有多大?——决定是否需要弹性伸缩

3.2 压力测试:给应用「上强度」

监控只能看到「日常表现」,但你要知道应用的极限在哪。这就得靠压力测试了。我一般用两个工具:stresssysbench

3.2.1 stress:简单粗暴的 CPU/内存压测

stress 是个轻量级工具,适合快速验证容器的资源限制是否生效:

# 模拟 4 个 CPU 核满载 60 秒
stress --cpu 4 --timeout 60

# 模拟 2 个 CPU 核 + 512MB 内存压力
stress --cpu 2 --vm 1 --vm-bytes 512M --timeout 30

在容器里跑:

docker run --rm --cpus="2" --memory="1g" \
  polinux/stress stress --cpu 2 --vm 1 --vm-bytes 800M --timeout 60

然后打开 cAdvisor 或 Prometheus,观察 CPU 和内存曲线。你会发现:

  • CPU 限制为 2 核时,即使 stress 请求 4 核,实际使用也不会超过 200%
  • 内存超过 800MB 时,容器会被 OOM Kill
核心结论: 压力测试不是为了「跑满资源」,而是为了验证你的资源限制配置是否正确。我曾经遇到过——明明设置了 --memory=1g,结果容器还是用了 1.5GB——后来发现是没设置 --memory-swap,导致 swap 把限制绕过去了。

3.2.2 sysbench:更专业的基准测试

sysbench 能测试 CPU、内存、磁盘 I/O、数据库等。我一般用它来做「基准线」测试:

# CPU 基准测试(计算素数)
sysbench cpu --cpu-max-prime=20000 run

# 内存基准测试
sysbench memory --memory-block-size=1M --memory-total-size=10G run

# 磁盘 I/O 测试
sysbench fileio --file-total-size=10G prepare
sysbench fileio --file-total-size=10G --file-test-mode=rndrw run
sysbench fileio --file-total-size=10G cleanup

在容器里跑,对比不同资源限制下的性能差异:

# 测试 1 核 vs 2 核的 CPU 性能
docker run --rm --cpus="1" someimage sysbench cpu run
docker run --rm --cpus="2" someimage sysbench cpu run

你会看到类似这样的结果:

CPU 限制 事件数(越高越好) 延迟(越低越好)
1 核 10,000 10.02ms
2 核 19,800 5.05ms
4 核 39,100 2.56ms

看到没?2 核的性能几乎是 1 核的两倍,但 4 核并没有翻倍——因为应用本身可能无法充分利用多核。这就是为什么我强调「不要盲目给资源」。

3.3 确定真实资源需求:三步法

好了,监控有了,压测也做了。怎么确定最终值?我总结了一个三步法:

  1. 收集日常峰值:用 Prometheus 查过去 7 天的 CPU 和内存峰值(去掉异常值)
  2. 压测极限值:用 stress/sysbench 模拟业务高峰,记录资源使用上限
  3. 加 20% 余量:在峰值基础上增加 20% 作为 requests,再加 50% 作为 limits

举个例子:

# 假设压测结果
CPU 峰值: 1.2 核
内存峰值: 512 MB

# 最终配置
resources:
  requests:
    cpu: "1.5"    # 1.2 * 1.2 ≈ 1.44,向上取整
    memory: "640Mi"  # 512 * 1.25 = 640
  limits:
    cpu: "2"      # 1.2 * 1.5 = 1.8,向上取整
    memory: "1Gi" # 512 * 2 = 1024
我的经验: requests 设得太低,调度时会出问题;设得太高,集群利用率低。我一般建议 requests 设为「日常平均值的 1.2 倍」,limits 设为「压测峰值的 1.5 倍」。这样既保证稳定性,又不浪费资源。

最后说一句:资源规划不是一劳永逸的事。应用在迭代,流量在变化,你的资源配比也得跟着调整。我建议每季度做一次「资源复盘」,用 Prometheus 拉数据,看看有没有「资源过剩」或「资源不足」的容器。

嗯,这一章的内容就到这。下一章我们聊聊怎么用 Kubernetes 的 HPA 和 VPA 做自动伸缩——那才是真正的「降本增效」利器。