万卡级算力集群 GPU 监控:基于观测云全链路观测实践
本文介绍如何通过观测云监控万卡级算力集群 GPU,与应用性能 Trace、业务日志、基础设施指标在统一平台中实现全栈关联与跨层下钻,真正构建起从硬件到业务的完整观测链路。
背景
在大模型与生成式 AI 驱动的智能算力时代,万卡级 GPU 算力集群已成为企业数字化转型的核心战略基础设施——从千亿参数大模型的分布式训练,到高并发推理服务的实时响应,数万张 GPU 协同构筑的算力网络,正在重新定义技术创新的边界与商业竞争的格局。然而,随着集群规模从百卡迈向万卡级,GPU 资源的可观测性正面临前所未有的挑战:传统监控手段仅能触达设备层的温度与利用率,无法穿透到进程、容器、CUDA 内核等更深层级,更难以实现从基础设施到应用层的全链路关联分析。
然而,当前绝大多数 GPU 监控方案仍停留在"设备健康检查"的初级阶段——部署 DCGM Exporter,通过 Prometheus 抓取 9400 端口指标,在 Grafana 中搭建温度与利用率面板,设置 85°C 告警阈值,便宣告监控体系建成。这种"开箱即用"的方案在单机房、几十张卡的规模下尚可应付,但面对万卡级算力集群的复杂拓扑与多租户混合负载场景,其观测深度的不足便暴露无遗。
在万卡级算力集群中,单张 H100 的小时级成本已达数十美元,整集群日耗算力成本动辄百万。当每一分钟的 GPU 闲置都意味着真金白银的流失时,运维与算法团队真正需要回答的,远不止"GPU 温度是否正常"这一个问题:
- 谁在消耗算力?——DCGM 仅能提供设备级聚合利用率,无法归因到具体进程、容器或 Pod,更无法穿透到团队、项目、任务维度,万卡集群的算力账单成了一笔糊涂账。
- 算力是否真的有效?——利用率 80% 不等于有效计算 80%,可能是数据加载瓶颈,可能是 Kernel Launch 开销过大,也可能是大量小 Kernel 频繁发射导致调度开销占比过高。没有 CUDA 运行时的深度观测,80% 的利用率背后可能藏着巨大的算力浪费。
- 投入产出比如何?——单卡数万、集群数亿的硬件投入,有多少时间在空转等待?训练任务的每瓦算力、每 token 成本是多少?传统监控只回答"硬件是否健康",却无法回答"算力投资是否高效"。
- 性能瓶颈在哪一层?——训练吞吐下降、推理延迟升高时,是驱动版本问题、CUDA 库兼容性问题、网络通信瓶颈,还是业务代码的算法效率问题?没有分层穿透的观测能力,只能逐层盲猜、逐一排除,排障周期以天甚至周计。
DCGM Exporter 作为 NVIDIA 官方的 NVML 封装,在硬件指标采集层面无疑是可靠的,但它从底层就继承了 NVML 的全部边界——仅能提供硬件级计数器数据、NVIDIA 生态独占、缺乏 CUDA 运行时内省能力、无法实现进程级算力归因。当算力集群的规模从百卡跃迁至万卡,当 GPU 资源从团队专属变为多租户共享,当性能优化从"能用就行"转向"每瓦算力"的精细化运营,仅靠设备层监控已远远不够。算力基础设施的管理者,需要一套更深层、更标准化、更具穿透力的全链路可观测方案。
OpenLIT OTel GPU Collector 的出现,为万卡级算力集群的 GPU 可观测性提供了全新的技术路径。它以 OpenTelemetry 为标准化协议基座,通过 eBPF 技术深度穿透 CUDA 运行时,将 GPU 可观测的边界从传统的"设备硬件层"一路推进到"计算内核层"。而当这套深度采集能力与观测云的全链路可观测平台相结合,GPU 指标便不再是孤立的数据孤岛——它可以与应用性能 Trace、业务日志、基础设施指标在统一平台中实现全栈关联与跨层下钻,真正构建起从硬件到业务的完整观测链路。
观测云
观测云是一款专为 IT 工程师打造的全链路可观测产品,它集成了基础设施监控、应用程序性能监控和日志管理,为整个技术栈提供实时可观察性。这款产品能够帮助工程师全面了解端到端的用户体验追踪,了解应用内函数的每一次调用,以及全面监控云时代的基础设施。此外,观测云还具备快速发现系统安全风险的能力,为数字化时代提供安全保障。
为什么选观测云?
观测云原生支持 OTLP 协议接入,无需额外部署 Collector 集群;GPU 指标可与应用性能、日志、基础设施指标在同一平台关联分析;内置丰富的 Dashboard 模板和告警引擎,开箱即用。
可观测链路有三段:OpenLIT 负责采集,DataKit 负责接收 OTLP 并上报,观测云负责查询、展示和告警。

