AI 工作负载可观测性:DCGM + Prometheus + Grafana

AI 加速器系列 · 第 7 篇

GPU 利用率 100%!一切看起来很好。直到你发现 Tensor Core 使用率只有 15%,80% 的时间 GPU 在等 CPU 喂数据和 AllReduce 通信。"GPU 利用率"是 AI 基础设施里最大的谎言。


一、为什么 GPU 监控跟 CPU 监控完全不同

CPU 监控很简单。top 告诉你 CPU 在跑什么,vmstat 告诉你上下文切换是否频繁,iostat 告诉你磁盘是否瓶颈。这些指标的含义是清晰的------CPU 利用率 80% 意味着 CPU 真的在干活,20% 的时间在 idle。

GPU 不是这样。nvidia-smi 显示的 GPU 利用率代表的是"过去一秒内,有多少时间 GPU kernel 在执行"。注意------只要有任何 kernel 正在执行,就计入"忙碌"。这意味着:

erlang 复制代码
一个向量加法 kernel 占着 SM 跑 → GPU 利用率 100%
一个矩阵乘法 kernel 只用 15% 的 Tensor Core 跑 → GPU 利用率 100%
一个 kernel 在等显存数据加载,SM 空转 → GPU 利用率 100%

同样的 100%,实际有效算力可能差 6 倍。所以 GPU 监控必须从四个维度展开:

markdown 复制代码
                    GPU 观测四象限

    计算效率                    显存效率
┌─────────────────┐     ┌─────────────────────┐
│ Tensor Core 占比 │     │ 带宽利用率            │
│ SM 占用率        │     │ 碎片化程度            │
│ FP16/FP8/FP4    │     │ HBM vs SRAM 命中率   │
└─────────────────┘     └─────────────────────┘

    通信效率                    功耗/稳定性
┌─────────────────┐     ┌─────────────────────┐
│ NVLink 带宽      │     │ 实际功耗 vs TDP      │
│ IB/RoCE 吞吐     │     │ 降频次数             │
│ NCCL 耗时占比    │     │ XID 错误            │
└─────────────────┘     └─────────────────────┘

没有哪一个维度单独能说明 GPU 的健康状态------四个维度交叉校验,才能判断训练任务是真的在高效运行还是"假忙碌"。


二、DCGM:NVIDIA 的数据中心 GPU 遥测标准

DCGM(Data Center GPU Manager)是 NVIDIA 官方出品的 GPU 遥测和诊断套件。它不是一个简单的 metrics exporter------而是一套完整的 GPU 生命周期管理系统。

架构

scss 复制代码
┌─────────────────────────────────────────────┐
│                  DCGM 体系                    │
│                                              │
│  ┌───────────┐  ┌───────────┐  ┌──────────┐ │
│  │Host Engine│  │Diagnostics│  │  Policy   │ │
│  │  (采集)    │  │  (诊断)    │  │  Actions  │ │
│  │           │  │           │  │  (动作)    │ │
│  │ 100+ 指标  │  │ Level 1-4 │  │ Taint/迁移 │ │
│  └─────┬─────┘  └─────┬─────┘  └─────┬─────┘ │
│        │              │              │        │
│        └──────────────┼──────────────┘        │
│                       ▼                       │
│            ┌───────────────────┐              │
│            │   NVML Library    │              │
│            │  (底层驱动接口)     │              │
│            └───────────────────┘              │
└─────────────────────────────────────────────┘
  • Host Engine:持久守护进程,通过 NVML(NVIDIA Management Library)每 10ms-1s 采集一次 GPU 遥测数据。包括 SM 利用率、显存带宽、NVLink 吞吐、功耗、温度、PCIe 吞吐、时钟频率、ECC 错误计数等 100+ 个指标。
  • Diagnostics:四级诊断体系------Level 1(快速自检,秒级)、Level 2(中等深度,分钟级)、Level 3(全量压力测试,小时级)、Level 4(长时间浸泡测试,天级)。Level 3 会跑完整的显存带宽测试、计算正确性测试、NVLink 连通性测试。
  • Policy Actions:检测到异常后自动执行的修复动作------GPU 降级、节点 Taint、Pod 驱逐或迁移。

