GPU 监控与调试
掌握 GPU 监控体系,从命令行工具到企业级监控平台
本章目标
- 深入掌握 nvidia-smi 的所有功能和输出含义
- 理解 DCGM (Data Center GPU Manager) 架构和使用
- 学会构建完整的 GPU 监控体系
- 掌握 GPU 性能分析和问题排查方法
1. nvidia-smi 深度解析
1.1 nvidia-smi 架构
┌─────────────────────────────────────────────────────────────────────┐
│ nvidia-smi 架构 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ nvidia-smi (CLI) │ │
│ │ │ │
│ │ 功能: │ │
│ │ - GPU 状态查询和监控 │ │
│ │ - GPU 配置管理 (时钟、电源、MIG 等) │ │
│ │ - 进程管理 │ │
│ │ - 拓扑查询 │ │
│ └──────────────────────────┬──────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ NVML (libnvidia-ml.so) │ │
│ │ NVIDIA Management Library │ │
│ │ │ │
│ │ C API 库,提供: │ │
│ │ - GPU 信息查询 │ │
│ │ - 性能计数器访问 │ │
│ │ - 事件通知 │ │
│ │ - 配置管理 │ │
│ └──────────────────────────┬──────────────────────────────────┘ │
│ │ ioctl │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ nvidia.ko (内核驱动) │ │
│ │ │ │
│ │ - 硬件寄存器访问 │ │
│ │ - 性能计数器读取 │ │
│ │ - 中断处理 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ GPU 硬件 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
1.2 基础输出详解
$ nvidia-smi
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 输出解析 │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ +-----------------------------------------------------------------------------+ │
│ | NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 | │
│ |-------------------------------+----------------------+----------------------| │
│ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | │
│ | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | │
│ |===============================+======================+======================| │
│ | 0 NVIDIA A100-SXM... On | 00000000:07:00.0 Off | 0 | │
│ | N/A 32C P0 52W / 400W | 412MiB / 81920MiB | 0% Default | │
│ +-------------------------------+----------------------+----------------------+ │
│ │
│ 字段说明: │
│ │
│ ┌─────────────────────┬────────────────────────────────────────────────────┐ │
│ │ NVIDIA-SMI │ nvidia-smi 工具版本 │ │
│ │ Driver Version │ NVIDIA 驱动版本 │ │
│ │ CUDA Version │ 驱动支持的最高 CUDA 版本 (不是安装的 CUDA 版本) │ │
│ ├─────────────────────┼────────────────────────────────────────────────────┤ │
│ │ GPU │ GPU 索引号 (从 0 开始) │ │
│ │ Name │ GPU 型号名称 │ │
│ │ Persistence-M │ 持久化模式 (On/Off),建议生产环境开启 │ │
│ │ Bus-Id │ PCIe 总线地址 (Domain:Bus:Device.Function) │ │
│ │ Disp.A │ Display Active,是否连接显示器 │ │
│ │ Volatile Uncorr. ECC│ 自上次重启以来的不可纠正 ECC 错误数 │ │
│ ├─────────────────────┼────────────────────────────────────────────────────┤ │
│ │ Fan │ 风扇转速百分比 (N/A 表示被动散热或不支持) │ │
│ │ Temp │ GPU 核心温度 (摄氏度) │ │
│ │ Perf │ 性能状态 P0-P12 (P0 最高性能,P12 最低) │ │
│ │ Pwr:Usage/Cap │ 当前功耗 / 功耗上限 (瓦特) │ │
│ │ Memory-Usage │ 显存使用量 / 总显存 │ │
│ │ GPU-Util │ GPU 计算单元利用率 (过去采样周期内) │ │
│ │ Compute M. │ 计算模式: Default/Exclusive_Thread/Exclusive_Process │ │
│ └─────────────────────┴────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
1.3 性能状态 (P-State) 详解
┌─────────────────────────────────────────────────────────────────────┐
│ GPU 性能状态 (P-State) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ P-State 定义了 GPU 的功耗和性能级别 │
│ │
│ ┌──────────┬────────────────────────────────────────────────────┐ │
│ │ P-State │ 说明 │ │
│ ├──────────┼────────────────────────────────────────────────────┤ │
│ │ P0 │ 最高性能状态,GPU 运行在最高时钟频率 │ │
│ │ P1 │ 高性能状态,略低于 P0 │ │
│ │ P2 │ 平衡状态,中等性能 │ │
│ │ P3-P7 │ 中间状态,逐渐降低性能 │ │
│ │ P8 │ 基本 3D 性能 │ │
│ │ P10 │ DVD 播放级别 │ │
│ │ P12 │ 最低性能状态,空闲 │ │
│ └──────────┴────────────────────────────────────────────────────┘ │
│ │
│ 数据中心 GPU (Tesla/A100/H100) 通常只有: │
│ - P0: 最高性能 (运行计算任务时) │
│ - P8: 空闲状态 │
│ │
│ 查看当前 P-State: │
│ $ nvidia-smi -q -d PERFORMANCE │
│ │
│ 锁定 GPU 时钟到最高频率 (避免 P-State 切换带来的性能抖动): │
│ $ sudo nvidia-smi -pm 1 # 启用持久化模式 │
│ $ sudo nvidia-smi -lgc 1410 # 锁定 GPU 时钟到 1410MHz│
│ $ sudo nvidia-smi -lmc 1593 # 锁定显存时钟 │
│ │
│ 解锁时钟: │
│ $ sudo nvidia-smi -rgc # 重置 GPU 时钟 │
│ $ sudo nvidia-smi -rmc # 重置显存时钟 │
│ │
└─────────────────────────────────────────────────────────────────────┘
1.4 查询命令大全
# ==================== 基础查询 ====================
# 查看所有 GPU 简要信息
nvidia-smi
# 查看指定 GPU (索引为 0)
nvidia-smi -i 0
# 查看详细信息
nvidia-smi -q
# 查看指定 GPU 的详细信息
nvidia-smi -i 0 -q
# ==================== 分类查询 ====================
# 查看内存信息
nvidia-smi -q -d MEMORY
# 输出: FB Memory Usage, BAR1 Memory Usage
# 查看利用率
nvidia-smi -q -d UTILIZATION
# 输出: GPU 利用率, 内存利用率, 编码器/解码器利用率
# 查看温度
nvidia-smi -q -d TEMPERATURE
# 输出: 当前温度, 关机温度阈值, 降频温度阈值
# 查看功耗
nvidia-smi -q -d POWER
# 输出: 当前功耗, 功耗限制, 默认/最小/最大功耗限制
# 查看时钟频率
nvidia-smi -q -d CLOCK
# 输出: Graphics/SM/Memory/Video 时钟, 最大时钟, 应用时钟
# 查看 ECC 信息
nvidia-smi -q -d ECC
# 输出: ECC 模式, 各类型错误计数
# 查看性能状态
nvidia-smi -q -d PERFORMANCE
# 输出: P-State, 时钟调节原因
# 查看进程信息
nvidia-smi -q -d PIDS
# 输出: 使用 GPU 的进程列表
# 查看支持的时钟频率
nvidia-smi -q -d SUPPORTED_CLOCKS
# 查看 PCIe 信息
nvidia-smi -q -d PCIE
# 输出: PCIe 代数, 链路宽度, 吞吐量
# ==================== 格式化输出 ====================
# CSV 格式输出 (适合脚本处理)
nvidia-smi --query-gpu=name,memory.total,memory.used,memory.free,utilization.gpu,temperature.gpu --format=csv
# 带表头的 CSV
nvidia-smi --query-gpu=index,name,memory.total,memory.used,utilization.gpu --format=csv
# 不带表头和单位
nvidia-smi --query-gpu=index,memory.used --format=csv,noheader,nounits
# ==================== 可查询的字段列表 ====================
# 查看所有可查询字段
nvidia-smi --help-query-gpu
# 常用字段:
# timestamp - 时间戳
# driver_version - 驱动版本
# name, gpu_name - GPU 名称
# gpu_bus_id - PCIe 总线 ID
# gpu_uuid - GPU UUID
# memory.total - 总显存
# memory.used - 已用显存
# memory.free - 空闲显存
# utilization.gpu - GPU 利用率
# utilization.memory - 显存带宽利用率
# temperature.gpu - GPU 温度
# power.draw - 当前功耗
# power.limit - 功耗限制
# clocks.gr - GPU 时钟
# clocks.mem - 显存时钟
# pstate - 性能状态
# fan.speed - 风扇转速
# ecc.errors.corrected.* - 可纠正 ECC 错误
# ecc.errors.uncorrected.* - 不可纠正 ECC 错误
# pcie.link.gen.current - 当前 PCIe 代数
# pcie.link.width.current - 当前 PCIe 宽度
# ==================== 进程查询 ====================
# 查看使用 GPU 的进程
nvidia-smi pmon -s um -c 1
# 输出: PID, 进程类型, SM利用率, 显存利用率, 编解码器利用率, 进程名
# 持续监控进程 (每秒刷新)
nvidia-smi pmon -s um
# 查看计算进程
nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv
# ==================== 拓扑查询 ====================
# 查看 GPU 拓扑矩阵
nvidia-smi topo -m
# 输出矩阵显示 GPU 之间的连接方式:
# X = 同一个 GPU
# SYS = 通过系统互联 (PCIe)
# NODE = 同一 NUMA 节点
# PHB = 通过 PCIe Host Bridge
# PXB = 通过 PCIe Bridge
# PIX = 通过 PCIe Switch
# NV# = 通过 NVLink (#表示连接数)
# 查看 GPU 与 CPU 的亲和性
nvidia-smi topo -a
# 查看 P2P 访问能力
nvidia-smi topo -p2p r # 读取
nvidia-smi topo -p2p w # 写入
nvidia-smi topo -p2p n # NVLink
nvidia-smi topo -p2p a # 原子操作
1.5 监控命令
# ==================== 实时监控 ====================
# 每秒刷新一次 (默认)
nvidia-smi -l 1
# 每 500 毫秒刷新一次
nvidia-smi -lms 500
# 后台循环输出到文件
nvidia-smi -l 1 -f gpu_stats.log &
# 使用 watch 命令监控
watch -n 1 nvidia-smi
# ==================== dmon - 设备监控 ====================
# 默认监控
nvidia-smi dmon
# 输出列: gpu pwr gtemp mtemp sm mem enc dec mclk pclk
# 选择监控指标
nvidia-smi dmon -s pucm
# p: 功耗
# u: 利用率
# c: 时钟频率
# m: 显存
# t: 温度
# v: ECC 错误
# e: PCIe 吞吐
# 指定采样间隔 (毫秒)
nvidia-smi dmon -d 100
# 监控指定 GPU
nvidia-smi dmon -i 0,1
# 输出到文件
nvidia-smi dmon -f dmon.log
# ==================== pmon - 进程监控 ====================
# 默认监控
nvidia-smi pmon
# 输出列: gpu pid type sm mem enc dec command
# 选择监控指标
nvidia-smi pmon -s um
# u: 利用率 (sm, mem, enc, dec)
# m: 显存使用
# 指定采样次数
nvidia-smi pmon -c 10
# 监控指定 GPU
nvidia-smi pmon -i 0
# ==================== 事件监控 ====================
# 监控 GPU 事件
nvidia-smi events -l
# 监控特定类型事件
nvidia-smi events -l -e single_bit_ecc
nvidia-smi events -l -e double_bit_ecc
nvidia-smi events -l -e pstate_change
nvidia-smi events -l -e clock_change
# ==================== 监控脚本示例 ====================
# 每秒采集一次,输出 CSV 格式,持续 1 小时
nvidia-smi --query-gpu=timestamp,index,name,temperature.gpu,utilization.gpu,utilization.memory,memory.used,power.draw \
--format=csv -l 1 > gpu_monitor_$(date +%Y%m%d_%H%M%S).csv &
# 后台监控并设置超时
timeout 3600 nvidia-smi -l 1 -f /var/log/nvidia/gpu_stats.log &
1.6 配置管理命令
# ==================== 持久化模式 ====================
# 查看持久化模式状态
nvidia-smi -q | grep "Persistence Mode"
# 启用持久化模式 (推荐生产环境)
sudo nvidia-smi -pm 1
# 禁用持久化模式
sudo nvidia-smi -pm 0
# 为什么要启用持久化模式?
# - 保持驱动常驻内存,避免首次调用的初始化延迟
# - 保持 GPU 配置 (时钟、功耗设置)
# - 减少启动开销
# ==================== 计算模式 ====================
# 查看计算模式
nvidia-smi -q | grep "Compute Mode"
# 设置计算模式
sudo nvidia-smi -c 0 # Default: 多进程共享
sudo nvidia-smi -c 1 # Exclusive_Thread: 单线程独占 (已废弃)
sudo nvidia-smi -c 2 # Prohibited: 禁止计算
sudo nvidia-smi -c 3 # Exclusive_Process: 单进程独占
# Exclusive_Process 模式说明:
# - 一次只允许一个进程使用 GPU
# - 其他进程尝试使用时会失败
# - 适合需要独占 GPU 的场景
# ==================== 功耗管理 ====================
# 查看功耗限制
nvidia-smi -q -d POWER
# 设置功耗限制 (瓦特)
sudo nvidia-smi -pl 300 # 设置为 300W
# 恢复默认功耗限制
sudo nvidia-smi -pl $(nvidia-smi -q -d POWER | grep "Default Power Limit" | awk '{print $5}')
# 查看支持的功耗范围
nvidia-smi -q -d POWER | grep "Power Limit"
# ==================== 时钟管理 ====================
# 查看当前时钟
nvidia-smi -q -d CLOCK
# 查看支持的时钟频率
nvidia-smi -q -d SUPPORTED_CLOCKS
# 设置应用时钟 (Graphics Clock, Memory Clock)
sudo nvidia-smi -ac 1593,1410 # 显存时钟 1593MHz, GPU 时钟 1410MHz
# 锁定 GPU 时钟到固定频率
sudo nvidia-smi -lgc 1410
# 锁定显存时钟
sudo nvidia-smi -lmc 1593
# 解锁时钟
sudo nvidia-smi -rgc # 重置 GPU 时钟
sudo nvidia-smi -rmc # 重置显存时钟
# 重置应用时钟到默认值
sudo nvidia-smi -rac
# ==================== ECC 管理 ====================
# 查看 ECC 状态
nvidia-smi -q -d ECC
# 启用 ECC
sudo nvidia-smi -e 1
# 需要重启才能生效
# 禁用 ECC
sudo nvidia-smi -e 0
# 清除 ECC 错误计数
sudo nvidia-smi -p 0 # 清除 Volatile 计数
sudo nvidia-smi -p 1 # 清除 Aggregate 计数
# ==================== MIG 管理 ====================
# 查看 MIG 状态
nvidia-smi -q | grep "MIG Mode"
# 启用 MIG
sudo nvidia-smi -i 0 -mig 1
sudo nvidia-smi -i 0 -r # 重置 GPU 使配置生效
# 禁用 MIG
sudo nvidia-smi -i 0 -mig 0
sudo nvidia-smi -i 0 -r
# MIG 实例管理见上一章
# ==================== GPU 重置 ====================
# 重置 GPU (清除所有进程和配置)
sudo nvidia-smi -r -i 0
# 注意: 重置前确保没有进程使用该 GPU
1.7 nvidia-smi 实战脚本
#!/bin/bash
# gpu_health_check.sh - GPU 健康检查脚本
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
echo "=========================================="
echo " GPU Health Check Report"
echo " $(date)"
echo "=========================================="
# 检查 nvidia-smi 是否可用
if ! command -v nvidia-smi &> /dev/null; then
echo -e "${RED}[ERROR] nvidia-smi not found${NC}"
exit 1
fi
# 获取 GPU 数量
GPU_COUNT=$(nvidia-smi --query-gpu=count --format=csv,noheader,nounits | head -1)
echo -e "\n${GREEN}Found $GPU_COUNT GPU(s)${NC}\n"
# 遍历每个 GPU
for i in $(seq 0 $((GPU_COUNT-1))); do
echo "----------------------------------------"
echo "GPU $i:"
echo "----------------------------------------"
# 获取 GPU 信息
GPU_INFO=$(nvidia-smi -i $i --query-gpu=name,driver_version,memory.total,memory.used,memory.free,temperature.gpu,power.draw,power.limit,utilization.gpu,utilization.memory,pstate,ecc.errors.uncorrected.volatile.total --format=csv,noheader,nounits)
IFS=',' read -r NAME DRIVER MEM_TOTAL MEM_USED MEM_FREE TEMP POWER POWER_LIMIT UTIL_GPU UTIL_MEM PSTATE ECC_ERRORS <<< "$GPU_INFO"
echo " Name: $NAME"
echo " Driver: $DRIVER"
echo ""
# 显存检查
MEM_PERCENT=$(echo "scale=1; $MEM_USED / $MEM_TOTAL * 100" | bc)
if (( $(echo "$MEM_PERCENT > 90" | bc -l) )); then
echo -e " Memory: ${RED}$MEM_USED / $MEM_TOTAL MiB ($MEM_PERCENT%)${NC}"
elif (( $(echo "$MEM_PERCENT > 70" | bc -l) )); then
echo -e " Memory: ${YELLOW}$MEM_USED / $MEM_TOTAL MiB ($MEM_PERCENT%)${NC}"
else
echo -e " Memory: ${GREEN}$MEM_USED / $MEM_TOTAL MiB ($MEM_PERCENT%)${NC}"
fi
# 温度检查
TEMP=$(echo $TEMP | tr -d ' ')
if (( TEMP > 85 )); then
echo -e " Temperature: ${RED}${TEMP}°C (CRITICAL)${NC}"
elif (( TEMP > 75 )); then
echo -e " Temperature: ${YELLOW}${TEMP}°C (WARNING)${NC}"
else
echo -e " Temperature: ${GREEN}${TEMP}°C${NC}"
fi
# 功耗检查
POWER=$(echo $POWER | tr -d ' ')
POWER_LIMIT=$(echo $POWER_LIMIT | tr -d ' ')
POWER_PERCENT=$(echo "scale=1; $POWER / $POWER_LIMIT * 100" | bc)
echo " Power: ${POWER}W / ${POWER_LIMIT}W ($POWER_PERCENT%)"
# 利用率
UTIL_GPU=$(echo $UTIL_GPU | tr -d ' ')
UTIL_MEM=$(echo $UTIL_MEM | tr -d ' ')
echo " GPU Util: ${UTIL_GPU}%"
echo " Memory BW Util: ${UTIL_MEM}%"
# P-State
PSTATE=$(echo $PSTATE | tr -d ' ')
echo " P-State: $PSTATE"
# ECC 错误检查
ECC_ERRORS=$(echo $ECC_ERRORS | tr -d ' ')
if [[ "$ECC_ERRORS" != "[N/A]" ]] && [[ "$ECC_ERRORS" != "0" ]] && [[ -n "$ECC_ERRORS" ]]; then
echo -e " ECC Errors: ${RED}$ECC_ERRORS (CRITICAL - Consider GPU replacement)${NC}"
else
echo -e " ECC Errors: ${GREEN}0${NC}"
fi
echo ""
done
# 检查持久化模式
echo "----------------------------------------"
echo "Configuration Check:"
echo "----------------------------------------"
PM_STATUS=$(nvidia-smi -q | grep "Persistence Mode" | head -1 | awk '{print $4}')
if [[ "$PM_STATUS" == "Enabled" ]]; then
echo -e " Persistence Mode: ${GREEN}Enabled${NC}"
else
echo -e " Persistence Mode: ${YELLOW}Disabled (Recommend enabling for production)${NC}"
fi
# 检查 GPU 进程
echo ""
echo "----------------------------------------"
echo "GPU Processes:"
echo "----------------------------------------"
nvidia-smi --query-compute-apps=gpu_uuid,pid,process_name,used_memory --format=csv 2>/dev/null || echo " No compute processes running"
echo ""
echo "=========================================="
echo "Health check completed"
echo "=========================================="
#!/usr/bin/env python3
"""
gpu_monitor.py - GPU 监控和告警脚本
用于生产环境的 GPU 监控,支持阈值告警
"""
import subprocess
import json
import time
import sys
from datetime import datetime
from typing import Dict, List, Optional
import argparse
class GPUMonitor:
"""GPU 监控类"""
# 默认告警阈值
DEFAULT_THRESHOLDS = {
'temperature': {'warning': 75, 'critical': 85},
'memory_percent': {'warning': 85, 'critical': 95},
'power_percent': {'warning': 90, 'critical': 100},
'ecc_errors': {'warning': 1, 'critical': 10},
}
def __init__(self, thresholds: Optional[Dict] = None):
self.thresholds = thresholds or self.DEFAULT_THRESHOLDS
def query_gpus(self) -> List[Dict]:
"""查询所有 GPU 状态"""
query_fields = [
'index',
'name',
'uuid',
'driver_version',
'memory.total',
'memory.used',
'memory.free',
'temperature.gpu',
'power.draw',
'power.limit',
'utilization.gpu',
'utilization.memory',
'pstate',
'ecc.errors.uncorrected.volatile.total',
'ecc.errors.corrected.volatile.total',
'pcie.link.gen.current',
'pcie.link.width.current',
]
cmd = [
'nvidia-smi',
f'--query-gpu={",".join(query_fields)}',
'--format=csv,noheader,nounits'
]
try:
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
except subprocess.CalledProcessError as e:
print(f"Error running nvidia-smi: {e}", file=sys.stderr)
return []
except FileNotFoundError:
print("nvidia-smi not found", file=sys.stderr)
return []
gpus = []
for line in result.stdout.strip().split('\n'):
if not line:
continue
values = [v.strip() for v in line.split(',')]
# 解析数值,处理 [N/A] 情况
def parse_value(v, default=0):
if v in ['[N/A]', '[Not Supported]', '']:
return default
try:
return float(v)
except ValueError:
return v
gpu = {
'index': int(values[0]),
'name': values[1],
'uuid': values[2],
'driver_version': values[3],
'memory_total': parse_value(values[4]),
'memory_used': parse_value(values[5]),
'memory_free': parse_value(values[6]),
'temperature': parse_value(values[7]),
'power_draw': parse_value(values[8]),
'power_limit': parse_value(values[9]),
'utilization_gpu': parse_value(values[10]),
'utilization_memory': parse_value(values[11]),
'pstate': values[12],
'ecc_uncorrected': parse_value(values[13]),
'ecc_corrected': parse_value(values[14]),
'pcie_gen': parse_value(values[15]),
'pcie_width': parse_value(values[16]),
}
# 计算百分比
if gpu['memory_total'] > 0:
gpu['memory_percent'] = (gpu['memory_used'] / gpu['memory_total']) * 100
else:
gpu['memory_percent'] = 0
if gpu['power_limit'] > 0:
gpu['power_percent'] = (gpu['power_draw'] / gpu['power_limit']) * 100
else:
gpu['power_percent'] = 0
gpus.append(gpu)
return gpus
def check_alerts(self, gpu: Dict) -> List[Dict]:
"""检查告警条件"""
alerts = []
# 温度告警
temp = gpu['temperature']
if temp >= self.thresholds['temperature']['critical']:
alerts.append({
'level': 'CRITICAL',
'metric': 'temperature',
'message': f"GPU {gpu['index']} temperature critical: {temp}°C",
'value': temp
})
elif temp >= self.thresholds['temperature']['warning']:
alerts.append({
'level': 'WARNING',
'metric': 'temperature',
'message': f"GPU {gpu['index']} temperature high: {temp}°C",
'value': temp
})
# 显存告警
mem_pct = gpu['memory_percent']
if mem_pct >= self.thresholds['memory_percent']['critical']:
alerts.append({
'level': 'CRITICAL',
'metric': 'memory',
'message': f"GPU {gpu['index']} memory critical: {mem_pct:.1f}%",
'value': mem_pct
})
elif mem_pct >= self.thresholds['memory_percent']['warning']:
alerts.append({
'level': 'WARNING',
'metric': 'memory',
'message': f"GPU {gpu['index']} memory high: {mem_pct:.1f}%",
'value': mem_pct
})
# ECC 错误告警
ecc = gpu['ecc_uncorrected']
if ecc >= self.thresholds['ecc_errors']['critical']:
alerts.append({
'level': 'CRITICAL',
'metric': 'ecc_errors',
'message': f"GPU {gpu['index']} ECC uncorrected errors critical: {ecc}",
'value': ecc
})
elif ecc >= self.thresholds['ecc_errors']['warning']:
alerts.append({
'level': 'WARNING',
'metric': 'ecc_errors',
'message': f"GPU {gpu['index']} ECC uncorrected errors: {ecc}",
'value': ecc
})
return alerts
def format_output(self, gpus: List[Dict], format: str = 'text') -> str:
"""格式化输出"""
if format == 'json':
output = {
'timestamp': datetime.now().isoformat(),
'gpus': gpus
}
return json.dumps(output, indent=2)
# 文本格式
lines = []
lines.append(f"GPU Monitor - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
lines.append("=" * 80)
for gpu in gpus:
lines.append(f"\nGPU {gpu['index']}: {gpu['name']}")
lines.append(f" UUID: {gpu['uuid']}")
lines.append(f" Memory: {gpu['memory_used']:.0f} / {gpu['memory_total']:.0f} MiB ({gpu['memory_percent']:.1f}%)")
lines.append(f" Temperature: {gpu['temperature']:.0f}°C")
lines.append(f" Power: {gpu['power_draw']:.1f} / {gpu['power_limit']:.1f} W ({gpu['power_percent']:.1f}%)")
lines.append(f" GPU Util: {gpu['utilization_gpu']:.0f}%")
lines.append(f" Memory BW Util: {gpu['utilization_memory']:.0f}%")
lines.append(f" P-State: {gpu['pstate']}")
lines.append(f" PCIe: Gen{gpu['pcie_gen']:.0f} x{gpu['pcie_width']:.0f}")
lines.append(f" ECC Errors: {gpu['ecc_uncorrected']:.0f} uncorrected, {gpu['ecc_corrected']:.0f} corrected")
# 检查告警
alerts = self.check_alerts(gpu)
if alerts:
lines.append(" Alerts:")
for alert in alerts:
lines.append(f" [{alert['level']}] {alert['message']}")
return '\n'.join(lines)
def run_continuous(self, interval: int = 5, format: str = 'text'):
"""持续监控"""
try:
while True:
gpus = self.query_gpus()
output = self.format_output(gpus, format)
# 清屏并输出
print('\033[2J\033[H', end='')
print(output)
time.sleep(interval)
except KeyboardInterrupt:
print("\nMonitoring stopped.")
def main():
parser = argparse.ArgumentParser(description='GPU Monitoring Tool')
parser.add_argument('-f', '--format', choices=['text', 'json'], default='text',
help='Output format')
parser.add_argument('-c', '--continuous', action='store_true',
help='Continuous monitoring mode')
parser.add_argument('-i', '--interval', type=int, default=5,
help='Monitoring interval in seconds (for continuous mode)')
parser.add_argument('--json', action='store_true',
help='Output in JSON format (shortcut for -f json)')
args = parser.parse_args()
format = 'json' if args.json else args.format
monitor = GPUMonitor()
if args.continuous:
monitor.run_continuous(interval=args.interval, format=format)
else:
gpus = monitor.query_gpus()
print(monitor.format_output(gpus, format))
if __name__ == '__main__':
main()
2. DCGM (Data Center GPU Manager)
2.1 DCGM 架构概览
┌─────────────────────────────────────────────────────────────────────┐
│ DCGM 架构 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 客户端工具 │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ dcgmi │ │dcgm-exporter│ │ 自定义 │ │ 集成应用 │ │ │
│ │ │ (CLI) │ │(Prometheus)│ │ 应用 │ │(SLURM等) │ │ │
│ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │
│ │ └─────────────┼─────────────┼─────────────┘ │ │
│ └─────────────────────┼─────────────┼────────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ DCGM API │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ libdcgm.so │ │ │
│ │ │ │ │ │
│ │ │ - C/C++ API │ │ │
│ │ │ - Python bindings (pydcgm) │ │ │
│ │ │ - Go bindings (dcgm-go) │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────┬───────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ nv-hostengine (守护进程) │ │
│ │ │ │
│ │ 功能: │ │
│ │ - GPU 发现和管理 │ │
│ │ - 健康检查和诊断 │ │
│ │ - 性能指标采集 │ │
│ │ - 策略管理 │ │
│ │ - 配置管理 │ │
│ │ │ │
│ │ 运行模式: │ │
│ │ - Embedded (嵌入): 库直接访问 GPU │ │
│ │ - Standalone (独立): 通过 hostengine 访问 │ │
│ │ - Remote (远程): 跨机器访问 │ │
│ └──────────────────────────┬───────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ NVML │ │
│ │ (底层 GPU 管理库) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ DCGM vs nvidia-smi 对比: │
│ ┌────────────────────┬────────────────────┬────────────────────┐ │
│ │ 特性 │ nvidia-smi │ DCGM │ │
│ ├────────────────────┼────────────────────┼────────────────────┤ │
│ │ 主要用途 │ 交互式查询/简单脚本 │ 数据中心监控 │ │
│ │ 健康检查 │ 基础 │ 完整诊断测试 │ │
│ │ 指标数量 │ ~30 │ ~150+ │ │
│ │ 历史数据 │ 不支持 │ 支持 │ │
│ │ GPU 分组 │ 不支持 │ 支持 │ │
│ │ 策略/告警 │ 不支持 │ 支持 │ │
│ │ 远程访问 │ 不支持 │ 支持 │ │
│ │ Prometheus 集成 │ 需要自己解析 │ 原生支持 │ │
│ └────────────────────┴────────────────────┴────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
2.2 DCGM 安装与配置
# ==================== 安装 DCGM ====================
# Ubuntu/Debian
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update
sudo apt-get install -y datacenter-gpu-manager
# RHEL/CentOS
sudo yum install -y datacenter-gpu-manager
# 验证安装
dcgmi --version
# ==================== 启动 nv-hostengine ====================
# 启动守护进程
sudo systemctl start nvidia-dcgm
sudo systemctl enable nvidia-dcgm
# 或手动启动
sudo nv-hostengine -n # -n 表示在前台运行
# 验证服务状态
systemctl status nvidia-dcgm
# 检查连接
dcgmi discovery -l
# ==================== 配置文件 ====================
# /etc/nvidia-dcgm/dcgm.conf
# 配置示例:
# 监听地址 (默认只监听本地)
# listen-address = 127.0.0.1:5555
# 远程访问 (允许所有地址)
# listen-address = 0.0.0.0:5555
# 日志级别
# log-level = INFO
# 日志文件
# log-file = /var/log/nvidia-dcgm/nv-hostengine.log
2.3 dcgmi 命令详解
# ==================== GPU 发现 ====================
# 列出所有 GPU
dcgmi discovery -l
# 输出:
# 4 GPUs found.
# +--------+----------------------------------------------------------------------+
# | GPU ID | Device Information |
# +========+======================================================================+
# | 0 | Name: NVIDIA A100-SXM4-80GB |
# | | PCI Bus ID: 00000000:07:00.0 |
# | | Device UUID: GPU-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
# +--------+----------------------------------------------------------------------+
# 查看系统信息
dcgmi discovery -i
# 查看 NVSwitch 信息 (如果有)
dcgmi discovery -s
# ==================== 设备属性查询 ====================
# 查看所有属性
dcgmi dmon -e 1
# 查看指定 GPU
dcgmi dmon -i 0
# 常用查询
dcgmi dmon -e 150,155,203,204,1001,1002,1003,1004,1005,1006,1007,1008,1009
# 150: SM 时钟
# 155: 内存时钟
# 203: GPU 温度
# 204: 功耗
# 1001: GPU 利用率
# 1002: 内存利用率
# 1003: 编码器利用率
# 1004: 解码器利用率
# 1005: 显存已用
# 1006: 显存空闲
# 1007: ECC 单比特错误
# 1008: ECC 双比特错误
# 1009: PCIe 重放次数
# ==================== GPU 分组 ====================
# 创建 GPU 组
dcgmi group -c "training-gpus" -a 0,1,2,3
# 输出: Successfully created group "training-gpus" with a group ID of 1
# 列出所有组
dcgmi group -l
# 查看组详情
dcgmi group -g 1 -i
# 删除组
dcgmi group -d 1
# 使用组进行操作
dcgmi health -g 1 -c # 对组内所有 GPU 运行健康检查
# ==================== 健康检查 ====================
# 查看健康检查状态
dcgmi health -g 1 -c
# 输出示例:
# +----------------------------------------------------------------------------+
# | Health Monitor Report |
# +============+========+=======================================================+
# | GPU ID | Health | Info |
# +============+========+=======================================================+
# | 0 | Pass | All health checks passed |
# | 1 | Pass | All health checks passed |
# +------------+--------+-------------------------------------------------------+
# 设置健康监控类型
dcgmi health -g 1 -s p # PCIe 检查
dcgmi health -g 1 -s m # 内存检查
dcgmi health -g 1 -s i # 内部检查
dcgmi health -g 1 -s t # 热量检查
dcgmi health -g 1 -s n # NVLink 检查
dcgmi health -g 1 -s a # 所有检查
# 持续健康监控
dcgmi health -g 1 -c -j # JSON 格式输出
# ==================== 诊断测试 ====================
# 运行诊断测试 (不同级别)
dcgmi diag -g 1 -r 1 # Level 1: 快速测试 (~15秒)
dcgmi diag -g 1 -r 2 # Level 2: 中等测试 (~2分钟)
dcgmi diag -g 1 -r 3 # Level 3: 完整测试 (~10分钟)
# Level 3 测试包括:
# - SM Stress Test (计算压力测试)
# - Diagnostic (诊断测试)
# - PCIe Bandwidth Test (PCIe 带宽测试)
# - Memory Bandwidth Test (显存带宽测试)
# - Targeted Stress Test (定向压力测试)
# 指定测试
dcgmi diag -g 1 -r 3 -j # JSON 格式输出
# ==================== 策略管理 ====================
# 设置策略 (当条件满足时触发动作)
dcgmi policy -g 1 --set 0,0 -t 85 -T 90 -p 350
# --set: 设置策略
# -t: 温度警告阈值
# -T: 温度临界阈值
# -p: 功耗限制
# 查看策略
dcgmi policy -g 1 -l
# 清除策略
dcgmi policy -g 1 --clear
# ==================== 配置管理 ====================
# 查看配置
dcgmi config -g 1 --get
# 设置配置
dcgmi config -g 1 --set -c 1410 -m 1593 # 设置时钟
dcgmi config -g 1 --set -p 300 # 设置功耗限制
# 强制配置 (覆盖当前设置)
dcgmi config -g 1 --enforce
# ==================== 性能统计 ====================
# 启动统计收集
dcgmi stats -g 1 -e
# 查看统计
dcgmi stats -g 1 -v
# 导出统计
dcgmi stats -g 1 -x stats_export.json
# 停止统计收集
dcgmi stats -g 1 -d
# ==================== 字段查询 ====================
# 列出所有可查询字段
dcgmi dmon --list
# 查询特定字段
dcgmi dmon -e 150,155,203,204,1001,1002 -d 1000
# -e: 字段 ID 列表
# -d: 采样间隔 (毫秒)
# 常用字段 ID:
# 100: 驱动版本
# 101: NVML 版本
# 140: 序列号
# 150: SM 时钟
# 155: 内存时钟
# 156: 视频时钟
# 203: GPU 温度
# 204: 功耗
# 206: 总能耗
# 210: 风扇转速
# 220: PCIe 发送吞吐
# 221: PCIe 接收吞吐
# 230: PCIe 重放次数
# 240: ECC 模式
# 310: 显存带宽利用率
# 312: 编码器利用率
# 313: 解码器利用率
# 1001: GPU 利用率
# 1002: 显存利用率
# 1004-1008: 各种 ECC 错误计数
2.4 dcgm-exporter (Prometheus 集成)
# dcgm-exporter 是 DCGM 的 Prometheus 导出器
# 将 GPU 指标暴露为 Prometheus 格式
# ==================== Kubernetes 部署 ====================
# dcgm-exporter DaemonSet
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: dcgm-exporter
namespace: monitoring
labels:
app: dcgm-exporter
spec:
selector:
matchLabels:
app: dcgm-exporter
template:
metadata:
labels:
app: dcgm-exporter
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9400"
spec:
nodeSelector:
nvidia.com/gpu: "true"
containers:
- name: dcgm-exporter
image: nvcr.io/nvidia/k8s/dcgm-exporter:3.3.0-3.2.0-ubuntu22.04
ports:
- name: metrics
containerPort: 9400
securityContext:
runAsNonRoot: false
runAsUser: 0
env:
- name: DCGM_EXPORTER_LISTEN
value: ":9400"
- name: DCGM_EXPORTER_KUBERNETES
value: "true"
- name: DCGM_EXPORTER_COLLECTORS
value: "/etc/dcgm-exporter/default-counters.csv"
volumeMounts:
- name: pod-resources
mountPath: /var/lib/kubelet/pod-resources
readOnly: true
- name: dcgm-counters
mountPath: /etc/dcgm-exporter
volumes:
- name: pod-resources
hostPath:
path: /var/lib/kubelet/pod-resources
- name: dcgm-counters
configMap:
name: dcgm-exporter-counters
---
# Service for Prometheus to scrape
apiVersion: v1
kind: Service
metadata:
name: dcgm-exporter
namespace: monitoring
labels:
app: dcgm-exporter
spec:
ports:
- name: metrics
port: 9400
targetPort: 9400
selector:
app: dcgm-exporter
---
# ServiceMonitor for Prometheus Operator
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: dcgm-exporter
namespace: monitoring
spec:
selector:
matchLabels:
app: dcgm-exporter
endpoints:
- port: metrics
interval: 15s
---
# ConfigMap for custom counters
apiVersion: v1
kind: ConfigMap
metadata:
name: dcgm-exporter-counters
namespace: monitoring
data:
default-counters.csv: |
# 格式: DCGM 字段, Prometheus 指标名, 类型, 帮助信息
# 可用类型: gauge, counter
# 基础指标
DCGM_FI_DEV_SM_CLOCK, DCGM_FI_DEV_SM_CLOCK, gauge, SM clock frequency (MHz)
DCGM_FI_DEV_MEM_CLOCK, DCGM_FI_DEV_MEM_CLOCK, gauge, Memory clock frequency (MHz)
DCGM_FI_DEV_GPU_TEMP, DCGM_FI_DEV_GPU_TEMP, gauge, GPU temperature (C)
DCGM_FI_DEV_MEMORY_TEMP, DCGM_FI_DEV_MEMORY_TEMP, gauge, Memory temperature (C)
DCGM_FI_DEV_POWER_USAGE, DCGM_FI_DEV_POWER_USAGE, gauge, Power usage (W)
DCGM_FI_DEV_TOTAL_ENERGY_CONSUMPTION, DCGM_FI_DEV_TOTAL_ENERGY_CONSUMPTION, counter, Total energy consumption (mJ)
# 利用率指标
DCGM_FI_DEV_GPU_UTIL, DCGM_FI_DEV_GPU_UTIL, gauge, GPU utilization (%)
DCGM_FI_DEV_MEM_COPY_UTIL, DCGM_FI_DEV_MEM_COPY_UTIL, gauge, Memory utilization (%)
DCGM_FI_DEV_ENC_UTIL, DCGM_FI_DEV_ENC_UTIL, gauge, Encoder utilization (%)
DCGM_FI_DEV_DEC_UTIL, DCGM_FI_DEV_DEC_UTIL, gauge, Decoder utilization (%)
# 显存指标
DCGM_FI_DEV_FB_FREE, DCGM_FI_DEV_FB_FREE, gauge, Framebuffer free memory (MiB)
DCGM_FI_DEV_FB_USED, DCGM_FI_DEV_FB_USED, gauge, Framebuffer used memory (MiB)
# PCIe 指标
DCGM_FI_DEV_PCIE_TX_THROUGHPUT, DCGM_FI_DEV_PCIE_TX_THROUGHPUT, counter, PCIe TX throughput (bytes)
DCGM_FI_DEV_PCIE_RX_THROUGHPUT, DCGM_FI_DEV_PCIE_RX_THROUGHPUT, counter, PCIe RX throughput (bytes)
DCGM_FI_DEV_PCIE_REPLAY_COUNTER, DCGM_FI_DEV_PCIE_REPLAY_COUNTER, counter, PCIe replay count
# NVLink 指标 (如果适用)
DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL, DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTAL, counter, NVLink total bandwidth (bytes)
# ECC 错误
DCGM_FI_DEV_ECC_SBE_VOL_TOTAL, DCGM_FI_DEV_ECC_SBE_VOL_TOTAL, counter, Single-bit ECC errors
DCGM_FI_DEV_ECC_DBE_VOL_TOTAL, DCGM_FI_DEV_ECC_DBE_VOL_TOTAL, counter, Double-bit ECC errors
# 性能指标
DCGM_FI_PROF_GR_ENGINE_ACTIVE, DCGM_FI_PROF_GR_ENGINE_ACTIVE, gauge, Ratio of time graphics engine is active
DCGM_FI_PROF_SM_ACTIVE, DCGM_FI_PROF_SM_ACTIVE, gauge, Ratio of cycles SM is active
DCGM_FI_PROF_SM_OCCUPANCY, DCGM_FI_PROF_SM_OCCUPANCY, gauge, Ratio of warps resident on SMs
DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, DCGM_FI_PROF_PIPE_TENSOR_ACTIVE, gauge, Ratio of cycles tensor (HMMA) pipe is active
DCGM_FI_PROF_DRAM_ACTIVE, DCGM_FI_PROF_DRAM_ACTIVE, gauge, Ratio of cycles DRAM interface is active
DCGM_FI_PROF_PCIE_TX_BYTES, DCGM_FI_PROF_PCIE_TX_BYTES, counter, PCIe TX bytes
DCGM_FI_PROF_PCIE_RX_BYTES, DCGM_FI_PROF_PCIE_RX_BYTES, counter, PCIe RX bytes
# Docker 方式运行 dcgm-exporter
docker run -d --gpus all --rm \
-p 9400:9400 \
--name dcgm-exporter \
nvcr.io/nvidia/k8s/dcgm-exporter:3.3.0-3.2.0-ubuntu22.04
# 验证指标
curl localhost:9400/metrics
# 输出示例:
# DCGM_FI_DEV_SM_CLOCK{gpu="0",UUID="GPU-xxx",device="nvidia0"} 1410
# DCGM_FI_DEV_GPU_TEMP{gpu="0",UUID="GPU-xxx",device="nvidia0"} 32
# DCGM_FI_DEV_POWER_USAGE{gpu="0",UUID="GPU-xxx",device="nvidia0"} 52
# DCGM_FI_DEV_GPU_UTIL{gpu="0",UUID="GPU-xxx",device="nvidia0"} 0
# DCGM_FI_DEV_FB_FREE{gpu="0",UUID="GPU-xxx",device="nvidia0"} 81508
# DCGM_FI_DEV_FB_USED{gpu="0",UUID="GPU-xxx",device="nvidia0"} 412
2.5 DCGM Python API
#!/usr/bin/env python3
"""
dcgm_monitor.py - 使用 DCGM Python API 进行 GPU 监控
"""
import pydcgm
import dcgm_fields
import dcgm_structs
import time
from typing import List, Dict, Optional
import json
class DCGMMonitor:
"""DCGM 监控封装类"""
def __init__(self, embedded: bool = True):
"""
初始化 DCGM 连接
Args:
embedded: True 使用嵌入模式, False 连接到 nv-hostengine
"""
if embedded:
self.handle = pydcgm.DcgmHandle(
opMode=dcgm_structs.DCGM_OPERATION_MODE_AUTO
)
else:
self.handle = pydcgm.DcgmHandle(
ipAddress='127.0.0.1',
opMode=dcgm_structs.DCGM_OPERATION_MODE_AUTO
)
self.system = self.handle.GetSystem()
self.group = None
def discover_gpus(self) -> List[int]:
"""发现所有 GPU"""
gpu_ids = self.system.discovery.GetAllSupportedGpuIds()
return list(gpu_ids)
def create_group(self, name: str, gpu_ids: Optional[List[int]] = None) -> pydcgm.DcgmGroup:
"""创建 GPU 组"""
if gpu_ids is None:
gpu_ids = self.discover_gpus()
self.group = self.system.GetGroupWithGpuIds(name, gpu_ids)
return self.group
def get_gpu_info(self, gpu_id: int) -> Dict:
"""获取 GPU 基本信息"""
attributes = self.system.discovery.GetGpuAttributes(gpu_id)
return {
'gpu_id': gpu_id,
'name': attributes.identifiers.deviceName,
'uuid': attributes.identifiers.uuid,
'pci_bus_id': attributes.identifiers.pciBusId,
'serial': attributes.identifiers.serial,
'driver_version': attributes.identifiers.driverVersion,
'memory_total': attributes.memoryInfo.memorySizeGb,
}
def get_gpu_metrics(self, gpu_id: int) -> Dict:
"""获取 GPU 实时指标"""
field_ids = [
dcgm_fields.DCGM_FI_DEV_GPU_TEMP,
dcgm_fields.DCGM_FI_DEV_POWER_USAGE,
dcgm_fields.DCGM_FI_DEV_GPU_UTIL,
dcgm_fields.DCGM_FI_DEV_MEM_COPY_UTIL,
dcgm_fields.DCGM_FI_DEV_FB_FREE,
dcgm_fields.DCGM_FI_DEV_FB_USED,
dcgm_fields.DCGM_FI_DEV_SM_CLOCK,
dcgm_fields.DCGM_FI_DEV_MEM_CLOCK,
dcgm_fields.DCGM_FI_DEV_ECC_SBE_VOL_TOTAL,
dcgm_fields.DCGM_FI_DEV_ECC_DBE_VOL_TOTAL,
]
field_group = pydcgm.DcgmFieldGroup(
self.handle,
name="metrics_group",
fieldIds=field_ids
)
# 创建临时组用于查询
temp_group = self.system.GetGroupWithGpuIds("temp", [gpu_id])
# 启用监控
temp_group.samples.WatchFields(
field_group,
updateFreq=1000000, # 1秒
maxKeepAge=10.0,
maxKeepSamples=10
)
time.sleep(1) # 等待数据收集
# 获取数据
data = temp_group.samples.GetLatest(field_group)
metrics = {}
for gpu_data in data.values:
for field_data in gpu_data:
field_id = field_data.fieldId
value = field_data.value
field_name = dcgm_fields.DcgmFieldGetById(field_id).tag
metrics[field_name] = value
# 清理
field_group.Delete()
temp_group.Delete()
return metrics
def run_health_check(self, gpu_ids: Optional[List[int]] = None) -> Dict:
"""运行健康检查"""
if self.group is None:
self.create_group("health_check_group", gpu_ids)
health = self.group.health
# 设置健康监控
health.Set(dcgm_structs.DCGM_HEALTH_WATCH_ALL)
# 获取健康状态
result = health.Check()
return {
'overall_health': result.overallHealth,
'incidents': [
{
'gpu_id': incident.entityInfo.entityId,
'health': incident.health,
'error_string': incident.error.msg,
}
for incident in result.incidents
]
}
def run_diagnostic(self, level: int = 1, gpu_ids: Optional[List[int]] = None) -> Dict:
"""
运行诊断测试
Args:
level: 诊断级别 1-3
"""
if self.group is None:
self.create_group("diag_group", gpu_ids)
diag = self.group.action.RunDiagnostic(
dcgm_structs.DCGM_DIAG_LVL_SHORT if level == 1 else
dcgm_structs.DCGM_DIAG_LVL_MED if level == 2 else
dcgm_structs.DCGM_DIAG_LVL_LONG
)
results = []
for test_result in diag.perGpuResponses:
results.append({
'gpu_id': test_result.gpuId,
'status': test_result.status,
'info': test_result.info,
'warning': test_result.warning,
})
return {
'level': level,
'version': diag.version,
'results': results
}
def close(self):
"""关闭 DCGM 连接"""
if self.group:
self.group.Delete()
self.handle.Shutdown()
def main():
monitor = DCGMMonitor(embedded=True)
try:
# 发现 GPU
gpu_ids = monitor.discover_gpus()
print(f"Found {len(gpu_ids)} GPU(s)")
# 获取每个 GPU 信息
for gpu_id in gpu_ids:
info = monitor.get_gpu_info(gpu_id)
print(f"\nGPU {gpu_id}:")
print(json.dumps(info, indent=2))
metrics = monitor.get_gpu_metrics(gpu_id)
print("Metrics:")
print(json.dumps(metrics, indent=2))
# 运行健康检查
print("\n=== Health Check ===")
health = monitor.run_health_check()
print(json.dumps(health, indent=2))
# 运行快速诊断
print("\n=== Diagnostic (Level 1) ===")
diag = monitor.run_diagnostic(level=1)
print(json.dumps(diag, indent=2))
finally:
monitor.close()
if __name__ == '__main__':
main()
3. GPU 性能分析工具
3.1 NVIDIA Nsight Systems
# Nsight Systems - 系统级性能分析工具
# 分析 CPU-GPU 交互、kernel 调度、内存传输等
# ==================== 基础使用 ====================
# 采集 profile 数据
nsys profile -o my_report python train.py
# 指定输出格式
nsys profile -o my_report --export=sqlite,json python train.py
# 只采集 CUDA 活动
nsys profile --trace=cuda python train.py
# 采集多种活动
nsys profile --trace=cuda,nvtx,osrt python train.py
# cuda: CUDA API 和 kernel
# nvtx: NVIDIA Tools Extension 标注
# osrt: OS Runtime (系统调用)
# ==================== 高级选项 ====================
# 延迟开始采集
nsys profile --delay=10 -d 30 python train.py
# --delay=10: 启动后等待 10 秒开始采集
# -d 30: 采集 30 秒
# 设置采样频率
nsys profile --sampling-frequency=10000 python train.py
# 采集 GPU 指标
nsys profile --gpu-metrics-device=all python train.py
# 采集 CUDA 内存操作
nsys profile --cuda-memory-usage=true python train.py
# ==================== 分析报告 ====================
# 查看报告统计
nsys stats my_report.nsys-rep
# 导出为其他格式
nsys export --type=json my_report.nsys-rep
# 在 GUI 中打开 (需要图形界面)
nsys-ui my_report.nsys-rep
# ==================== 常见分析场景 ====================
# 1. 分析训练循环
nsys profile \
--trace=cuda,nvtx \
--cuda-memory-usage=true \
--gpu-metrics-device=all \
-o training_profile \
python train.py --epochs=1
# 2. 分析推理延迟
nsys profile \
--trace=cuda \
--stats=true \
-o inference_profile \
python inference.py
# 3. 分析数据加载
nsys profile \
--trace=cuda,osrt \
--sample=cpu \
-o dataload_profile \
python train.py
# 使用 NVTX 标注代码区域
import torch
import nvtx
class TrainingLoop:
def __init__(self, model, optimizer):
self.model = model
self.optimizer = optimizer
def train_epoch(self, dataloader):
for batch_idx, (data, target) in enumerate(dataloader):
# 标注数据传输
with nvtx.annotate("Data to GPU", color="blue"):
data = data.cuda()
target = target.cuda()
# 标注前向传播
with nvtx.annotate("Forward", color="green"):
output = self.model(data)
loss = F.cross_entropy(output, target)
# 标注反向传播
with nvtx.annotate("Backward", color="red"):
self.optimizer.zero_grad()
loss.backward()
# 标注优化器更新
with nvtx.annotate("Optimizer Step", color="yellow"):
self.optimizer.step()
# PyTorch 内置的 profiler 也可以导出 nsys 格式
with torch.profiler.profile(
activities=[
torch.profiler.ProfilerActivity.CPU,
torch.profiler.ProfilerActivity.CUDA,
],
schedule=torch.profiler.schedule(wait=1, warmup=1, active=3),
on_trace_ready=torch.profiler.tensorboard_trace_handler('./log'),
record_shapes=True,
with_stack=True
) as prof:
for step in range(5):
train_step()
prof.step()
3.2 NVIDIA Nsight Compute
# Nsight Compute - Kernel 级性能分析工具
# 深入分析单个 CUDA kernel 的性能
# ==================== 基础使用 ====================
# 分析所有 kernel
ncu python train.py
# 分析特定 kernel
ncu --kernel-name "volta_sgemm" python train.py
# 分析前 N 个 kernel
ncu --launch-count 10 python train.py
# 跳过前 N 个 kernel
ncu --launch-skip 100 --launch-count 10 python train.py
# ==================== 指标集 ====================
# 使用预定义指标集
ncu --set full python train.py
# full: 完整指标集
# detailed: 详细指标
# basic: 基础指标
# roofline: Roofline 分析
# 指定单个指标
ncu --metrics sm__throughput.avg.pct_of_peak_sustained_elapsed python train.py
# 指定多个指标
ncu --metrics \
sm__throughput.avg.pct_of_peak_sustained_elapsed,\
dram__throughput.avg.pct_of_peak_sustained_elapsed,\
gpu__compute_memory_throughput.avg.pct_of_peak_sustained_elapsed \
python train.py
# ==================== 输出格式 ====================
# 输出到文件
ncu -o kernel_analysis python train.py
# CSV 格式
ncu --csv python train.py > kernel_metrics.csv
# 页面格式 (适合查看)
ncu --page raw python train.py
# ==================== 高级分析 ====================
# Roofline 分析
ncu --set roofline -o roofline_report python train.py
# 源码关联 (需要编译时带 -lineinfo)
ncu --set source python train.py
# 比较两次运行
ncu --import baseline.ncu-rep --import optimized.ncu-rep
# ==================== 常用指标解释 ====================
# Throughput 指标:
# sm__throughput: SM 吞吐量 (计算利用率)
# dram__throughput: 显存带宽利用率
# 执行指标:
# sm__warps_active: 活跃的 warps 数
# sm__cycles_elapsed: 执行周期数
# 内存指标:
# l1tex__t_bytes: L1 cache 访问字节
# lts__t_bytes: L2 cache 访问字节
# dram__bytes: 显存访问字节
# 指令指标:
# sm__inst_executed: 执行的指令数
# sm__sass_inst_executed_op_tensor: Tensor Core 指令数
3.3 PyTorch Profiler
#!/usr/bin/env python3
"""
pytorch_profiler.py - PyTorch GPU 性能分析
"""
import torch
import torch.nn as nn
import torch.profiler
from torch.profiler import profile, record_function, ProfilerActivity
import time
def create_model():
"""创建示例模型"""
return nn.Sequential(
nn.Linear(1024, 4096),
nn.ReLU(),
nn.Linear(4096, 4096),
nn.ReLU(),
nn.Linear(4096, 1024),
).cuda()
def profile_basic():
"""基础性能分析"""
model = create_model()
x = torch.randn(32, 1024).cuda()
# 预热
for _ in range(10):
model(x)
torch.cuda.synchronize()
# 性能分析
with profile(
activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
record_shapes=True,
profile_memory=True,
with_stack=True,
) as prof:
with record_function("model_inference"):
output = model(x)
# 打印结果
print(prof.key_averages().table(
sort_by="cuda_time_total",
row_limit=20
))
# 导出 Chrome trace
prof.export_chrome_trace("trace.json")
# 导出 stacks (火焰图)
prof.export_stacks("stacks.txt", "self_cuda_time_total")
def profile_training_loop():
"""分析训练循环"""
model = create_model()
optimizer = torch.optim.Adam(model.parameters())
criterion = nn.MSELoss()
x = torch.randn(32, 1024).cuda()
y = torch.randn(32, 1024).cuda()
# 使用 schedule 控制采集
with profile(
activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
schedule=torch.profiler.schedule(
wait=1, # 等待 1 步
warmup=1, # 预热 1 步
active=3, # 采集 3 步
repeat=2 # 重复 2 次
),
on_trace_ready=torch.profiler.tensorboard_trace_handler('./log/profiler'),
record_shapes=True,
profile_memory=True,
with_stack=True,
) as prof:
for step in range(10):
with record_function("forward"):
output = model(x)
loss = criterion(output, y)
with record_function("backward"):
optimizer.zero_grad()
loss.backward()
with record_function("optimizer"):
optimizer.step()
prof.step() # 标记步结束
def profile_memory():
"""内存分析"""
model = create_model()
# 重置内存统计
torch.cuda.reset_peak_memory_stats()
torch.cuda.empty_cache()
print(f"Initial memory: {torch.cuda.memory_allocated() / 1024**2:.2f} MB")
with profile(
activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
profile_memory=True,
record_shapes=True,
) as prof:
x = torch.randn(64, 1024).cuda()
print(f"After input: {torch.cuda.memory_allocated() / 1024**2:.2f} MB")
output = model(x)
print(f"After forward: {torch.cuda.memory_allocated() / 1024**2:.2f} MB")
loss = output.sum()
loss.backward()
print(f"After backward: {torch.cuda.memory_allocated() / 1024**2:.2f} MB")
# 打印内存事件
print("\n=== Memory Events ===")
print(prof.key_averages().table(
sort_by="self_cuda_memory_usage",
row_limit=10
))
print(f"\nPeak memory: {torch.cuda.max_memory_allocated() / 1024**2:.2f} MB")
def profile_custom_kernels():
"""分析自定义操作"""
model = create_model()
x = torch.randn(32, 1024).cuda()
with profile(
activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
with_stack=True,
) as prof:
# 标注不同阶段
with record_function("stage_1_preprocess"):
x_norm = (x - x.mean()) / x.std()
with record_function("stage_2_inference"):
output = model(x_norm)
with record_function("stage_3_postprocess"):
result = torch.softmax(output, dim=-1)
# 打印按阶段分组的结果
print(prof.key_averages(group_by_input_shape=True).table(
sort_by="cuda_time_total",
row_limit=20
))
class GPUMemoryTracker:
"""GPU 内存追踪器"""
def __init__(self, device=0):
self.device = device
self.history = []
def snapshot(self, tag: str = ""):
"""记录当前内存状态"""
self.history.append({
'tag': tag,
'allocated': torch.cuda.memory_allocated(self.device),
'reserved': torch.cuda.memory_reserved(self.device),
'max_allocated': torch.cuda.max_memory_allocated(self.device),
'timestamp': time.time()
})
def report(self):
"""生成报告"""
print("\n=== GPU Memory Report ===")
print(f"{'Tag':<30} {'Allocated':<15} {'Reserved':<15} {'Max Alloc':<15}")
print("-" * 75)
for h in self.history:
print(f"{h['tag']:<30} "
f"{h['allocated']/1024**2:>12.2f} MB "
f"{h['reserved']/1024**2:>12.2f} MB "
f"{h['max_allocated']/1024**2:>12.2f} MB")
def reset(self):
"""重置统计"""
self.history = []
torch.cuda.reset_peak_memory_stats(self.device)
torch.cuda.empty_cache()
def analyze_tensor_core_usage():
"""分析 Tensor Core 使用情况"""
# Tensor Core 需要特定的数据类型和维度
# FP16 或 BF16, 矩阵维度是 8 的倍数
# 不使用 Tensor Core
a = torch.randn(1000, 1000).cuda()
b = torch.randn(1000, 1000).cuda()
with profile(activities=[ProfilerActivity.CUDA]) as prof:
with record_function("FP32_matmul"):
c = torch.mm(a, b)
print("FP32 (No Tensor Core):")
print(prof.key_averages().table(row_limit=5))
# 使用 Tensor Core (FP16, 维度是 8 的倍数)
a_fp16 = torch.randn(1024, 1024, dtype=torch.float16).cuda()
b_fp16 = torch.randn(1024, 1024, dtype=torch.float16).cuda()
with profile(activities=[ProfilerActivity.CUDA]) as prof:
with record_function("FP16_matmul"):
c_fp16 = torch.mm(a_fp16, b_fp16)
print("\nFP16 (With Tensor Core):")
print(prof.key_averages().table(row_limit=5))
if __name__ == '__main__':
print("=== Basic Profiling ===")
profile_basic()
print("\n=== Training Loop Profiling ===")
profile_training_loop()
print("\n=== Memory Profiling ===")
profile_memory()
print("\n=== Custom Kernels Profiling ===")
profile_custom_kernels()
print("\n=== Tensor Core Analysis ===")
analyze_tensor_core_usage()
4. GPU 问题排查
4.1 常见问题诊断
┌─────────────────────────────────────────────────────────────────────┐
│ GPU 问题排查流程图 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 问题现象 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 1. 检查 GPU 是否被识别 │ │
│ │ nvidia-smi │ │
│ │ lspci | grep -i nvidia │ │
│ │ │ │
│ │ ✗ 未识别 → 检查硬件连接、驱动安装 │ │
│ │ ✓ 已识别 → 继续下一步 │ │
│ └──────────────────────────┬──────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 2. 检查驱动状态 │ │
│ │ cat /proc/driver/nvidia/version │ │
│ │ dmesg | grep -i nvidia │ │
│ │ │ │
│ │ ✗ 驱动错误 → 重新安装驱动 │ │
│ │ ✓ 驱动正常 → 继续下一步 │ │
│ └──────────────────────────┬──────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 3. 检查 GPU 健康状态 │ │
│ │ nvidia-smi -q -d ECC # ECC 错误 │ │
│ │ nvidia-smi -q -d POWER # 功耗状态 │ │
│ │ nvidia-smi -q -d TEMPERATURE # 温度 │ │
│ │ dcgmi health -c # DCGM 健康检查 │ │
│ │ │ │
│ │ ✗ 硬件问题 → 联系硬件支持 │ │
│ │ ✓ 硬件正常 → 继续下一步 │ │
│ └──────────────────────────┬──────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 4. 检查应用层问题 │ │
│ │ - CUDA 版本兼容性 │ │
│ │ - 显存不足 (OOM) │ │
│ │ - 进程冲突 │ │
│ │ - 性能问题 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
4.2 问题排查命令集
# ==================== 驱动和硬件检查 ====================
# 检查 GPU 是否被系统识别
lspci | grep -i nvidia
# 输出示例: 07:00.0 3D controller: NVIDIA Corporation GA100 [A100]
# 检查驱动版本
cat /proc/driver/nvidia/version
# 输出: NVRM version: NVIDIA UNIX x86_64 Kernel Module 535.104.05
# 检查内核模块
lsmod | grep nvidia
# nvidia_uvm, nvidia_drm, nvidia_modeset, nvidia
# 检查驱动加载日志
dmesg | grep -i nvidia | tail -50
# 检查 GPU 设备文件
ls -la /dev/nvidia*
# ==================== ECC 错误检查 ====================
# 查看 ECC 状态
nvidia-smi -q -d ECC
# 关键指标:
# - Volatile: 自上次驱动加载以来的错误
# - Aggregate: GPU 生命周期内的总错误
# 如果看到大量 ECC 错误,特别是 Double Bit 错误:
# - 可能是硬件问题
# - 需要联系供应商更换
# 清除 ECC 计数器 (用于排查是否是偶发)
sudo nvidia-smi -p 0 # 清除 volatile
# 然后运行一段时间工作负载,再检查
# ==================== 温度问题排查 ====================
# 查看温度
nvidia-smi -q -d TEMPERATURE
# 关键阈值:
# - GPU Shutdown Temp: 通常 90-100°C
# - GPU Slowdown Temp: 通常 80-85°C
# 如果温度过高:
# 1. 检查散热系统
# 2. 检查机房温度
# 3. 检查 GPU 负载是否异常
# 4. 考虑降低功耗限制
# 监控温度变化
nvidia-smi dmon -s t -d 1000 # 每秒采集一次温度
# ==================== 功耗问题排查 ====================
# 查看功耗状态
nvidia-smi -q -d POWER
# 检查是否被限制
nvidia-smi -q | grep "Performance State"
# P0 = 最高性能
# 如果不在 P0,检查:
# - 功耗限制
# - 温度限制
# - 时钟限制
# 查看限制原因
nvidia-smi -q | grep -A 10 "Clocks Throttle Reasons"
# Idle: GPU 空闲
# Applications Clocks Setting: 应用时钟设置
# SW Power Cap: 软件功耗限制
# HW Slowdown: 硬件降频 (温度/功耗)
# Sync Boost: 同步加速限制
# SW Thermal Slowdown: 软件温度降频
# HW Thermal Slowdown: 硬件温度降频
# HW Power Brake Slowdown: 功耗制动
# ==================== 显存问题排查 ====================
# 查看显存使用
nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv
# 查找显存泄漏
# 1. 记录初始状态
nvidia-smi --query-gpu=memory.used --format=csv,noheader,nounits
# 2. 运行一段时间后再检查
# 3. 如果持续增长,可能存在泄漏
# 清理显存 (PyTorch)
# torch.cuda.empty_cache()
# 强制杀死占用 GPU 的进程
sudo fuser -v /dev/nvidia*
sudo kill -9 <pid>
# ==================== PCIe 问题排查 ====================
# 查看 PCIe 信息
nvidia-smi -q -d PCIE
# 关键检查:
# - Link Gen: 应该是 Gen4 或 Gen5
# - Link Width: 应该是 x16
# 如果 Link Width 降级 (如变成 x1):
# - 检查物理连接
# - 检查主板 BIOS 设置
# - 检查 PCIe 插槽是否正常
# 查看 PCIe 带宽测试
nvidia-smi topo -m
# 运行带宽测试
dcgmi diag -r 2 # 包含 PCIe 带宽测试
# ==================== NVLink 问题排查 ====================
# 查看 NVLink 状态
nvidia-smi nvlink -s
# 查看 NVLink 错误计数
nvidia-smi nvlink -e
# 查看 NVLink 拓扑
nvidia-smi topo -m
# ==================== 进程问题排查 ====================
# 查看使用 GPU 的进程
nvidia-smi pmon -s um
# 查看进程详情
nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv
# 检查 zombie GPU 进程
# 有时进程退出但 GPU 资源未释放
ps aux | grep -E "python|cuda" | grep -v grep
nvidia-smi
# 如果发现不一致,尝试:
sudo nvidia-smi -r # 重置 GPU (会杀死所有进程)
4.3 性能问题排查
# ==================== GPU 利用率低 ====================
# 诊断步骤:
# 1. 检查 GPU 利用率
nvidia-smi dmon -s u -d 1000
# 2. 如果 GPU 利用率低,可能原因:
# a. CPU 瓶颈 (数据加载跟不上)
# b. 内存带宽瓶颈
# c. I/O 瓶颈
# d. 同步点太多
# 3. 使用 nsys 分析
nsys profile --trace=cuda,osrt python train.py
# 查看 CUDA API 调用时间分布
# 如果 cudaMemcpy 占比高 → 数据传输瓶颈
# 如果 cudaStreamSynchronize 占比高 → 同步开销大
# 4. 检查数据加载
# PyTorch DataLoader num_workers 设置
# 使用 pin_memory=True
# 使用 prefetch_factor
# ==================== 显存带宽瓶颈 ====================
# 1. 检查显存带宽利用率
nvidia-smi dmon -s u -d 1000
# 看 Memory Utilization 列
# 2. 使用 ncu 分析
ncu --metrics dram__throughput.avg.pct_of_peak_sustained_elapsed python inference.py
# 如果接近峰值,说明是带宽瓶颈
# 优化方法:
# - 使用更小的数据类型 (FP16/INT8)
# - 减少内存访问 (算子融合)
# - 使用 Flash Attention 等优化算法
# ==================== Tensor Core 未使用 ====================
# 检查是否使用 Tensor Core
ncu --metrics sm__inst_executed_pipe_tensor python train.py
# 如果 Tensor 指令数为 0,检查:
# 1. 数据类型: 需要 FP16/BF16/TF32
# 2. 矩阵维度: 需要是 8 的倍数
# 3. 是否启用 TF32: torch.backends.cuda.matmul.allow_tf32 = True
# ==================== CUDA kernel 优化 ====================
# 使用 ncu 分析单个 kernel
ncu --set full --kernel-name "ampere_sgemm" python train.py
# 关注指标:
# - SM Throughput: 计算利用率
# - Memory Throughput: 内存带宽利用率
# - Achieved Occupancy: SM 占用率
# Roofline 分析
ncu --set roofline -o roofline python train.py
4.4 容器中的 GPU 问题排查
# ==================== 容器 GPU 访问问题 ====================
# 1. 检查容器是否能看到 GPU
docker run --rm --gpus all nvidia/cuda:12.0-base nvidia-smi
# 2. 如果失败,检查 nvidia-container-runtime
nvidia-container-cli info
# 3. 检查 Docker 配置
cat /etc/docker/daemon.json | grep nvidia
# 4. 检查容器内的设备
docker run --rm --gpus all nvidia/cuda:12.0-base ls -la /dev/nvidia*
# 5. 检查驱动库是否正确挂载
docker run --rm --gpus all nvidia/cuda:12.0-base ldconfig -p | grep nvidia
# ==================== Kubernetes GPU 问题 ====================
# 1. 检查节点 GPU 资源
kubectl describe node <node-name> | grep -A 10 "Allocatable"
# 2. 检查 device plugin 状态
kubectl get pods -n kube-system | grep nvidia
kubectl logs -n kube-system <nvidia-device-plugin-pod>
# 3. 检查 Pod 是否获得 GPU
kubectl describe pod <pod-name> | grep -A 5 "Limits"
# 4. 进入 Pod 检查 GPU
kubectl exec -it <pod-name> -- nvidia-smi
# 5. 检查 MIG 配置 (如果使用)
kubectl get node <node> -o jsonpath='{.status.allocatable}' | jq
# ==================== 常见容器问题 ====================
# 问题: "Failed to initialize NVML"
# 原因: 驱动版本不匹配
# 解决: 使用与宿主机驱动兼容的 CUDA 镜像
# 问题: "no CUDA-capable device is detected"
# 原因: --gpus 参数未传递或配置错误
# 解决: 检查 Docker/containerd 配置
# 问题: 容器内 GPU 数量与预期不符
# 原因: NVIDIA_VISIBLE_DEVICES 环境变量
# 解决: 检查环境变量设置
# 问题: 容器内 GPU 性能低于预期
# 原因: 可能是共享 GPU 或 MIG
# 解决: 检查 GPU 共享配置
5. 生产环境监控体系
5.1 完整监控架构
┌─────────────────────────────────────────────────────────────────────────────┐
│ 生产环境 GPU 监控架构 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ │
│ │ Grafana │ │
│ │ Dashboard │ │
│ └────────┬────────┘ │
│ │ │
│ ┌────────┴────────┐ │
│ │ Prometheus │ │
│ │ │ │
│ │ - 指标存储 │ │
│ │ - 告警规则 │ │
│ │ - 查询引擎 │ │
│ └────────┬────────┘ │
│ │ │
│ ┌────────────────────────┬┴──────────────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │dcgm-exporter│ │node-exporter│ │kube-state-metrics│ │
│ │ │ │ │ │ │ │
│ │ GPU 指标 │ │ 系统指标 │ │ K8s 资源指标 │ │
│ │ - 利用率 │ │ - CPU │ │ - Pod 状态 │ │
│ │ - 显存 │ │ - 内存 │ │ - GPU 请求 │ │
│ │ - 温度 │ │ - 磁盘 │ │ │ │
│ │ - 功耗 │ │ - 网络 │ │ │ │
│ └──────┬──────┘ └──────┬──────┘ └────────┬────────┘ │
│ │ │ │ │
│ ┌──────┴──────┐ ┌──────┴──────┐ ┌────────┴────────┐ │
│ │ GPU Node │ │ All Nodes │ │ Kubernetes │ │
│ │ │ │ │ │ API Server │ │
│ │ ┌───────┐ │ │ │ │ │ │
│ │ │ GPU 0 │ │ │ │ │ │ │
│ │ │ GPU 1 │ │ │ │ │ │ │
│ │ │ ... │ │ │ │ │ │ │
│ │ └───────┘ │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
5.2 Prometheus 告警规则
# prometheus-gpu-alerts.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: gpu-alerts
namespace: monitoring
spec:
groups:
- name: gpu.rules
rules:
# GPU 温度告警
- alert: GPUTemperatureWarning
expr: DCGM_FI_DEV_GPU_TEMP > 75
for: 5m
labels:
severity: warning
annotations:
summary: "GPU temperature warning on {{ $labels.gpu }}"
description: "GPU {{ $labels.gpu }} on {{ $labels.instance }} temperature is {{ $value }}°C"
- alert: GPUTemperatureCritical
expr: DCGM_FI_DEV_GPU_TEMP > 85
for: 1m
labels:
severity: critical
annotations:
summary: "GPU temperature critical on {{ $labels.gpu }}"
description: "GPU {{ $labels.gpu }} on {{ $labels.instance }} temperature is {{ $value }}°C"
# GPU 显存告警
- alert: GPUMemoryUsageHigh
expr: (DCGM_FI_DEV_FB_USED / (DCGM_FI_DEV_FB_USED + DCGM_FI_DEV_FB_FREE)) * 100 > 90
for: 5m
labels:
severity: warning
annotations:
summary: "GPU memory usage high on {{ $labels.gpu }}"
description: "GPU {{ $labels.gpu }} memory usage is {{ $value | printf \"%.1f\" }}%"
# GPU 利用率告警 (低利用率可能表示问题)
- alert: GPULowUtilization
expr: DCGM_FI_DEV_GPU_UTIL < 10 and DCGM_FI_DEV_FB_USED > 1000
for: 30m
labels:
severity: warning
annotations:
summary: "GPU underutilized on {{ $labels.gpu }}"
description: "GPU {{ $labels.gpu }} has low utilization ({{ $value }}%) but memory is allocated"
# ECC 错误告警
- alert: GPUECCErrorsDetected
expr: increase(DCGM_FI_DEV_ECC_DBE_VOL_TOTAL[1h]) > 0
labels:
severity: critical
annotations:
summary: "GPU ECC double-bit errors detected on {{ $labels.gpu }}"
description: "GPU {{ $labels.gpu }} has ECC errors. Consider replacement."
# GPU 掉线告警
- alert: GPUDown
expr: up{job="dcgm-exporter"} == 0
for: 5m
labels:
severity: critical
annotations:
summary: "GPU monitoring down on {{ $labels.instance }}"
description: "dcgm-exporter is not responding on {{ $labels.instance }}"
# 功耗告警
- alert: GPUPowerLimitReached
expr: (DCGM_FI_DEV_POWER_USAGE / DCGM_FI_DEV_POWER_LIMIT) * 100 > 95
for: 5m
labels:
severity: warning
annotations:
summary: "GPU power limit reached on {{ $labels.gpu }}"
description: "GPU {{ $labels.gpu }} is at {{ $value | printf \"%.1f\" }}% of power limit"
# PCIe 错误告警
- alert: GPUPCIeReplayErrors
expr: increase(DCGM_FI_DEV_PCIE_REPLAY_COUNTER[1h]) > 100
labels:
severity: warning
annotations:
summary: "High PCIe replay errors on {{ $labels.gpu }}"
description: "GPU {{ $labels.gpu }} has high PCIe replay count"
# NVLink 错误告警
- alert: GPUNVLinkErrors
expr: increase(DCGM_FI_DEV_NVLINK_CRC_FLIT_ERROR_COUNT_TOTAL[1h]) > 0
labels:
severity: warning
annotations:
summary: "NVLink errors detected on {{ $labels.gpu }}"
description: "GPU {{ $labels.gpu }} has NVLink CRC errors"
- name: gpu.capacity
rules:
# GPU 资源碎片告警
- alert: GPUResourceFragmentation
expr: |
(
sum(kube_node_status_allocatable{resource="nvidia_com_gpu"}) -
sum(kube_pod_container_resource_requests{resource="nvidia_com_gpu"})
) > 2
and
count(kube_pod_status_phase{phase="Pending"} == 1) > 0
for: 15m
labels:
severity: warning
annotations:
summary: "GPU resource fragmentation detected"
description: "There are free GPUs but pending pods cannot be scheduled"
# GPU 利用率低于期望
- alert: ClusterGPUUnderutilized
expr: avg(DCGM_FI_DEV_GPU_UTIL) < 30
for: 1h
labels:
severity: info
annotations:
summary: "Cluster GPU underutilization"
description: "Average GPU utilization is {{ $value | printf \"%.1f\" }}%"
5.3 Grafana Dashboard
{
"dashboard": {
"title": "GPU Monitoring Dashboard",
"panels": [
{
"title": "GPU Utilization",
"type": "graph",
"targets": [
{
"expr": "DCGM_FI_DEV_GPU_UTIL",
"legendFormat": "GPU {{gpu}} - {{instance}}"
}
]
},
{
"title": "GPU Memory Usage",
"type": "graph",
"targets": [
{
"expr": "DCGM_FI_DEV_FB_USED / 1024",
"legendFormat": "GPU {{gpu}} Used (GiB)"
},
{
"expr": "(DCGM_FI_DEV_FB_USED + DCGM_FI_DEV_FB_FREE) / 1024",
"legendFormat": "GPU {{gpu}} Total (GiB)"
}
]
},
{
"title": "GPU Temperature",
"type": "gauge",
"targets": [
{
"expr": "DCGM_FI_DEV_GPU_TEMP"
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{"color": "green", "value": null},
{"color": "yellow", "value": 70},
{"color": "red", "value": 80}
]
}
},
{
"title": "GPU Power Usage",
"type": "graph",
"targets": [
{
"expr": "DCGM_FI_DEV_POWER_USAGE",
"legendFormat": "GPU {{gpu}} Power (W)"
}
]
},
{
"title": "GPU SM Clock",
"type": "graph",
"targets": [
{
"expr": "DCGM_FI_DEV_SM_CLOCK",
"legendFormat": "GPU {{gpu}} SM Clock (MHz)"
}
]
},
{
"title": "GPU Memory Bandwidth Utilization",
"type": "graph",
"targets": [
{
"expr": "DCGM_FI_DEV_MEM_COPY_UTIL",
"legendFormat": "GPU {{gpu}}"
}
]
},
{
"title": "ECC Errors (Uncorrected)",
"type": "stat",
"targets": [
{
"expr": "sum(DCGM_FI_DEV_ECC_DBE_VOL_TOTAL) by (gpu)"
}
]
},
{
"title": "PCIe Throughput",
"type": "graph",
"targets": [
{
"expr": "rate(DCGM_FI_DEV_PCIE_TX_THROUGHPUT[5m]) / 1024 / 1024",
"legendFormat": "GPU {{gpu}} TX (MB/s)"
},
{
"expr": "rate(DCGM_FI_DEV_PCIE_RX_THROUGHPUT[5m]) / 1024 / 1024",
"legendFormat": "GPU {{gpu}} RX (MB/s)"
}
]
}
]
}
}
6. 本章总结
6.1 核心知识点
┌─────────────────────────────────────────────────────────────────────┐
│ GPU 监控与调试知识图谱 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ nvidia-smi │
│ ├── 基础查询: nvidia-smi, nvidia-smi -q │
│ ├── 分类查询: -d MEMORY/UTILIZATION/TEMPERATURE/POWER/ECC │
│ ├── 格式化输出: --query-gpu, --format=csv │
│ ├── 实时监控: -l, dmon, pmon │
│ └── 配置管理: -pm, -pl, -lgc, -mig │
│ │
│ DCGM │
│ ├── 架构: nv-hostengine + libdcgm + dcgmi │
│ ├── 功能: GPU 分组、健康检查、诊断测试、策略管理 │
│ ├── 指标: 150+ 种指标,覆盖性能、健康、配置 │
│ └── 集成: dcgm-exporter → Prometheus → Grafana │
│ │
│ 性能分析工具 │
│ ├── Nsight Systems: 系统级分析 (CPU-GPU 交互) │
│ ├── Nsight Compute: Kernel 级分析 (Roofline) │
│ └── PyTorch Profiler: 深度学习框架级分析 │
│ │
│ 问题排查 │
│ ├── 硬件问题: ECC 错误、温度、功耗、PCIe/NVLink │
│ ├── 驱动问题: 版本兼容、模块加载 │
│ ├── 性能问题: 利用率低、带宽瓶颈、Tensor Core 未使用 │
│ └── 容器问题: GPU 访问、设备挂载、K8s 调度 │
│ │
│ 生产监控 │
│ ├── 指标采集: dcgm-exporter + Prometheus │
│ ├── 告警规则: 温度、显存、ECC、功耗 │
│ └── 可视化: Grafana Dashboard │
│ │
└─────────────────────────────────────────────────────────────────────┘
6.2 面试要点
nvidia-smi 和 DCGM 的区别?
- nvidia-smi: 交互式工具,适合简单查询和脚本
- DCGM: 数据中心级别,支持 GPU 分组、健康检查、远程访问、更多指标
如何判断 GPU 是否正常工作?
- 检查 ECC 错误(特别是 Double-Bit)
- 检查温度是否正常
- 检查功耗和时钟是否被限制
- 运行 DCGM 诊断测试
GPU 利用率低的可能原因?
- CPU 瓶颈(数据加载跟不上)
- I/O 瓶颈
- 同步点太多
- Batch size 太小
如何分析 CUDA kernel 性能?
- 使用 Nsight Compute 进行 kernel 级分析
- 关注 SM Throughput、Memory Throughput
- 使用 Roofline 分析定位瓶颈
生产环境如何监控 GPU?
- dcgm-exporter 采集指标
- Prometheus 存储和告警
- Grafana 可视化
- 设置合理的告警阈值
6.3 下一章预告
下一章我们将进入 Kubernetes GPU 调度,深入探讨:
- Device Plugin 机制
- GPU 调度器实现
- 拓扑感知调度
- 弹性 GPU 调度