资讯动态

深度学习GPU监控实战:从nvidia-smi到Prometheus+Grafana告警体系

发布时间:2026/10/21 22:07:12 来源:尧图企业网站定制
跑深度学习训练的人几乎每天都要跟 NVIDIA GPU 打交道。训练崩了、显存爆了、利用率上不去说来说去都是同一个问题一开始没人把 GPU 监控当回事。我自己早几年刚接触服务器的时候只会敲 nvidia-smi 看一眼显存还剩多少用完就完事。后来开始调多卡训练、排查 OOM、搭可视化监控才发现 GPU 的状态远比表面看到的要复杂。这篇文章会把我在实际环境里验证过的监控方法完整梳理一遍从最基础的 nvidia-smi 命令到长期日志、可视化告警再到训练场景下容易踩的几个坑。刚入门的同学建议从第 1 节开始看有一定基础的同学可以直接跳到第 4、5 节那里有不少我踩过之后才总结出来的经验。1. 先从最基础的 nvidia-smi 说起1.1 一条命令看懂 GPU 的“体检报告”输入 nvidia-smi你会在终端里看到一张大表。很多人习惯只看右上角的显存占用但这一屏信息其实非常密集。顶部写着驱动版本和 CUDA 版本每次环境排查我都会先确认这两个对得上号下面每张卡单独一行从左到右依次是 GPU 型号、当前显存用量、计算利用率Util%、温度、风扇转速、功耗、性能状态Pstate以及底部列出的正在运行的进程。这里面的门道在于利用率并不能完整反映卡在忙什么。一类常见场景是 GPU Util 显示接近 100%但功耗只有几十瓦这说明计算单元一直在发起大量小 kernel计算效率不高反过来功耗拉满但利用率上不去多半是显存带宽或数据传输卡住了。所以我建议练成一个习惯看状态时把利用率、功耗、温度三列同时参看而不是只盯着一个数字。单独看利用率很容易产生“跑得还不错”的错觉真正排查性能问题时这三个字段缺一不可。1.2 把输出变成脚本能解析的格式nvidia-smi 支持用 --query 指定字段输出这比人工读表靠谱得多。我最常用的一条是nvidia-smi --query-gpuindex,temperature.gpu,utilization.gpu,memory.used,memory.total,power.draw --formatcsv,noheader,nounits这里有几个容易忽略的参数csv 表示输出逗号分隔noheader 表示不带表头方便拼接时间戳nounits 会去掉百分号、瓦特这类单位得到的全是纯数字用 awk 或者 Python 处理都不用清洗。如果想把输出接到告警脚本里可以直接用 awk 按列提取比如取第三列温度做判断写成awk -F, $285 {print}就是一个最简单的温度告警雏形。想查看有哪些字段可以传入运行 nvidia-smi --help-query-gpu列表非常长像风扇转速、显存总数、PCIe 吞吐、电源功耗上限都包含在内。查看正在占用 GPU 的进程则用nvidia-smi --query-compute-appspid,process_name,used_memory --formatcsv,noheader配合 pid 再去看这个进程到底在做什么基本就能定位是谁在吃卡。这里补充一个我踩过的坑第一次调用 nvidia-smi 偶尔会有半秒延迟这是驱动初始化导致的写在高频采样脚本里时别把间隔设得太小10 秒一次足够否则脚本自身也会占用不少 CPU 和 IO。1.3 容易被忽略的 Pstate、持续模式和 MIGPstate 是显卡当前工作状态的编号范围从 0 到 12数字越大越接近闲置。看到 P8、P12 说明显卡基本在“待机”不用担心功耗如果长期在 P0 附近则说明负载一直很重。这个字段平时容易被忽视但在排查“显卡功耗高但利用率低”时非常有用两个数字一对比就能看出是不是在做大量小指令调度。还有两类情况新手第一次看到常常会慌。一类是打开了 MIG 模式的卡nvidia-smi 显示的显存总容量会明显变小因为整张卡被切片成多个实例这时所有查询要加上 --id 指定具体实例另一类是把驱动程序的“持久化模式”关闭了每次调用 nvidia-smi 都得重新初始化响应明显变慢。前者主要是多用户共享资源时的配置后者建议在服务器上通过nvidia-smi --persistence-mode1打开尤其是有频繁监控需求的环境这样连续查询的延迟会稳定很多。2. 实时刷新与后台日志把监控变成长期任务2.1 watch 命令调试时最快的刷新方式需要盯住当前状态变化时我最常用的是watch -n 1 nvidia-sminvidia-smi 默认只打印一次加 watch 后每秒钟刷新一次温度、利用率、显存变化都能实时看到。加-d参数还能高亮两次刷新之间有变化的部分观察显存抖动时特别有用。这个命令适合调试程序、观察 OOM 快要发生时的显存走势。它的局限也很明显关掉终端就没有任何历史记录而且只能看到当前状态前面发生过的情况全部被冲掉。所以它定位是“临时工具”不能作为长期监控方案。真要把监控留痕还得往日志方向走。2.2 一个简单的采样脚本把状态写进日志想要留痕最直接的方式是写一个后台脚本定时把 GPU 状态追加到 CSV 文件里。我最初用的版本是这样#!/bin/bash while true; do ts$(date %Y-%m-%d %H:%M:%S) status$(nvidia-smi --query-gpuindex,utilization.gpu,memory.used,temperature.gpu --formatcsv,noheader,nounits | paste -sd |) echo $ts,$status /var/log/gpu_monitor.csv sleep 30 done用 nohup 拉起来之后它就会每 30 秒记录一次。这里有几个细节采样间隔不建议低于 10 秒否则监控脚本自己会占用额外 IO 和 CPU日志文件要配合 logrotate 定期归档避免跑几个月后把磁盘塞满如果要写更复杂的判断逻辑比如温度超过阈值就写一条 WARN建议改成 Python 脚本流程上更容易维护。启动方式也很常规nohup /usr/local/bin/gpu_status.sh /dev/null 21 或者直接放入 crontab每分钟执行一次再把输出追加到同一个 CSV。这个方案没有告警能力但作为“事后回溯”的数据来源已经够用。真想做告警和面板展示光靠 CSV 不够后面那套可视化方案才是完整的答案。2.3 nvtopSSH 进去一眼看清全盘状态长时间在终端里看 GPU我强烈推荐 nvtop它对标的体验完全是 htop。安装好之后直接运行 nvtop界面会展示每张卡的利用率、显存、温度、功耗的实时曲线底部还能列出当前占用 GPU 的进程按 CPU、内存排序也都能操作。整个界面会覆盖终端当前屏幕退出按 q 就行。用它的经验是在排查“为什么这轮训练 GPU 利用率掉到 0”时开着 nvtop 同时再开另一个终端跑数据加载测试能够很快定位是不是 CPU 预处理把数据喂慢了。类似工具还有 gpustat但在交互体验和信息密度上nvtop 更接近 htop我个人更建议先装 nvtop。这个工具虽然看起来只是把 nvidia-smi 的数据可视化但信息密度高很多用完基本回不去了。3. 真正可以告警的可视化监控Prometheus Grafana3.1 这套组合是怎么分工的如果你想做到“温度超过 85 度就告警、显存不足自动通知、训练跑到一半能回看历史曲线”那就必须上可视化监控了。我选的是行业内验证过很多次的组合一个采集器进程负责把 GPU 状态转成 HTTP 接口Prometheus 定时去拉取这些指标并存储成时序数据Grafana 负责从 Prometheus 读取数据画成面板并执行告警规则。为什么选这套而不是自己写脚本我自己以前也觉得写个 shell 脚本够了但真正遇到多节点、多卡、需要历史回溯的场景脚本就力不从心了一是历史查询没有现成索引二是告警阈值改起来要改代码三是和 CPU、内存、网络监控没法统一在一张图里。Prometheus 这套体系天然就是为长期运行、多维查询、规则告警设计的之前只用来监控过 CPU 的话这次加一个 GPU exporter 就能把空白补上。社区里有大量现成的 exporter 实现选择基于 NVIDIA 官方工具暴露指标的那种即可避免去维护一套自己解析 nvidia-smi 输出的私有协议。3.2 从零部署一次采集、存储、展示先说采集器。如果熟悉容器操作最省事的方式是跑一个 exporter 容器docker run -d --name gpu-exporter \ --restart unless-stopped \ --gpus all \ -p 9835:9835 \ 社区或官方维护的 GPU exporter 镜像这里有个容易踩的坑容器必须通过--gpus all显式把 GPU 设备传进去否则 exporter 读不到任何显卡指标日志里全是 devices not found。启动后可以用curl localhost:9835/metrics验证能看到大量以 gpu_ 开头的指标就说明采集端正常。Prometheus 端只需要在配置文件里加一个 jobscrape_configs: - job_name: gpu scrape_interval: 15s static_configs: - targets: [localhost:9835]重启 Prometheus 后在 Targets 页面如果看到 UP 状态采集链路就通了。Grafana 这边推荐的做法先把 Prometheus 填入数据源然后导入社区现成的 GPU 监控仪表盘模板导入后一般就能看到温度、利用率、显存、功耗这些面板。如果想自己画图最简单的查询是avg(gpu 利用率字段)并按时间范围展示配置好单位和图例即可。我自己第一次搭这套时最耗时间的就是找对指标字段名所以建议先导入模板再从模板里逆向学习指标关系会比从零建面板快很多。告警配置在 Grafana 的 Alerting 栏里通过规则统一管理也可以直接写在 Prometheus 的 rules 文件中。下面是一个示例实际部署时把指标名替换成你采集到的字段groups: - name: gpu_alerts rules: - alert: GPUHighTemperature expr: 你的温度指标 85 for: 5m labels: severity: warning annotations: summary: GPU {{ $labels.gpu }} 温度过高3.3 一开始就设定好的三个告警阈值结合我实际运营服务器的经验建议至少先做三类告警。告警项建议阈值触发说明GPU 温度85℃持续 5 分钟温度长时间过高容易引起降频和硬件老化显存剩余低于 10% 或不足某阈值防止生产任务 OOM尤其要注意推理节点利用率异常10 分钟内持续为 0 且有训练进程训练可能已卡死或数据线程异常第一是温度告警消费级卡长期超过 85 度会影响寿命建议阈值设在 85持续 5 分钟以上再触发避免瞬时波动误报。第二是显存告警手动设置“剩余显存低于 10%”或“可用显存不足某数值”这个在生产环境的推理任务里尤其重要不然出现 OOM 的时候都是事后才知道。第三是“利用率长期为 0 但进程还在”的异常告警这类情况多半意味着训练卡死或数据加载线程挂掉了没有这个规则你可能第二天早上才发现训练停了半夜。还可以考虑功耗和风扇转速但前三条覆盖了绝大多数日常问题。初期不要设置太多规则否则告警疲劳反而会让人忽略真正的问题。4. 深度学习训练场景下的监控心得4.1 最隐蔽的陷阱显存碎片导致的 OOM在 PyTorch 这类框架里跑训练监控最常遇到的现象是训练开了一两个小时后中途突然报 CUDA out of memory重启同一份代码又能继续跑。这种不是单纯因为显存不够更多是显存碎片造成的——框架默认的显存缓存分配器会在反复分配和释放中留下大量不连续的小块最终导致即使总剩余显存足够也分配不出连续的大块。建议先用 nvidia-smi 看两层数据一层是整卡显存容量和使用量另一层是进程自身统计的显存占用两者的差值往往就是缓存碎片。框架层面常见的缓解办法是开启扩展内存段支持在启动训练前设置export PYTORCH_CUDA_ALLOC_CONFexpandable_segments:True结合监控去验证时你会发现开启后显存曲线比之前稳定不少。TensorFlow 场景也有类似限制显存增长、保留最小碎片空间的选项。这个坑最值得记住的是遇到“偶尔 OOM”先别急着加显存优先怀疑碎片。4.2 多卡利用率一并打到 100% 并没有那么简单训练时打开 nvidia-smi常见画面是四块卡里利用率在 0 到 100 之间来回抖动或者某些卡长期落后。这种不均匀最常见的原因不在 GPU 而在数据供给链路DataLoader 的 num_workers 不够、CPU 预处理太慢、磁盘 IO 跟不上都会让 GPU 在等待中空转。想验证的话我会同时开一个终端看 CPU 使用率和 nvtop如果 CPU 在那一刻已经打满大概率就是喂数据喂不上的问题。更细的定位可以用nvidia-smi dmon它按秒刷新能看到 Utilization 列的瞬时波动再用工具查看网络吞吐能区分是在等数据还是在等梯度同步。多机训练里梯度同步的等待经常被误认为 GPU 故障其实看 Power 和 Utilization 就会明白训练并没有停止只是每轮之间有一段同步空窗。这种问题监控面板看平均值往往看不出来必须看秒级曲线所以我一直强调日志采样间隔不能太大。4.3 在训练日志里埋点GPU 指标和 loss 放一起看如果要长期优化训练建议把 GPU 监控和训练本身的指标挂到同一个观测面板上。最简单的方式是写一小段采集代码在验证循环里读取当前显存、温度和利用率import pynvml pynvml.nvmlInit() handle pynvml.nvmlDeviceGetHandleByIndex(0) def read_gpu_stats(): mem pynvml.nvmlDeviceGetMemoryInfo(handle) util pynvml.nvmlDeviceGetUtilizationRates(handle) temp pynvml.nvmlDeviceGetTemperature(handle, pynvml.NVML_TEMPERATURE_GPU) return { gpu_mem_used: mem.used // 1024 ** 2, gpu_mem_total: mem.total // 1024 ** 2, gpu_util: util.gpu, gpu_temp: temp, }把这个函数的返回值配合每个迭代的 loss 一起写入 TensorBoard 或 CSV后面复盘时就能看到“loss 开始震荡的那一刻GPU 温度是不是已经接近 90 度”这种问题。比单独看监控面板更直观的一点是它可以把 GPU 状态和训练进度对齐到同一条时间线上而不用手动去对两张图的时间戳。训练结束前记得调用 pynvml.nvmlShutdown() 释放资源不然下次初始化可能会报警告。再有条件的话把每轮的 batch 耗时也记录下来和 GPU 利用率一起观察就能快速判断瓶颈在前后向计算还是数据加载。5. 常见问题速查与避坑5.1 GPU 利用率很低显存却占了很高这种情况我遇到最多的是两类原因。一类是数据加载瓶颈GPU 在等 CPU 把样本喂进来此时显存里已经缓存了部分 batch但计算单元闲得慌另一类是模型结构里存在大量串行小算子导致计算单元没有被充分利用。前者把 DataLoader 的 num_workers 调大、用 prefetch 机制预取数据观察是否有改善后者需要用 profiler 工具分析热点算子看能不能把串行操作改成 batch 化。判断到底是哪一种最快的方法是看 CPU 利用率CPU 被打满多半是数据问题CPU 不高但 GPU 利用率愣是上不去就要往算子执行效率方向查。5.2 温度过高导致显卡降频Pstate 会直观反映降频。如果发现温度上升后 Util 反而下降看 Pstate 是不是从 P0 退到了更高的数字功耗上限也被压低那基本可以确认触发了温度保护。除了清灰、改善机箱风道之外临时手段可以用nvidia-smi -pl手动限制功耗上限把温度压在一个稳定区间。要注意这个设置会改变整卡的性能边界只建议在确实散热不足的机器上使用。还有一个容易被忽略的细节是显卡与显卡之间的间距有些服务器为了节省空间把卡插得太密中间几块卡的温度往往比两端高出不少看监控的时候如果只有中间卡报警优先检查物理散热。5.3 nvidia-smi 输出异常或显示 “N/A”驱动问题、MIG 模式、容器权限都可能导致输出不对。常见的是更新驱动后内核模块没有加载成功先用lsmod | grep nvidia确认模块状态再加载对应的 nvidia 相关模块。容器里出现权限问题时往往需要在启动时传入设备访问权限或者直接使用 NVIDIA 容器工具包让容器正确看到 GPU 设备。另外MIG 开启后某些字段在整卡维度上是“N/A”的需要按照实例 ID 查询别急着怀疑驱动坏了。这类问题排查时先分清楚是“物理卡看不到”还是“部分字段读不到”前者考虑驱动和设备直通后者基本就是查询维度选错了。5.4 显存泄漏跑多久显存就涨多久长时间运行后显存慢慢涨满通常是缓存没释放。排查思路是分阶段盯曲线先跑固定的若干轮看显存是不是每轮都稳定上涨然后在代码里先注释掉验证、再注释掉数据加载逐步二分定位到底哪一段在涨。PyTorch 下还要小心 DataLoader 的 worker 数量设置以及在循环里创建的临时变量没有及时清除该加 del 加 del该清缓存清缓存。这里尤其推荐把第 2 节的后台采样脚本先跑起来因为显存泄漏一定是缓慢的、跨几个小时的没有长期日志帮忙拉曲线靠肉眼很难判断什么时候开始异常。我处理过的一个案例就是靠对比日志发现某个验证函数在每次调用后多保留了整个 batch 的梯度定位后一行代码就解决了。我自己现在服务器上保留的最终方案其实很固定日常排查用 nvtop长期稳定性交给 Prometheus 加 Grafana训练日志里埋一段 pynvml 采样。这套组合每个环节都用在自己最合适的位置不算复杂但非常实用。如果你现在还停留在“出问题时才敲一下 nvidia-smi”的阶段我建议先把第 1 节的查询参数练熟再加个后台采样脚本养成随时有数据可查的习惯。GPU 监控这件事最难的不是工具而是先有“它值得被持续记录”的意识一旦这个习惯建立起来很多诡异的问题都能在真正出事之前被监控曲线提前拦住。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