核心指标表

DCGM 暴露的指标分为 12 个 Field Group(字段组)。以下是训练和推理场景最关键的一组:

指标 含义 正常范围 告警阈值
DCGM_FI_DEV_GPU_UTIL GPU kernel 执行时间占比 - 不要单独使用------它与实际效率无关
DCGM_FI_PROF_PIPE_TENSOR_ACTIVE Tensor Core 管道活跃占比 > 50% < 20% 说明模型瓶颈不在计算,在数据/通信
DCGM_FI_PROF_PIPE_FP16_ACTIVE FP16 管道活跃占比 取决于精度 全 FP32 且 Tensor Core 低 → 没开混合精度训练
DCGM_FI_DEV_FB_USED 显存(Framebuffer)已用量 - > 95% → 即将 OOM,考虑减小 batch size
DCGM_FI_DEV_FB_FREE 显存剩余量 - < 4GB → 碎片化风险高
DCGM_FI_PROF_DRAM_ACTIVE HBM 显存读写活跃占比 30-70% > 90% → 显存带宽瓶颈
DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL NVLink 总吞吐 接近理论峰值 < 50% 理论峰值 → NVLink 降速或拓扑异常
DCGM_FI_DEV_POWER_USAGE 实时功耗 接近 TDP(如 A100 400W) 远低于 TDP 且 GPU_UTIL 高 → GPU 在降频运行
DCGM_FI_DEV_PCIE_TX_THROUGHPUT PCIe 发送吞吐 - 持续接近 PCIe 带宽上限 → Host-to-Device 数据搬运瓶颈
DCGM_FI_DEV_XID_ERRORS XID 错误累计计数 严格为 0 > 0 → 立即告警,根据 XID 编号判断严重性
DCGM_FI_DEV_ECC_CURRENT 当前 ECC 错误数 0 > 0 → 显存有可纠正单比特错误,累积可能升级
DCGM_FI_DEV_CLOCK_THROTTLE_REASONS 时钟降频原因编码 0 非零 → GPU 因为功耗/温度/电流限制在降频

XID 错误快速参考

XID(eXternal ID)是 NVIDIA GPU 驱动报告错误的编码。不是所有 XID 都致命,但任何一个 XID > 0 都意味着发生了不该发生的事:

ini 复制代码
XID 48  = 双比特显存错误(Uncorrectable ECC)
          → 硬件故障,GPU 必须更换,数据可能已损坏
XID 79  = GPU 从 PCIe 总线上掉落了("GPU fell off the bus")
          → 驱动崩溃/硬件故障/电源不足,应用进程被 kill
XID 45  = 显存页退役(Page retirement)
          → 部分显存扇区被标记为不可用,显存容量减少
XID 31  = GPU 内部微控制器超时
          → 可能由功耗不足、散热问题、驱动 bug 触发
XID 13  = 图形引擎挂起(Graphics Engine Hang)
          → 在计算密集型训练中较少见,但可能表明加速卡压力过大
XID 61  = ECC 单比特错误超过阈值被报告
          → 显存磨损的早期信号,先于 XID 48 出现

告警策略:XID 48 / XID 79 → 紧急,自动 Taint 节点并通知 SRE;XID 45 → 高危,标记节点为"降级",不再调度新训练任务;XID 31 → 中等,观察是否复现,连续 3 次触发 Taint。


三、DCGM Exporter → Prometheus → Grafana 完整链路

整体架构

