《容器化AI应用性能瓶颈排查实战》

4、性能监控体系:Prometheus、Grafana、cAdvisor、GPU监控(DCGM)

4.1 监控体系的核心原理:从指标采集到可视化闭环

在容器化AI应用性能瓶颈排查中,监控体系解决的是“黑盒”问题——没有监控,你无法判断瓶颈究竟出在CPU调度、内存分配、磁盘I/O、网络延迟还是GPU算力耗尽。一套完整的监控体系由四个层次构成:指标采集层(cAdvisor、DCGM-Exporter)、时序存储层(Prometheus)、可视化层(Grafana)和告警层(Alertmanager)。其核心原理是:采集器以固定时间间隔(通常15秒)抓取目标暴露的/metrics端点,将带标签的样本数据(如 container_cpu_usage_seconds_total)推送到Prometheus TSDB,再由Grafana通过PromQL查询渲染出趋势图。

对于AI负载,监控体系需要额外关注GPU维度的指标——显存占用率、SM(流式多处理器)利用率、GPU温度、显存带宽、PCIe传输速率等。这些指标无法由cAdvisor直接获取,必须依赖NVIDIA DCGM(Data Center GPU Manager)暴露的细粒度GPU遥测数据。

关键认知:CPU监控看“饱和度”,GPU监控看“利用率+显存水位”。CPU饱和通常表现为运行队列长度持续超过核数,而GPU瓶颈往往出现在SM利用率高但显存带宽未饱和(计算受限),或显存占用接近上限但SM利用率低(显存受限)。
容器化AI应用监控体系架构 采集层 cAdvisor 容器CPU/内存/网络/磁盘 /metrics 端口:8080 DCGM-Exporter GPU SM/显存/温度/带宽 /metrics 端口:9400 Node-Exporter 宿主机CPU/内存/磁盘I/O /metrics 端口:9100 自定义 业务指标 /metrics 存储与查询层 Prometheus Server TSDB · PromQL · 抓取任务 · 告警规则 可视化与告警层 Grafana Dashboard CPU/GPU/内存/网络面板 Alertmanager 告警路由 · 通知 钉钉/邮件 通知渠道 抓取间隔: 15s | 数据保留: 15天 | 标签: pod_name, container_name, gpu_uuid 核心指标: container_cpu_usage_seconds_total, DCGM_FI_DEV_GPU_UTIL, DCGM_FI_DEV_FB_USED

4.2 部署方法流程:四步搭建完整监控栈

以下以Kubernetes环境为例,展示最小化部署流程。假设已有NVIDIA GPU节点并安装了nvidia-device-plugin。

步骤1:部署Prometheus Stack(含cAdvisor)

# 使用kube-prometheus-stack Helm Chart
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

helm install monitoring prometheus-community/kube-prometheus-stack \
  --namespace monitoring --create-namespace \
  --set prometheus.prometheusSpec.scrapeInterval=15s \
  --set grafana.enabled=true

cAdvisor已内置于kubelet中,Prometheus通过kubelet服务发现自动抓取/metrics/cadvisor端点,无需单独部署。

步骤2:部署DCGM-Exporter

# 使用NVIDIA官方Helm Chart
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update

helm install dcgm-exporter nvidia/dcgm-exporter \
  --namespace monitoring \
  --set serviceMonitor.enabled=true \
  --set serviceMonitor.interval=15s

验证GPU指标是否进入Prometheus:

kubectl port-forward -n monitoring svc/prometheus-operated 9090:9090
# 浏览器访问 http://localhost:9090,查询 DCGM_FI_DEV_GPU_UTIL

步骤3:配置Grafana数据源与导入仪表盘

# 获取Grafana管理员密码
kubectl get secret -n monitoring monitoring-grafana -o jsonpath="{.data.admin-password}" | base64 -d

# 端口转发
kubectl port-forward -n monitoring svc/monitoring-grafana 3000:80

在Grafana中:

  • 添加Prometheus数据源,URL填 http://prometheus-operated:9090
  • 导入社区仪表盘ID:14282(Kubernetes集群视图)、14513(NVIDIA DCGM GPU监控)

步骤4:配置关键告警规则

# prometheus-additional.yaml
groups:
- name: gpu-alerts
  rules:
  - alert: GPUSMUtilizationHigh
    expr: DCGM_FI_DEV_GPU_UTIL > 95
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "GPU SM利用率超过95% (GPU {{ $labels.gpu }})"
  - alert: GPUMemoryAlmostFull
    expr: DCGM_FI_DEV_FB_USED / DCGM_FI_DEV_FB_TOTAL > 0.9
    for: 10m
    labels:
      severity: critical
    annotations:
      summary: "GPU显存占用超过90% (GPU {{ $labels.gpu }})"

4.3 实战案例:定位训练任务GPU显存泄漏

场景描述:某PyTorch分布式训练任务运行约6小时后,出现CUDA out of memory错误,但任务重启后又能正常运行一段时间。

排查过程:

  1. 打开Grafana DCGM仪表盘,查看DCGM_FI_DEV_FB_USED(显存已用)时间序列,发现显存占用呈阶梯式上升,每30分钟增加约2GB,而非平稳波动。
  2. 查看DCGM_FI_DEV_GPU_UTIL(SM利用率),发现利用率正常(85%左右),排除计算瓶颈。
  3. 进一步查看container_memory_working_set_bytes(容器工作集内存),发现与显存上升趋势同步,但宿主内存充足。
  4. 结合代码审查,定位到问题:数据加载器(DataLoader)中pin_memory=True且num_workers=8,每个worker在每次迭代时未释放tensor.cuda()的临时变量,导致显存碎片化累积。

解决方案:在训练循环中增加torch.cuda.empty_cache()调用,并将num_workers降为4,显存曲线恢复平稳。

误区提醒:不要仅看GPU利用率而忽略显存趋势。SM利用率高但显存缓慢增长,往往是显存泄漏的典型信号;反之,显存占用高但SM利用率低,则可能是批大小过大或数据加载瓶颈。

4.4 监控指标速查表

指标名称 来源 含义 瓶颈阈值参考
container_cpu_usage_seconds_total cAdvisor 容器累计CPU时间 rate(5m) > 0.8 * 核数
container_memory_working_set_bytes cAdvisor 容器工作集内存 接近limit时告警
DCGM_FI_DEV_GPU_UTIL DCGM GPU SM利用率(%) > 95% 持续5分钟
DCGM_FI_DEV_FB_USED DCGM 显存已用(字节) > 90% 总量
DCGM_FI_DEV_GPU_TEMP DCGM GPU温度(℃) > 85℃ 持续10分钟
DCGM_FI_DEV_PCIE_RX_BYTES DCGM PCIe接收速率 接近理论带宽时关注

4.5 常见误区与复盘

  • 误区一:只监控GPU不监控CPU。AI训练中CPU负责数据预处理和分布式通信,CPU瓶颈会导致GPU空转(SM利用率低但任务耗时增加)。应同时观察container_cpu_usage与DCGM_FI_DEV_GPU_UTIL的比值。
  • 误区二:抓取间隔设置过短。1秒抓取间隔会产生大量时序数据,消耗存储和查询性能。15秒对绝大多数AI场景足够,如需更细粒度可对特定指标单独设置。
  • 误区三:忽略标签设计。Prometheus标签(如pod_name、gpu_uuid)是查询的索引。部署时务必统一命名规范,否则后续聚合查询会非常痛苦。
  • 误区四:告警阈值一刀切。不同模型(CV、NLP、推荐系统)的GPU利用率基线差异很大。建议先收集一周基线数据,再设定动态阈值。