一台 8 卡 A100 的训练节点,nvidia-smi 显示显存占满 99%,但训练吞吐只有理论值的 30%;另一台机器上训练任务频繁重启,日志里只有一句 “CUDA error: unspecified launch failure”,没有任何业务异常。这两类问题,传统的 CPU 内存、网络 IO 指标一个都答不上来——加速器的故障模式、指标语义与归因维度都和 CPU 完全不同。
本文讲清楚三件事:GPU 指标从哪来、怎么读(DCGM 与 NVML 的指标语义)、坏了怎么定位(XID 错误码与 ECC/NVLink 健康度)、以及一个多卡多任务节点上「这块卡到底是谁在用、为什么慢」的归因方法。同时给出可直接落地的采集架构与告警规则。
1. GPU 为什么需要专门的可观测性
1.1 CPU 指标套不到 GPU 上
把 GPU 当成「一块更快的 CPU」来监控,几乎必然踩坑。差异体现在三个层面:
语义差异:
CPU 利用率 = 非空闲时间占比,接近线性可解释
GPU "利用率" 至少有三个口径:SM 活跃、显存带宽、Tensor Core 占用
它们可以同时很高或同时很低,单看一个数字会得出相反结论
故障差异:
CPU 故障多数表现为进程崩溃或 ECC 内存报错
GPU 故障表现为 XID 错误、NVLink 降速、显存不可纠正 ECC、
甚至"静默降频"——任务不报错但变慢
归因差异:
一个进程独占一个 CPU 核心是常态
一张 GPU 上可能同时跑多个进程、多个容器、多个 MIG 实例
不解决归因,"这块卡很忙"就无法回答"该找谁"
1.2 三层指标来源
GPU 可观测性的数据来自三个层次,覆盖范围和开销各不相同:
| 层次 | 接口 | 能拿到什么 | 特点 |
|---|---|---|---|
| 驱动层 | NVML(C 库) | 利用率、显存、温度、功耗、ECC、XID | 最全,需进程直连驱动 |
| 导出层 | dcgm-exporter | 把 DCGM 字段转成 Prometheus 指标 | 事实标准,带 GPU 标签 |
| 应用层 | CUDA/NVML Profiling | kernel 耗时、SM occupancy、显存分配栈 | 精细但开销大,按需开启 |
生产环境的默认组合是 DCGM(Data Center GPU Manager)+ dcgm-exporter + Prometheus:DCGM 负责与驱动和硬件对话(含健康检查与诊断),exporter 负责暴露成时序指标。应用层 profiling 只在排查具体 kernel 问题时临时开启。
1.3 与通用方法论的衔接
GPU 属于典型的「资源」,可以直接套用 RED/USE 方法论
中的 USE 三件套:Utilization(利用率)、Saturation(饱和度)、Errors(错误)。GPU 的饱和度不看队列长度,而看「是否有任务在等卡」(NVIDIA 侧的 nvidia-smi 排队、Kubernetes 侧的 Pending Pod、调度器的等待时长);错误则看 XID 与 ECC。
2. DCGM 指标体系
2.1 核心指标字段
DCGM 用一套 Field ID 命名空间组织指标,dcgm-exporter 会转成 DCGM_FI_* 形式的 Prometheus 指标。最常用的几类:
| Prometheus 指标 | 含义 | 单位 | 常见误读 |
|---|---|---|---|
DCGM_FI_DEV_GPU_UTIL | SM 活跃时间占比 | % | 高不等于算得满 |
DCGM_FI_DEV_MEM_COPY_UTIL | 显存控制器忙占比 | % | 与 GPU_UTIL 独立 |
DCGM_FI_DEV_FB_USED | 已用帧缓冲 | MiB | 包含缓存,不等于有效数据 |
DCGM_FI_DEV_FB_FREE | 空闲帧缓冲 | MiB | 碎片化时"有空间却分配失败" |
DCGM_FI_DEV_POWER_USAGE | 板卡功耗 | W | 撞到 TDP 会触发降频 |
DCGM_FI_DEV_GPU_TEMP | 核心温度 | ℃ | 超过阈值触发硬件降频 |
DCGM_FI_DEV_SM_CLOCK | SM 实际频率 | MHz | 与 Max Clock 对比看降频 |
DCGM_FI_DEV_ECC_DBE_VOL_TOTAL | 不可纠正 ECC 累计 | 次 | 非 0 即需介入 |
DCGM_FI_DEV_XID_ERRORS | 最近一次 XID 码 | 码值 | 是"最近值"不是计数器 |
2.2 利用率与显存的正确读法
DCGM_FI_DEV_GPU_UTIL 的定义是「在过去采样周期内,至少有一个 kernel 在执行的时间占比」。这意味着两件事:一是它是时间维度的占比,不反映并行度;二是采样周期越短,抖动越大。
典型误判一:GPU_UTIL 98% 但吞吐低
原因:kernel 之间存在大量串行同步与空洞,或者 kernel 本身访存受限
佐证:同时看 MEM_COPY_UTIL 与 SM_CLOCK,若带宽打满则是访存瓶颈
典型误判二:GPU_UTIL 30% 但任务"很慢"
原因:数据加载(DataLoader)在 CPU 侧成为瓶颈,GPU 在等数据
佐证:看进程的 CPU 使用率与磁盘/网络 IO
典型误判三:显存 OOM 但 FB_FREE 不为 0
原因:PyTorch 缓存分配器碎片化,或 MIG 实例限额
佐证:看 FB_USED 的锯齿曲线与 MIG 配置
显存指标要区分「进程视角」与「设备视角」。DCGM_FI_DEV_FB_USED 是整卡视角(含所有进程与 CUDA context 开销);要归因到具体进程,需要 nvidia-smi --query-compute-apps 或 DCGM 的进程级字段。
2.3 dcgm-exporter 部署要点
在 Kubernetes 上以 DaemonSet 方式部署,关键是让每个 Pod 的指标带上 gpu、GPU_I_ID(MIG 实例)、Hostname、pod、namespace 标签:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: dcgm-exporter
namespace: gpu-observability
spec:
selector:
matchLabels:
app: dcgm-exporter
template:
metadata:
labels:
app: dcgm-exporter
spec:
nodeSelector:
nvidia.com/gpu.present: "true"
tolerations:
- key: nvidia.com/gpu
operator: Exists
effect: NoSchedule
containers:
- name: dcgm-exporter
image: nvcr.io/nvidia/k8s/dcgm-exporter:3.3.5-3.4.0-ubuntu22.04
args:
- -f
- /etc/dcgm-exporter/counters.csv
- --kubernetes=true
- --collect-interval=15000
ports:
- name: metrics
containerPort: 9400
securityContext:
privileged: false
capabilities:
add: ["SYS_ADMIN"]
volumeMounts:
- name: dev
mountPath: /dev
volumes:
- name: dev
hostPath:
path: /dev
几个容易忽略的参数:
--collect-interval 采集周期,默认 30000ms;训练场景建议 10~15s
--kubernetes=true 附加 Pod/Namespace 标签,需 RBAC 读 Pods
--mig-format 多实例 GPU 的标签格式,决定能否按 MIG 归因
-f counters.csv 白名单式选字段,避免暴露全部 200+ 字段导致基数膨胀
⚠️ 注意:
dcgm-exporter默认暴露全部字段时,单节点指标量可达数千条。生产环境务必用counters.csv只保留真正要告警和绘图的字段,并关闭无用的DCGM_FI_PROF_*(Profiling 字段需要额外开启且开销较高)。
3. XID 错误与硬件故障定位
3.1 XID 码表
XID(NVIDIA Xid)是驱动在检测到 GPU 异常时写入内核日志的错误码。它不是「累计计数」,而是「最近一次事件的码值」,需要配合 dmesg 或 journald 才能拿到完整历史。
| XID | 含义 | 严重程度 | 处置 |
|---|---|---|---|
| 13 | Graphics Engine Exception | 中 | 多为应用代码问题,可重试 |
| 31 | GPU memory page fault | 中 | 越界访问,查应用 |
| 43 | GPU stopped processing | 高 | 该进程上下文已损坏,需重启进程 |
| 48 | Double Bit ECC Error | 极高 | 显存不可纠正错误,换卡 |
| 63 | ECC page retirement 记录 | 中 | 有页被退休,关注累计趋势 |
| 74 | NVLink 错误 | 高 | 检查 NVSwitch/线缆,可能降速 |
| 79 | GPU has fallen off the bus | 极高 | 掉卡,需重启节点/换卡 |
| 94 | Contained ECC error | 高 | 单卡隔离,容器被终止 |
| 119 | GSP RPC timeout | 高 | 固件/驱动异常,升级驱动 |
| 120 | GSP error | 高 | 同上 |
3.2 采集与告警
dcgm-exporter 暴露的 DCGM_FI_DEV_XID_ERRORS 只反映最近一次 XID,更可靠的来源是内核日志。推荐双路采集:
# 方式一:从 dmesg / journald 抓 NVRM 行,转成日志指标
journalctl -k -f | grep -E 'NVRM: Xid'
# 输出样例:
# NVRM: Xid (PCI:0000:3b:00): 79, pid=12345, GPU has fallen off the bus.
# 方式二:nvidia-smi 查询 XID 与健康状态
nvidia-smi --query-gpu=index,pci.bus_id,xid.errors,retired_pages.pending \
--format=csv
# 方式三:DCGM 主动诊断(可离线跑,用于送修前取证)
dcgmi diag -r 3 -g 0
Prometheus 侧的告警规则可以这样写:
groups:
- name: gpu-health
rules:
- alert: GPUUncorrectableECC
expr: increase(DCGM_FI_DEV_ECC_DBE_VOL_TOTAL[1h]) > 0
labels:
severity: critical
annotations:
summary: "GPU {{ $labels.gpu }} 出现不可纠正 ECC 错误"
runbook: "确认 XID=48/94,隔离该卡并申请更换"
- alert: GPUXidError
expr: DCGM_FI_DEV_XID_ERRORS != 0
for: 1m
labels:
severity: warning
annotations:
summary: "GPU {{ $labels.gpu }} XID={{ $value }},检查 dmesg NVRM 行"
- alert: GPUThrottling
expr: DCGM_FI_DEV_SM_CLOCK < DCGM_FI_DEV_SM_CLOCK_MAX * 0.7
for: 10m
labels:
severity: warning
annotations:
summary: "GPU {{ $labels.gpu }} 降频超过 30%,检查温度与功耗"
3.3 从 XID 到根因的判断路径
XID 48/94(不可纠正 ECC)
→ 立即隔离该卡:kubectl cordon 节点,或把该 GPU 从资源池摘除
→ dcgmi diag -r 3 留证
→ 更换硬件,不要"再观察一下"
XID 79(掉卡)
→ 通常是 PCIe 链路或供电问题
→ 检查 dmesg 是否有 link down / AER 报错
→ 重启节点后若复现,换卡或换槽位
XID 74(NVLink)
→ nvidia-smi nvlink -s 查看各链路状态与错误计数
→ 多机训练场景下,NVLink 降速会直接拖垮 AllReduce
4. 训练与推理任务的 GPU 归因
4.1 归因难在哪
一张 A100 上可能同时有:系统侧的 CUDA context、多个训练容器的进程、监控/推理小任务。FB_USED 是整卡汇总值,直接看它无法回答「谁占的」。归因需要在指标上挂业务标签,路径有两条:容器标签(Pod/Namespace)和 MIG 实例标签。
4.2 Kubernetes 场景
dcgm-exporter 通过 kubelet 的 Pod 资源与 Device Plugin 的分配信息,把 pod、namespace、container 标签附加到指标上。前提是 RBAC 允许读 Pods,且 GPU 由 nvidia-device-plugin 分配:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: dcgm-exporter-reader
rules:
- apiGroups: [""]
resources: ["pods", "nodes", "services", "endpoints"]
verbs: ["get", "list", "watch"]
归因查询示例——按 Pod 看显存占用与利用率:
# 每个训练 Pod 的显存占用(MiB)
sort_desc(
sum by (namespace, pod) (DCGM_FI_DEV_FB_USED)
)
# 找出"占着卡但没干活"的 Pod:显存高、SM 利用率低
(
sum by (pod) (DCGM_FI_DEV_FB_USED) > 20000
)
and on (pod)
(
avg by (pod) (DCGM_FI_DEV_GPU_UTIL) < 5
)
第二条查询是排障中最实用的模式:显存占用高但利用率长期接近 0,通常是进程卡死、死锁,或者模型加载后进入了长时间的数据预处理而没启动训练。
4.3 MIG 场景
MIG(Multi-Instance GPU)把一张卡切成多个独立实例,每个实例有独立的 SM 与显存。此时:
指标区分:
DCGM_FI_DEV_GPU_UTIL 仍是整卡视角,会把多个实例混在一起
必须按 GPU_I_ID(实例 ID)与 GPU_I_PROFILE(如 3g.40gb)分组
显存指标在 MIG 下按实例上报,可精确归因
注意:
MIG 实例的 SM 数固定,利用率上限受实例规格限制
混部不同规格实例时,跨实例比较利用率没有意义
4.4 与业务指标关联
GPU 指标要和业务侧打通才有诊断价值。推理服务的吞吐、TTFT、排队时长属于 LLM 服务可观测性
的范畴,把它的指标与 GPU 指标按 pod 对齐后,可以回答「吞吐下降是因为模型变慢,还是因为卡被抢占」:
关联维度:pod / node / gpu
典型问题:vLLM 的 prefill 延迟上升
若 GPU_UTIL 同时下降 → 上游请求变少或数据侧阻塞
若 GPU_UTIL 上升但吞吐下降 → 可能是显存不足触发重算,或降频
若 SM_CLOCK 下降 → 先查温度与功耗,再看业务
在 Kubernetes 上调度 GPU 工作负载时,节点亲和、拓扑(NVLink 域)与 MIG 策略本身也是可观测性的一部分,可参考 Kubernetes AI/ML GPU 专题 中的调度与资源治理章节。
5. 采集架构与开销控制
5.1 采集方式对比
| 方式 | 粒度 | 开销 | 适用 |
|---|---|---|---|
nvidia-smi 轮询 | 整卡 | 低但进程开销明显 | 临时排查 |
| NVML 直连 | 整卡 + 进程 | 低 | 自研 agent |
| dcgm-exporter | 整卡 + 进程 + MIG | 低(默认字段) | 生产标准 |
| DCGM Profiling 字段 | kernel 级 | 中高 | 性能调优 |
nsys / ncu | kernel 级 | 高 | 单次深挖 |
ℹ️ 核心:
nvidia-smi每次调用都会与驱动通信,在高频轮询(如每秒)下本身就会成为可观的开销。生产采集一律走常驻的 exporter,不要用脚本循环调nvidia-smi。
5.2 频率与基数
采集频率:
15s —— 告警与容量规划(推荐默认)
1s —— 短期性能分析,仅临时开启
60s —— 长期趋势,可降低远端存储压力
基数控制:
每卡约 20 个字段 × 8 卡 × 数百节点 = 数万时序
加上 pod/namespace 标签后基数会放大
策略:counters.csv 白名单 + 关闭 DCGM_FI_PROF_*
长期存储用远端写,参考 Prometheus 长期存储方案
6. 告警、容量与成本
6.1 该告的与不该告的
必告:
ECC 不可纠正错误 > 0
XID 出现在高危码表(48/74/79/94/119/120)
温度 > 85℃ 持续 5 分钟
降频(SM_CLOCK / MAX_CLOCK < 0.7)持续 10 分钟
掉卡(GPU 数量少于期望值)
不该告:
GPU_UTIL 高 —— 卡忙是正常状态,不是故障
FB_USED 高 —— 显存占满在设计内
单次 XID 13 —— 多为应用问题,交业务侧处理
6.2 容量与成本
GPU 是数据中心里最贵的资源,利用率与成本的关联非常直接。闲置的 GPU 与低效的 GPU 是两种不同的浪费,前者靠调度解决,后者靠监控发现:
# GPU 利用率长期低于 20% 的卡(闲置浪费)
avg_over_time(DCGM_FI_DEV_GPU_UTIL[7d]) < 20
# 显存分配率高但利用率低(低效使用)
avg_over_time(DCGM_FI_DEV_FB_USED[7d])
/ avg_over_time(DCGM_FI_DEV_FB_TOTAL[7d]) > 0.8
and
avg_over_time(DCGM_FI_DEV_GPU_UTIL[7d]) < 30
把 GPU 时数折算成成本,与业务收益对齐,属于可观测性向财务侧延伸的部分,可参考 可观测性成本智能 的思路:指标采集的粒度要与决策粒度匹配——如果决策是「这个团队该不该再申请 8 张卡」,那么按团队聚合的周级利用率就够,不需要每卡每秒的原始数据。
7. 常见误读与最佳实践
| 误读 | 真实情况 | 正确做法 |
|---|---|---|
| GPU_UTIL 高就是好 | 高利用率也可能在空转同步 | 结合吞吐与 MEM_COPY_UTIL |
| 显存没满就没问题 | 碎片化可导致分配失败 | 看分配失败计数与锯齿曲线 |
| 没有 XID 就是健康 | 静默降频不产生 XID | 对比实际频率与最大频率 |
| 单卡指标能代表整机 | 多卡故障具有局部性 | 按 gpu 标签逐卡告警 |
| MIG 下看整卡利用率 | 多实例混在一起无法解释 | 按 GPU_I_ID 分组 |
□ 用 dcgm-exporter DaemonSet 采集,白名单字段而非全量
□ 指标必须带 gpu / pod / namespace / GPU_I_ID 标签
□ ECC 不可纠正错误与高危 XID 一律 critical 告警
□ 单独监控降频(实际频率 / 最大频率)
□ 显存与利用率交叉查询,识别"占卡不干活"
□ XID 历史从 journald 采集,不只依赖 exporter 的最近值
□ 采集频率 15s 起步,不要用脚本高频调 nvidia-smi
□ GPU 指标与业务指标按 pod 对齐,形成归因闭环
小结
GPU 可观测性的核心矛盾是「整卡视角的指标」与「多租户、多进程、多实例的归因需求」之间的错位。解决路径有三步:一是选对指标来源(DCGM + dcgm-exporter,白名单控制基数);二是读对语义(利用率是时间占比而非并行度,显存是整卡视角而非进程视角);三是挂对标签(Pod/Namespace/MIG 实例),让每一条指标都能回答「是谁」。故障侧则以 XID 与 ECC 为纲:不可纠正 ECC 与高危 XID 必须立即隔离硬件,不要"再观察"。最后,GPU 是最贵的资源,把利用率与成本、业务吞吐对齐,监控才从"看健康"升级为"看效益"。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。