bash 复制代码
┌──────────────────────────────────────────────────────────────┐
│  GPU Node                                                     │
│  ┌──────────┐ ┌──────────────────┐                           │
│  │  GPU 0   │ │    DCGM daemon   │                           │
│  │  GPU 1   │◄┼──────────────────┤                           │
│  │  GPU 2   │ │  (Host Engine)    │                           │
│  │  GPU 3   │ └────────┬─────────┘                           │
│  └──────────┘          │ Unix Socket / TCP                    │
│                        ▼                                      │
│  ┌─────────────────────────────────────────┐                 │
│  │         DCGM Exporter (sidecar)          │                 │
│  │  - 从 DCGM Host Engine 拉取遥测数据       │                 │
│  │  - 暴露 :9400/metrics (Prometheus 格式)   │                 │
│  │  - 支持 GPU 级别标签注入                  │                 │
│  └────────────────────┬────────────────────┘                 │
└───────────────────────┼──────────────────────────────────────┘
                        │ scrape (15s interval)
                        ▼
           ┌─────────────────────────┐
           │      Prometheus          │
           │  - 持久化时序数据         │
           │  - PromQL 告警规则引擎   │
           │  - 多集群联邦             │
           └────────────┬────────────┘
                        │ data source
                        ▼
           ┌─────────────────────────┐
           │        Grafana           │
           │  - GPU 集群全局仪表盘     │
           │  - 训练任务钻取视图       │
           │  - NVLink/NCCL 健康面板  │
           └─────────────────────────┘

DCGM Exporter 部署

通过 NVIDIA GPU Operator 统一部署,在 ClusterPolicy CR 中启用 DCGM Exporter:

yaml 复制代码
apiVersion: nvidia.com/v1
kind: ClusterPolicy
metadata:
  name: gpu-cluster-policy
spec:
  dcgmExporter:
    enabled: true
    env:
      - name: DCGM_EXPORTER_LISTEN
        value: ":9400"
      - name: DCGM_EXPORTER_KUBERNETES
        value: "true"            # 自动注入 K8s Pod/Node 标签
      - name: DCGM_EXPORTER_COLLECTORS
        value: "/etc/dcgm-exporter/default-counters.csv"  # 指标白名单
    serviceMonitor:
      enabled: true
      interval: 15s
      # 自动创建 ServiceMonitor CRD,Prometheus Operator 自动发现

自定义指标采集策略。default-counters.csv 包含 60+ 指标,但部分高采样率指标会显著增加 Prometheus 的存储压力。根据场景剪裁:

csv 复制代码
# 训练场景关键指标(低频采样就够,大部分指标 10s-30s 采样)
DCGM_FI_DEV_GPU_UTIL,        gauge, GPU Utilization
DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, Tensor Core Active
DCGM_FI_DEV_FB_USED,         gauge, Framebuffer Used (MB)
DCGM_FI_DEV_FB_FREE,         gauge, Framebuffer Free (MB)
DCGM_FI_PROF_DRAM_ACTIVE,    gauge, DRAM Active
DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL, counter, NVLink Bandwidth Total
DCGM_FI_DEV_POWER_USAGE,     gauge, Power Usage (W)
DCGM_FI_DEV_XID_ERRORS,      counter, XID Errors
DCGM_FI_DEV_CLOCK_THROTTLE_REASONS, gauge, Clock Throttle Reasons
DCGM_FI_DEV_ECC_CURRENT,     counter, ECC Errors
DCGM_FI_DEV_PCIE_TX_THROUGHPUT, counter, PCIe TX Throughput

Prometheus 告警规则

告警规则不是"超阈值就发通知"------需要避开常见误报并关联业务上下文:

yaml 复制代码
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: gpu-alerts
  namespace: monitoring
spec:
  groups:
    # --- 硬件故障(立即响应)---
    - name: gpu-hardware-critical
      rules:
        - alert: GPUXidCriticalError
          expr: increase(DCGM_FI_DEV_XID_ERRORS[5m]) > 0
          for: 1m
          labels:
            severity: critical
            component: gpu
          annotations:
            summary: "GPU {{ $labels.gpu }} XID error detected"
            description: >
              Node {{ $labels.node }}, GPU {{ $labels.gpu }} reported XID error.
              XID values seen: {{ $value }}.
              Common causes: HW failure (XID 48), GPU fell off bus (XID 79).

    # --- 显存告警(即将 OOM)---
    - name: gpu-memory
      rules:
        - alert: GPUMemoryHighUsage
          expr: |
            (DCGM_FI_DEV_FB_USED / (DCGM_FI_DEV_FB_USED + DCGM_FI_DEV_FB_FREE)) > 0.95
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "GPU {{ $labels.gpu }} memory usage > 95%"

    # --- 计算效率告警 ---
    - name: gpu-efficiency
      rules:
        - alert: GPULowTensorCoreUtilization
          expr: |
            DCGM_FI_DEV_GPU_UTIL > 80
            and
            DCGM_FI_PROF_PIPE_TENSOR_ACTIVE < 20
          for: 10m
          labels:
            severity: warning
          annotations:
            summary: "GPU busy but Tensor Core idle"
            description: >
              GPU {{ $labels.gpu }}: GPU_UTIL={{ $labels.DCGM_FI_DEV_GPU_UTIL }}%,
              but Tensor Core only {{ $value }}% active.
              Likely bottlenecked by data loading or communication, not compute.

    # --- 降频检测 ---
    - name: gpu-throttling
      rules:
        - alert: GPUClockThrottling
          expr: DCGM_FI_DEV_CLOCK_THROTTLE_REASONS > 0
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "GPU {{ $labels.gpu }} clock throttling active"
            description: >
              Throttle reason mask: {{ $value }}.
              Check: power limit, thermal limit, or current limit.