OpenLIT → DataKit → 观测云链路
采集到的数据分三层,各层的生效条件和价值不同:
| 层级 | 指标示例 | 生效条件与价值 |
|---|---|---|
| 设备层 | hw.gpu.utilization、hw.gpu.memory、hw.gpu.power、hw.gpu.temperature | 只要 Collector 能访问 GPU 就有数据。回答"GPU 是否在线、是否有容量压力"。 |
| 进程归因层 | process.gpu.utilization、process.gpu.memory | 要求 Collector 能看见主机进程(--pid=host)。回答"谁在用 GPU",支撑多租户计费和资源调度。 |
| CUDA eBPF 层 | gpu.kernel.launch.calls、gpu.memory.copies、gpu.sm_active | 要求真实负载加载 libcudart,受内核版本、eBPF capability 和安全策略影响。回答"GPU 在干什么",支撑性能调优。 |
这种分层设计的核心思路是:不同层级对应不同的决策类型。设备层服务于运维响应(换卡、扩容),进程层服务于成本核算(调度、计费),内核层服务于性能优化(Kernel 融合、内存对齐)。越往下层,价值越高,但依赖条件也越苛刻。
接入步骤
接入过程分为五步:前置准备 → 配置 DataKit OTLP 接收 → 部署 OpenLIT Collector → 分层验证与排障 → 查询与告警配置。
1. 前置准备
部署前需确保主机已具备以下基础环境:
- NVIDIA 驱动已安装且正常工作
- Docker 或 Containerd 容器运行时
- NVIDIA Container Toolkit(容器访问 GPU 必需)
- DataKit 已部署且能正常上报到观测云
部署前先用一个临时容器确认 Docker 能访问 GPU(镜像按本机驱动兼容性选择):
docker run --rm --gpus all <兼容本机驱动的 CUDA 镜像> nvidia-smi
这一步失败就先修驱动或 Container Toolkit,OpenLIT 无法绕过底层问题。
2. 配置 DataKit OTLP 接收
编辑 /usr/local/datakit/conf.d/opentelemetry.conf(首次启用时从 samples/opentelemetry.conf.sample 复制,已有配置直接编辑不要覆盖):
[[inputs.opentelemetry]]
customer_tags_all = true
[inputs.opentelemetry.grpc]
addr = "127.0.0.1:4317"
max_payload = 16777216
customer_tags_all = true 会保留 OpenLIT 上报的全部 OTel attributes,便于用 GPU ID、进程、Kernel 名等 tag 自定义视图。
注意两点:
- attribute key 中的点入库后变为下划线(
hw.id→hw_id,cuda.kernel.name→cuda_kernel_name) process.pid、cuda.kernel.name等高基数字段可能带来时序膨胀,生产环境可改为明确的customer_tags白名单
重启并确认监听:
sudo datakit service -R
ss -lntp | grep ':4317'

