GPU 掉卡告警怎么做(一):count<8 和 XID 失灵,以及'冻住不动'的误报坑

一句话:给 GPU 集群做"掉卡"告警,我先想用"卡数变少"和"报错码"来发现坏卡,结果都失灵;改用"指标冻住不动"来判断,又被"其实是没采到数据"坑了误报;最后发现把测试环境排掉,告警就干净了。

下面讲清这三步。文中 IP、Pod 名均为占位。

环境

  • 每节点 8 张 GPU,跑在 k8s 上;
  • dcgm-exporter 采集 GPU 指标 → Prometheus;
  • Nightingale(n9e)按 PromQL 判定 + 飞书告警。

"掉卡"指:某张 GPU 挂死、业务用不了它,但节点本身还是正常的。

一、最直觉的两个办法,都没用

想法 1:坏卡会从拓扑消失,数节点卡数 < 8

实际数不出来,原因得从故障类型说起。这次掉卡是 Xid 119/154(GSP 固件超时)。按 NVIDIA/云厂商文档,这类故障里 GPU 并没有从 PCIe 总线掉下去——它在 nvidia-smi 里还在,只是显示 ERR!(排障时实测 gpu7 正是 ERR! 40C,DCGM 采到的温度也冻在 40,对得上)。

卡既然还在驱动的设备列表里,dcgm-exporter 就照样把它当一张在册卡、继续查询并导出它的指标序列(读到的是卡死的最后值,所以每分钟都有新采样点、只是值冻住不变)。而 count(...) by (Hostname) 数的就是"这台还有几条 GPU 序列",8 条一条没少 → 照样数到 8。

要让 count 真掉到 7,得卡从 PCIe 总线上彻底消失才行——比如另一种掉卡 Xid 79(GPU fell off the bus),驱动访问不到它、序列才可能没掉。这也正好解释了为什么不同故障类型、不同集群,表现会不一样。

想法 2:看 XID_ERRORS 报没报错误码。
实际:这次掉卡是 GSP 固件超时,DCGM 根本读不到设备,错误码计数器停在 0——内核日志里明明有 Xid 119/154,但 DCGM 没采到。

注:不是所有掉卡都这样。另一套集群的掉卡是 Xid 79(掉总线),那种 DCGM 能采到,可以直接用 XID_ERRORS == 79 即时报。能不能靠 XID,取决于具体的故障类型,得拿真实掉卡数据验证,别想当然。

二、换个思路:卡挂了,指标会"冻住不动"

卡挂死时,它的功耗、温度、时钟会冻在最后一个值不动;而活卡哪怕空闲,这三个数也一定在小幅抖动。

所以判定式就是:这三个指标在过去一段时间里标准差都为 0(完全没变化):

stddev_over_time(功耗[15m]) == 0
and stddev_over_time(温度[15m]) == 0
and stddev_over_time(时钟[15m]) == 0

为什么要三个一起?单看一个会误判(空闲卡某个数偶尔也会平一阵);三个一起冻死才是真挂了。

三、坑:"冻住不动",可能是真冻,也可能是没数据

上线前回测,发现有张健康卡被报了"冻结 15 分钟"。把它的原始数据逐分钟打出来一看:

11:38   功耗23W  温度33   <- 有数据,空闲正常
11:44   功耗21W  温度33
11:45   缺       缺        <- 从这里开始,全没数据了!
...     (一直缺)

卡根本没冻,是从 11:45 起没采到数据了。 窗口里只剩前面那两个相同的空闲值,标准差当然是 0 → 被误判成"冻结"。

这就是这条规则最大的坑:标准差为 0(值没变化)有两种完全不同的原因——

  • 真冻卡:每分钟都采到、每次都是同一个值(数据密);
  • 没数据:采集断了,窗口里就剩一两个残留值碰巧相同(数据疏)。

两者都让"标准差=0",但只有第一种是真掉卡。

再查"为什么没数据":不是采集程序挂了,而是那张卡上的 Pod 被重新调度走了——旧 Pod 走之前是空闲的,它最后那几个不变的值,被规则误当成了"冻结"。

四、真正的解法:只盯生产环境

那个制造假冻结的 Pod,是测试(test)命名空间的。这就对上了——测试环境的 Pod 频繁重启、调度,生产环境稳定。

于是给判定式加一个条件:只看生产命名空间(namespace="prod"),把 test 排除掉。重测结果:

  • 生产卡 15 天里零误报(健康卡再没出现过长时间"冻结");
  • 真掉卡照样能抓到(真掉卡会冻好几个小时)。

一刀就干净了。比起在判定式里堆各种花哨条件,"按命名空间排掉测试环境"才是对症的那一刀

小坑:有的集群用 kube-prometheus-stack,真实命名空间标签会被改名成 exported_namespace(namespace 变成了 kube-system),那种要写 exported_namespace="prod"

五、别纠结告警快几分钟

还有个常被纠结的问题:告警多久能报出来?这条规则的延迟,主要不是"持续确认时间(for)"决定的,而是判定式里那个 [15m] 窗口:

  • 卡必须先"冻满 15 分钟",标准差才可能为 0、规则才可能触发——这是绕不开的下限;
  • for 只是再叠加几分钟防抖。for=1分钟 总延迟约 16 分钟,for=5分钟 约 20 分钟,就差几分钟

而掉卡是小时级故障(卡一挂就趴好几个小时),你第 16 分钟报还是第 20 分钟报,处理上没区别。所以这条规则要的是"准、不漏",不是"快",别纠结 for 设多小。真想更快,只能把 [15m] 缩短,但窗口越短越容易误判,得不偿失。

小结

  1. GPU 掉卡别只靠一个信号:count<8XID、"冻住不动"各有盲区,必须拿真实掉卡数据回测,别想当然;
  2. "指标没变化"有两种原因:真冻卡(数据密) vs 没采到数据(数据疏)——排查时一定要分清,把原始采样点打出来看缺没缺最直接;
  3. 冻卡类规则最大的误报源是测试环境 Pod 频繁调度,用 namespace="prod" 排掉它,告警立刻干净。

快速参考

判定式(只盯生产、排除测试噪声)

(stddev_over_time(DCGM_FI_DEV_POWER_USAGE{namespace="prod"}[15m]) == 0)
and (stddev_over_time(DCGM_FI_DEV_GPU_TEMP{namespace="prod"}[15m])   == 0)
and (stddev_over_time(DCGM_FI_DEV_SM_CLOCK{namespace="prod"}[15m])   == 0)
# kube-prometheus-stack 的集群改用 exported_namespace="prod"
# for 取 3~5 分钟即可;能采到 Xid 79 的集群另配 XID_ERRORS==79 即时报

排查口诀

  • "指标没变化"先分清:真冻卡(数据密) or 没数据(数据疏)——把原始采样点逐分钟打出来看缺没缺;
  • 冻卡规则的主要误报源是测试环境 Pod 调度,用 namespace="prod" 排掉;
  • 延迟由 [15m] 窗口决定,不是 for;小时级故障别过度压延迟。
posted @ 2026-06-08 16:38  Hello_worlds  阅读(11)  评论(0)    收藏  举报