Grafana 仪表盘设计

三块仪表盘对应三个关注层级:

第一块:集群全局概览(Cluster Overview)

做"红绿灯"式健康总览------80% 的日常巡检在这一块面板上完成:

scss 复制代码
┌──────────────────────────────────────────────────────────┐
│  GPU Cluster Health                    [Last 1h] [🔴3 ⚠5]│
├───────────────────┬──────────────────┬───────────────────┤
│  GPU Count │ 256  │  Healthy │ 248   │ Degraded │ 8      │
│  XID Errors│ 0    │  Throttle│ 3 GPU │ High Mem │ 12 GPU│
├───────────────────┴──────────────────┴───────────────────┤
│  Top 10 GPU by Tensor Core Util      (line chart)        │
│  Bottom 10 GPU by NVLink BW          (line chart)        │
│  Average Power vs TDP per Node       (gauge)             │
│  FB Memory Distribution Histogram    (heatmap)           │
└──────────────────────────────────────────────────────────┘

第二块:训练任务深钻(Training Job Deep Dive)

training-job 标签筛选,展示单个训练任务的 GPU 效率全貌:

  • MFU 估算:TensorCoreActive / GPUUtil * FP16_Peak_TFLOPS / GPU_Actual_TFLOPS
  • 各 GPU 间的 Tensor Core 利用率方差------方差大说明负载不均衡(DataLoader 问题或网络拓扑不对等)
  • NCCL AllReduce 时间占比:NVLink_BW_Actual / NVLink_BW_Peak 结合 ResNet-50/NCCL 测试基线

第三块:NVLink + NCCL 健康面板

训练任务突然变慢时首先打开的面板:

bash 复制代码
Panel: NVLink Bandwidth per GPU Pair (heatmap)
       GPU0→GPU1: ████████ 850 GB/s (▼ 5% vs peak)
       GPU2→GPU3: ████░░░░ 420 GB/s (▼ 53% vs peak) ← 异常!
       GPU4→GPU5: ████████ 880 GB/s (normal)

Panel: NVLink Error Count (counter, per link)
       → 任何非零值都说明物理链路或线缆有问题

Panel: NCCL Bus Bandwidth Histogram
       → 预期:集中在 ~280 GB/s (A100 SXM)
       → 异常:双峰分布或有 15% 远离均值 → 网络拓扑有非对称链路

Grafana Dashboard JSON 关键片段

仪表盘的变量筛选器让同一块面板服务于所有训练任务:

json 复制代码
{
  "templating": {
    "list": [
      {
        "name": "training_job",
        "label": "Training Job",
        "type": "query",
        "datasource": "Prometheus",
        "query": "label_values(DCGM_FI_DEV_GPU_UTIL, training_job)",
        "multi": false,
        "includeAll": true
      },
      {
        "name": "node",
        "label": "GPU Node",
        "type": "query",
        "datasource": "Prometheus",
        "query": "label_values(DCGM_FI_DEV_GPU_UTIL{training_job=\"$training_job\"}, node)",
        "multi": true
      }
    ]
  },
  "panels": [
    {
      "title": "Tensor Core Utilization (per GPU)",
      "targets": [
        {
          "expr": "DCGM_FI_PROF_PIPE_TENSOR_ACTIVE{training_job=\"$training_job\", node=~\"$node\"}",
          "legendFormat": "{{gpu}}"
        }
      ],
      "thresholds": [
        { "value": 20, "color": "red", "op": "lt" },
        { "value": 50, "color": "yellow", "op": "lt" },
        { "value": 80, "color": "green", "op": "gte" }
      ]
    },
    {
      "title": "NVLink Bandwidth (node-level aggregated)",
      "targets": [
        {
          "expr": "sum by (node) (rate(DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL{node=~\"$node\"}[5m]))",
          "legendFormat": "{{node}}"
        }
      ]
    }
  ]
}