协议与端口必须成对:本文用 grpc 配 4317;改用 http/protobuf 则配 4318 且接收端要启用 OTLP/HTTP,两者不能混搭。跨主机或非 host 网络部署时,把 addr 改成 0.0.0.0:4317、Collector 端点改成 DataKit 的内网地址,用防火墙限制来源,不要把未认证的 OTLP 端口暴露到公网。
3. 部署 OpenLIT Collector
NVIDIA + Docker、同机直连 DataKit 的启动命令:
docker pull ghcr.io/openlit/otel-gpu-collector:latest
docker run -d \
--name otel-gpu-collector \
--restart unless-stopped \
--network host \
--gpus all \
--pid=host \
--cap-add BPF \
--cap-add PERFMON \
--ulimit memlock=-1:-1 \
-e OTEL_SERVICE_NAME=openlit-otel-gpu-collector \
-e OTEL_RESOURCE_ATTRIBUTES='deployment.environment=production,team=ml,host.name=gpu-host-01' \
-e OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 \
-e OTEL_EXPORTER_OTLP_PROTOCOL=grpc \
-e OTEL_METRIC_EXPORT_INTERVAL=15000 \
-e OTEL_GPU_EBPF_ENABLED=true \
ghcr.io/openlit/otel-gpu-collector:latest
每个关键参数对应第一节的一层能力:
| 参数 | 对应层级 | 作用 |
|---|---|---|
--network host |
链路层 | 连接宿主机 127.0.0.1:4317 |
--gpus all |
设备层 | 访问 GPU 设备,获取 hw.gpu.* 指标 |
--pid=host |
进程层 | 看见主机进程,实现进程归因和 libcudart 发现 |
BPF / PERFMON / memlock |
CUDA eBPF 层 | eBPF 初始化所需权限 |
少哪个参数,对应层的数据就缺。
OTEL_METRIC_EXPORT_INTERVAL 单位是毫秒,默认 60000,官方对 GPU 主机建议 15000。OTEL_RESOURCE_ATTRIBUTES 至少给出稳定的 host.name、deployment.environment 和团队归属。生产环境验证后应固定镜像版本,不长期跟随 latest。
启动后用 docker logs --tail 200 otel-gpu-collector 确认四件事:GPU 已发现、没有持续的 OTLP export error、eBPF 初始化没有 capability / memlock / perf event 报错、容器不在重启循环中。

4. 分层验证与排障
先验证设备层。 在观测云「指标」中对 otel_service 做一条最小查询,确认每块 GPU 每 15 秒持续上报:
M::`otel_service`:(last(`hw.gpu.up`) AS `GPU 在线`) [::15s] BY `host`,`hw_id`,`hw_name`
hw.gpu.up 连续出现即说明「GPU → OpenLIT → OTLP gRPC → DataKit → 观测云」整条链路成立。此时如果 CUDA Kernel/Memcpy 没有数据,不要回头排查 OTLP 端口,应检查工作负载与 eBPF 层。
CUDA eBPF 指标只有真实 CUDA 负载执行了对应操作才会产生(调用 Kernel 才有 gpu.kernel.launch.calls,执行 cudaMemcpy* 才有 gpu.memory.copies_*)。没有 CUDA workload 时设备层正常、CUDA 图表为空是预期行为。

指标缺数据时,按缺口直接定位层级:
| 现象 | 优先检查 |
|---|---|
所有 hw.gpu.* 都没有 |
GPU 设备映射、驱动、Container Toolkit、Collector 日志 |
| 设备指标有,进程指标没有 | --pid=host、负载是否真的在用 GPU |
| 进程指标有,Kernel/Memcpy/SM 没有 | libcudart、BPF/PERFMON、memlock、AppArmor/Yama/perf event |
| Collector 有数据但观测云没有 | OTLP 协议与端口、DataKit 监听、网络与 DataKit 日志 |
| 只缺某个 Memcpy 方向 | 负载没做该方向拷贝,不是采集故障 |
5. 查询与告警配置
写查询时最容易错的是把 Gauge、Counter、Histogram 当成同一种数据,对应三条规则:
比例值是 0..1,不是 0..100。 hw.gpu.utilization、process.gpu.utilization、gpu.sm_active 等在 Dashboard 用 percent_decimal 单位直接展示;Monitor 阈值按百分数写时要先乘 100。
Counter 看速率或增量,不看原值。 gpu.kernel.launch.calls 是累计值,画发射频率用 [::1m:rate];硬件错误用 [::5m:increase] 只盯新增。
Histogram 入库后展开为 _sum / _count / _bucket / _min / _max,字段名保留 OTel 点分名。
# Memcpy 吞吐(B/s)
M::`otel_service`:(sum(`gpu.memory.copies_sum`) AS `Memcpy 吞吐`) [::1m:rate] BY `cuda_memcpy_kind`
# Memcpy 调用率
M::`otel_service`:(sum(`gpu.memory.copies_count`) AS `Memcpy 调用率`) [::1m:rate] BY `cuda_memcpy_kind`