四、SLO/A 监控:AI 服务的生死线

GPU 硬件监控解决的是"卡是不是坏了"的问题,SLO 监控解决的是"服务是不是在正常服务用户"的问题。两者缺一不可------GPU 全绿但用户体感烂的情况太常见了。

推理服务 SLO

大模型推理是一个延迟敏感且成本极高的服务。一次请求可能跨越:

scss 复制代码
用户请求 → API 网关 → 推理服务 → Preprocessing(CPU)
  → Tokenizer → Model Forward(GPU) → KV Cache Lookup
  → Token Generation × N → Detokenizer → Postprocessing
  → API 响应

核心 SLO 指标:

指标 目标 说明
Time to First Token (TTFT) P95 < 500ms 用户感知延迟的关键------"模型是不是卡住了"
Token Generation Latency P95 < 50ms/token 影响流式输出的流畅度
End-to-End Latency P99 < 5s (100 token) 包含预处理+推理+后处理的总延迟
QPS (Queries Per Second) 随 TPS 线性增长 并发能力的直接度量
Token Per Second (TPS) 基准性能的 90%+ 发现 GPU 性能退化
OOM Rate < 0.1% 请求触发 CUDA OOM 的比例------直接故障
Request Timeout Rate < 0.5% 超过 deadline 被取消的请求比例

注意------推理的 TTFT 跟 GPU 时钟频率不是线性关系。GPU 降频 15% 可能导致 TTFT 恶化 80%。原因:TTFT 包含了大量的 kernel launch 开销、CUDA context 切换、KV Cache 分配。GPU 降频直接拉长每个 kernel 的执行时间,这些固定开销的累积效应对首 token 延迟的影响远超对吞吐的影响。所以 GPU 降频监测量的是根因,TTFT 监测量的是用户体验------两者必须对应。

训练 SLO:MFU(Model FLOPS Utilization)

训练任务不看 QPS------看的是有效浮点运算效率:

erlang 复制代码
MFU = 实际达到的 TFLOPS / GPU 理论峰值 TFLOPS

MFU 与 GPU_UTIL 的关键区别:
                                                                      
 GPU 有 312 TFLOPS (A100 FP16)                                        
                                                                      
 ████████████████████████████████████████████  312 TFLOPS (理论峰值)    
 ████████████████████████░░░░░░░░░░░░░░░░░░░  180 TFLOPS (实际有效算力) 
                                                                      
         GPU_UTIL = 98%         ← 几乎所有时间都有 kernel 在跑        
         MFU = 58%              ← 但有效算力只有理论峰值的 58%        
                                                                      
 丢失的 42% 去哪里了?                                                 
   → 15%  等数据从 host 搬到 device (PCIe 瓶颈)                         
   → 12%  AllReduce 梯度同步时 GPU 空转 (NCCL 通信开销)                 
   →  8%  kernel launch 开销 (太多小 kernel,CPU dispatch 跟不上)       
   →  5%  Tensor Core 闲置 (算子没有用 Tensor Core 实现)                
   →  2%  ECC 纠错开销                                                

典型 MFU 基线(GPT 类大模型,大规模分布式训练):

  • MFU > 55%:训练效率优秀
  • MFU 50-55%:正常范围
  • MFU 35-50%:存在明显瓶颈(通信、数据加载或 kernel 优化空间大)
  • MFU < 35%:严重问题,训练成本翻倍

业界标杆:Meta 在 Llama 3 训练中达到 MFU ~38-43%(24k H100 集群),DeepSeek 在 V3 训练中报告 MFU ~38-42%。MFU 随集群规模增大呈递减趋势------这不是优化不到位,而是通信拓扑的物理极限。