Dashboard 建议按四个问题分组组织,设备层与 CUDA 层分开,缺数据时能一眼定位层级:
- GPU 是否在线——在线状态、数量
- 是否有容量压力——利用率、显存、温度、功耗
- 谁在使用 GPU——进程名、PID、进程显存与利用率
- CUDA 行为是否健康——Kernel 发射率、Memcpy 吞吐与调用率、SM 活跃度
- 是否发生硬件或调度异常——硬件错误、节流原因
告警初始基线:
- GPU 利用率 5 分钟均值低于 10% 且连续多个检测周期(成本告警)
- 显存
usage / limit × 100 > 95%(容量告警) - 5 分钟
increase(hw.errors) > 0(硬件告警) - Kernel 发射率超基线并持续(工作负载信号)
其中 Kernel 发射频率没有跨业务通用阈值,训练、推理、批处理差异很大,先观察一段稳定业务基线再按 workload 分层设置。
接入效果
接入 OpenLIT + 观测云后,GPU 可观测性的价值体现在五个实战场景中——这些都是传统 DCGM 监控无法回答的问题。
场景一:哪个 CUDA Kernel 在吃我的 GPU?
启用 eBPF 后,采集器通过 uprobe 挂载到 cudaLaunchKernel 上,将 Kernel 发射转成按 Kernel 名分桶的指标。在观测云中可以直接查询:
# 整个集群中发射频率最高的 Top-10 Kernel
M::`otel_service`:(topk(10, sum(`gpu.kernel.launch.calls`) [::5m:rate] BY `cuda_kernel_name`))
对于 vLLM 部署,你将看到 flash_attention_kernel、rms_norm_kernel、paged_attention_v2_kernel——这些名字与你 CUDA 库中编译的符号一一对应。这意味着 GPU 可观测性第一次从基础设施团队渗透到了 ML 工程师的工具箱:当 Kernel 发射频率异常升高时,直接指向 torch.compile 等优化方向,而不是运维层面的排查。
场景二:内存带宽被浪费了吗?
通过按方向拆分的 Memcpy 吞吐指标,可以判断数据加载是否高效:
- 健康的训练循环会将数据固定在设备上,Memcpy 以
DeviceToDevice为主 - 如果
HostToDevice占主导,说明数据加载器没有预热锁页内存,PCIe 总线在反复震荡 - Memcpy 调用率高但吞吐低,说明是大量细碎的小拷贝——几乎总是一个性能 bug
传统监控只能看到"显存用了多少",但看不到"数据是怎么流动的"。eBPF 级别的 Memcpy 追踪让数据加载效率优化有了量化依据。
场景三:硬件健康的"金丝雀"信号
ECC 和 PCIe 重放错误是硬件故障的早期预警。采集器以 hw.errors 指标发射三类错误:corrected(可纠正)、uncorrected(不可纠正)、pcie_replay(PCIe 重放)。
不可纠正 ECC 错误出现非零增量,意味着这张卡应该退出资源池。单个 PCI 地址上的 PCIe 重放错误速率升高,意味着线缆或插槽接触不良。这些信号比温度告警更早、更准确地预测硬件故障——等到温度飙到 85°C 时,SM 时钟可能已经降频很久了。
总结
OpenLIT OTel GPU Collector + 观测云的组合,将 GPU 可观测性从传统的"设备温度监控"提升到了"全栈决策支持"的层面。其核心价值体现在三个维度:
深度:从设备层到内核层。 通过 eBPF 技术深入 CUDA 运行时,实现了 Kernel 级别的可观测性,让 ML 工程师能直接从指标定位优化方向,而不是靠猜测和 profiling。
标准:OTel 原生,无缝融入现有体系。 全程使用 OpenTelemetry 标准协议和语义约定,GPU 指标可以与应用 Trace、业务指标、日志在观测云中统一关联分析,不需要维护一套独立的 GPU 监控栈。
统一:一个平台,全栈可观测。 在观测云中,GPU 指标不是孤岛——你可以从应用延迟异常下钻到 GPU 利用率,再下钻到具体的 Kernel 发射模式,实现真正的全栈问题定位。