OpenTelemetry 在 AI 链路中的角色

GPU metrics 给你"现在 GPU 在干什么",Prometheus + Grafana 给你"过去一段时间的趋势和异常",但排查一次具体的慢请求------"为什么用户 A 在第 15:03 分的请求用了 12 秒"------你需要 OpenTelemetry 的 trace:

yaml 复制代码
Span: InferenceRequest (12.3s)
│
├─ Span: Preprocessing (1.2s)
│   ├─ Tokenizer.encode()     0.8s
│   ├─ Image resize (ViT)     0.3s
│   └─ Tensor to GPU          0.1s
│
├─ Span: Model Forward (10.1s)         ← 瓶颈在这里
│   ├─ KV Cache Query         1.2s
│   ├─ Attention × 32 layers  7.8s      ← Trace 能定位到第几层慢
│   │   ├─ Layer 0-7:         0.4s
│   │   ├─ Layer 8-15:        0.6s
│   │   ├─ Layer 16-23:       2.1s      ← 这一组突然变慢 → 显存换页 or 降频
│   │   └─ Layer 24-31:       4.7s
│   └─ FFN × 32 layers       1.1s
│
└─ Span: Postprocessing (1.0s)
    ├─ Detokenizer.decode()   0.7s
    └─ Response serialization 0.3s

OTLP Exporter 配置示例:

yaml 复制代码
apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
  name: inference-otel
spec:
  config: |
    receivers:
      otlp:
        protocols:
          grpc:
            endpoint: 0.0.0.0:4317
    processors:
      batch:
        timeout: 5s
        send_batch_size: 512
    exporters:
      prometheus:
        endpoint: 0.0.0.0:8889
        # 将 OTel trace 派生出的 RED metrics 推给 Prometheus
      jaeger:
        endpoint: jaeger-collector:14250
        tls:
          insecure: true
    service:
      pipelines:
        traces:
          receivers: [otlp]
          processors: [batch]
          exporters: [jaeger]
        metrics:
          receivers: [otlp]
          processors: [batch]
          exporters: [prometheus]

一句话总结

GPU 利用率是基础设施里最大的谎言------用 Tensor Core 活跃度 + NVLink 带宽 + MFU + TTFT 四维交叉校验,才能区分 GPU 是在高效计算还是在假装忙碌。DCGM 给数据,Prometheus 存数据,Grafana 看数据,OpenTelemetry 追数据,四个组件不是可选的------是 GPU 集群从"能跑"到"可靠"的分界线。


下一篇:NVIDIA NIM------从检查点到生产级推理 API 的最后一公里。怎么把 HuggingFace 上的模型权重变成生产环境里带鉴权、限流、监控、自适应批处理的推理端点,以及 NIM 在 K8s 上的部署全流程。

相关推荐
西门老铁1 小时前
JWT 是什么?三段式结构、签名原理与 6 个安全坑一次讲透
后端·面试
gyx_这个杀手不太冷静2 小时前
高级前端开发职业规划(2026—2035)
前端·面试·agent
qpsj2 小时前
DeepSeek 缓存命中涨价 12 倍:4 个改动,账单回到涨价前
人工智能·llm
一只叫煤球的猫3 小时前
开个新坑,从头开始完整拆解 Spring AI 2.0 的源码
后端·面试·aigc
uncle_ll5 小时前
Llama 3 私有化落地全栈实战:从 Ollama 极速部署到 LLaMA Factory LoRA 定制微调
llm·nlp·llama·ollama·大模型微调
浅念-5 小时前
MySQL 索引底层完整详解|磁盘Page|B+树推导|聚簇非聚簇索引|索引SQL操作
大数据·数据库·b树·sql·mysql·面试·职场和发展
众人皆醒我独醉6 小时前
GPU 集群 IaC:Terraform + Ansible 部署自动化
面试·ansible·gpu
Dr.kangder6 小时前
嵌入式面试总结(二十二)——指针
java·面试·职场和发展·架构·嵌入式
Dr.kangder6 小时前
嵌入式面试总结(二十)——ISA原理
单片机·面试·职场和发展·系统架构·嵌入式