Oamc

GPU 服务器常见故障处理

本页目录

场景

GPU 服务器(训练卡、推理卡、显卡服务器)跑着任务出问题了:卡不见了、 nvidia-smi 报错、显存占用异常、训练突然中断、推理结果不对、温度报警。

这篇按「现象 → 定位 → 处理」整理,都是实际会遇到的。11111

前置条件

  • 能 SSH 上服务器,或者有 IPMI / iDRAC / iLO 的远程控制台
  • 记下这台机器用的卡型(A100 / H100 / L40S / RTX 4090 / 国产卡等)和驱动版本
  • 有另一台同型号的机器能对照,出问题时好比对
  • 重要任务先确认有 checkpoint,别在故障状态硬跑
  • 驱动和 CUDA 版本先记下来,换驱动前一定要记

配置

1. nvidia-smi 找不到卡

最常见。按顺序查:

# 先看系统能不能看到卡
nvidia-smi
# 报错信息分几种:
#   "command not found"          → 驱动没装或 PATH 不对
#   "Failed to initialize NVML"  → 驱动加载了但和内核对不上
#   "No devices were found"      → 驱动在,但没找到硬件

三种情况分别处理:

驱动没装 / 装了但 PATH 不对

# 看驱动模块在不在
lsmod | grep nvidia
ls /proc/driver/nvidia/version

# 模块没加载,但文件在
modprobe nvidia
nvidia-smi

Failed to initialize NVML(驱动与内核版本不匹配)

这是升级内核或升级驱动之后最常见的。内核头文件和驱动版本对不上:

uname -r                                    # 当前内核
cat /proc/driver/nvidia/version             # 驱动带的内核版本
ls /usr/src/kernels/                        # 有没有对应的头文件

两种修法:

# 办法一:装当前内核的头文件,然后重装驱动
apt install linux-headers-$(uname -r)
# 重新安装对应的 .run 驱动包(会重新编译内核模块)

# 办法二:回退内核(更稳,但要重启)
# 编辑 /etc/default/grub,把 GRUB_DEFAULT 改成旧内核
# update-grub && reboot

No devices were found(驱动在,但卡不被识别)

# PCI 层面能不能看到卡
lspci | grep -i nvidia
lspci -nn | grep -i "3d controller\|nvidia"

# 内核有没有报错
dmesg -T | grep -iE "nvidia|nvrm|nouveau" | tail -30

几种典型:

"NVRM: Xid: ... GPU has fallen off the bus"   → 卡掉了,PCIe 链路问题(见第 4 节)
"NVRM: Failed to initialize GPU"            → 卡本身或供电有问题
"unable to map BAR"                          → 内存映射失败,可能是 IOMMU 或 PCIe 设置
"Nouveau ... kernel driver"                  → 装了开源驱动,抢占了独显

如果是 nouveau 抢占了:

# 禁用开源驱动
echo "blacklist nouveau" >> /etc/modprobe.d/blacklist-nouveau.conf
echo "options nouveau modeset=0" >> /etc/modprobe.d/blacklist-nouveau.conf
update-initramfs -u && reboot

完全看不到卡(lspci 也没有)

那就是硬件层面的问题了:卡没插好、供电没接、卡坏了、或者 PCIe 插槽有问题。 这个只能去机房看。

2. 驱动版本不对 / CUDA 版本不匹配

# 驱动版本
nvidia-smi --query-gpu=driver_version --format=csv,noheader

# CUDA 运行时版本(注意:这是驱动最高支持的版本,不是装的 toolkit 版本)
nvidia-smi | head -3

# PyTorch 实际用的版本
python -c "import torch; print(torch.__version__, torch.version.cuda, torch.cuda.is_available())"

# 实际加载的驱动
cat /proc/driver/nvidia/version

对应关系:

CUDA 版本 最低驱动
12.4 550
12.1 530
11.8 520
11.4 470

注意几个容易混的点:

torch.version.cuda     → 编译 PyTorch 时用的 CUDA 版本
nvidia-smi 右上角的 CUDA → 驱动最高能支持的 CUDA 版本

nvidia-smi 显示的 CUDA 版本比 torch.version.cuda 高是正常的,不用管。 反过来说,如果 torch.cuda.is_available() 返回 False,PyTorch 自己带的 CUDA 库 和驱动不兼容才是问题。

常见报错:

"CUDA driver version is insufficient for CUDA runtime version"
  → 驱动太老,升级驱动

"CUDA error: no kernel image is available for execution on the device"
  → 驱动不支持这张卡的架构(Ampere / Hopper / Ada 的 sm_xx)
  → 升级驱动到支持该卡的版本,老驱动不认新卡

"undefined symbol: cudaGetDriverEntryPoint"
  → 装了多个 CUDA toolkit,环境变量指向了旧的

多 CUDA 版本共存时,看当前用的是哪个:

echo $CUDA_HOME
ls -la /usr/local/ | grep cuda
which nvcc && nvcc --version

3. 显存占用异常 / OOM

# 当前每张卡的占用
nvidia-smi --query-gpu=index,name,memory.total,memory.used,memory.free,utilization.gpu \
  --format=csv

# 找出占显存的进程
nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv

# 僵尸进程占用(进程已死但显存没释放)
fuser -v /dev/nvidia*

典型情况:

显存被僵尸进程占住

进程被 kill -9 了,但 CUDA context 没释放:

# 找到占用的进程
nvidia-smi --query-compute-apps=pid --format=csv,noheader

# 确认是僵尸(状态 Z)
ps -o pid,stat,cmd -p <PID>

# 清理
kill -9 <PID>

如果杀不掉,说明 GPU 卡住了,只能重启:

nvidia-smi --gpu-reset -i 0
# 报 "in use" 就只能重启机器

训练中途 OOM,但之前能跑

# 看是不是有其他进程抢了显存
nvidia-smi

# PyTorch 显存碎片
python -c "import torch; torch.cuda.empty_cache()"

# 混合精度没开
# 在代码里加:
# with torch.autocast('cuda', dtype=torch.float16):

设置显存占用比例(限制 PyTorch 抢多少):

export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
# 老版本用:
export PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128

4. 训练中断 / "GPU has fallen off the bus"

这个报错最吓人,但通常不是卡坏了。

# 内核日志里的 Xid
dmesg -T | grep -iE "xid|nvidia" | tail -50

# 常见 Xid 码
#   Xid 13/31   → 显存错误,可能卡坏了或过热
#   Xid 48      → 双位错误,通常要换卡
#   Xid 63/64   → 掉卡,PCIe 链路问题
#   Xid 79      → GPU 掉卡(硬件故障)

先看物理连接:

# PCIe 链路状态和速率
nvidia-smi -q | grep -A 10 "GPU Link Info"
# Link Gen 和 Link Width 都要和安装时一致

# 实际协商到的速率(应为 PCIe 4.0 x16 之类)
nvidia-smi --query-gpu=pci.bus_id,pcie.link.gen.current,pcie.link.width.current \
  --format=csv

如果协商速率降了(比如装在 x8 槽位上只能跑 x8,或者速率从 4 降到 1), 说明 PCIe 链路协商有问题,检查主板插槽和转接卡。

处理顺序:

# 1. 查温度(过热也会掉卡)
nvidia-smi --query-gpu=index,temperature.gpu,power.draw,power.limit,clocks_throttle_reasons.active \
  --format=csv

# 2. 查电源线(8 pin / 12VHPWR 没插紧很常见)
#    需要去机房看

# 3. 复位 GPU
nvidia-smi --gpu-reset -i 0

# 4. 换槽位测试(能换机器上的另一个槽位最好)

电源线没插紧是掉卡的头号原因。12VHPWR 接头插一半特别容易, 而且接口会发热变色,插的时候要完全到底。

5. 温度报警 / 降频

# 详细温度
nvidia-smi --query-gpu=index,name,temperature.gpu,temperature.memory,\
power.draw,power.limit,clocks.sm,clocks.max.sm --format=csv

# 降频原因(0=正常,非 0 就是被限制)
nvidia-smi --query-gpu=index,clocks_throttle_reasons.active,\
clocks_throttle_reasons.sw_thermal_slowdown,\
clocks_throttle_reasons.hw_thermal_slowdown,\
clocks_throttle_reasons.sw_power_cap,\
clocks_throttle_reasons.hw_power_brake_slowdown --format=csv

常见原因和对策:

现象 原因 处理
温度 85℃+ 一直降频 风道堵了 / 风扇转速不够 清灰,检查机箱进风口
温度正常但功耗被限 电源功率不够 核对电源瓦数,升级电源
瞬时温度冲到 95℃ 散热硅脂老化 换硅脂
温度传感器报错 卡或固件问题 查固件版本

数据中心卡(A100/H100)一般是被动散热,靠机箱风扇吹。这类卡 装在普通塔式机箱里温度一定高,装之前先确认机箱风道。

6. 推理结果不对 / 精度问题

# 看是不是掉到了低精度
nvidia-smi --query-gpu=index,name,clocks.sm,clocks.max.sm,power.draw --format=csv
# 检查浮点控制状态
import torch
print(torch.cuda.get_device_capability(0))
print(torch.backends.cuda.matmul.allow_tf32)
print(torch.backends.cudnn.allow_tf32)

几个常见的「结果不对」:

TF32 导致的精度差异

A100/H100 默认开 TF32,矩阵乘法精度从 FP32 降到 TF32。 和 CPU 对数或者做精度回归测试时会对不上:

torch.backends.cuda.matmul.allow_tf32 = False
torch.backends.cudnn.allow_tf32 = False

多卡结果不一致

# 检查各卡状态
nvidia-smi --query-gpu=index,name,persistence_mode,compute_mode --format=csv

compute_mode 应该是 Default。如果是 Exclusive Process,第二个进程就起不来。

DDP 通信卡住

# 看 NCCL 状态
echo $NCCL_DEBUG
export NCCL_DEBUG=INFO
export NCCL_P2P_DISABLE=1     # 排查 P2P 问题
export NCCL_IB_DISABLE=1     # 排查 InfiniBand 问题

NCCL 卡住通常是 PCIe 拓扑问题:

nvidia-smi topo -m

看同一组卡是不是挂在同一个 CPU socket 下(NVLink 有效), 跨 socket 的 P2P 通信会慢很多甚至卡死。

显存没对齐导致跑满

# 批大小不是所有卡的倍数
if batch_size % world_size != 0:
    batch_size = (batch_size // world_size) * world_size

7. 多卡训练 NCCL 挂掉

# 常见报错
# "NCCL error: unhandled system error"
# "NCCL WARN Call to ibv_open_device failed"
# "NCCL INFO Bootstrap : no socket interface found"

排查:

# 1. 网卡状态(多机训练必查)
ibstat
ip addr show | grep ib

# 2. 端口和防火墙
ss -lntp | grep -E "29[0-9]{3}"

# 3. 关掉一些高级特性先跑通
export NCCL_P2P_DISABLE=1
export NCCL_SHM_DISABLE=1
export NCCL_IB_DISABLE=1
export NCCL_SOCKET_IFNAME=eth0
export NCCL_DEBUG=INFO

NCCL_SOCKET_IFNAME 一定要显式指定,否则多网卡机器上它可能选到 IB 网卡或者管理网口,bootstrap 就连不上。

单机能跑、多机不行,基本都是 IB 网卡或防火墙的问题。

8. 容器里用不了 GPU

# 容器里跑
docker run --gpus all nvidia/cuda:12.1.0-base-ubuntu22.04 nvidia-smi

报错和对策:

"could not select device driver "" with capabilities: [[gpu]]"
  → 没装 nvidia-container-toolkit
  → 装完要 systemctl restart docker

"unknown option --gpus"(老版本 docker)
  → docker 19.03 以下不支持,装个新版本

容器里 nvidia-smi 报 "Failed to initialize NVML"
  → 通常是宿主机驱动版本和容器内 CUDA 版本不匹配
  → 容器 CUDA 版本别超过宿主机驱动支持的版本

装 toolkit:

# Ubuntu
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/stable/$distribution/nvidia-container-toolkit.list | \
  sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt update && sudo apt install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

9. 其他常见问题

MPS 用了之后卡住

# 启动 MPS
nvidia-cuda-mps-control -d

# 停掉
echo quit | nvidia-cuda-mps-control

# 残留的 MPS 进程要手动清理
ps aux | grep mps

nvidia-smi 显示 "GPU is lost"

# 软复位
nvidia-smi --gpu-reset -i 0
# 不行就硬复位
sudo nvidia-smi --gpu-reset -i 0

ECC 报错

nvidia-smi -q | grep -A 5 "ECC Errors"

H100/A100 的 ECC 计数涨了不一定坏,看 Volatile 和 Aggregate 两项, Aggregate 持续涨说明显存真有问题。

持久化模式没开

计算节点建议开着,容器和长时间任务启动更快:

nvidia-smi -pm 1
# 做成开机自启
cat > /etc/systemd/system/nvidia-persistenced.service <<'EOF'
[Unit]
Description=NVIDIA Persisted Daemon
After=network.target

[Service]
Type=forking
ExecStart=/usr/bin/nvidia-smi -pm 1
ExecStop=/usr/bin/nvidia-smi -pm 0

[Install]
WantedBy=multi-user.target
EOF
systemctl enable --now nvidia-persistenced

验证

# 基础检查
nvidia-smi                                   # 能看到所有卡,温度功耗正常
nvidia-smi --query-gpu=index,name,driver_version,memory.total,temperature.gpu \
  --format=csv

# 负载测试
python -c "
import torch
print('可用:', torch.cuda.is_available())
print('卡数:', torch.cuda.device_count())
for i in range(torch.cuda.device_count()):
    print(i, torch.cuda.get_device_name(i))
x = torch.randn(4096, 4096, device='cuda')
print('矩阵运算 OK:', (x @ x).sum().item())
"

# 显存能正常释放(跑完再查一次)
nvidia-smi --query-gpu=index,memory.used --format=csv

多卡环境还要确认:

nvidia-smi topo -m              # PCIe 拓扑正常
ibstat                          # IB 网卡正常(多机训练)
python -c "
import torch, torch.distributed as dist
dist.init_process_group('nccl')
print('NCCL 正常')
"

回滚

  • 换驱动前先备份旧版本:
# 记下当前版本
nvidia-smi --query-gpu=driver_version --format=csv,noheader
# 旧驱动安装包留着,别删
  • 驱动装坏了先别急着重装,先试软复位:
sudo nvidia-smi --gpu-reset -i 0
  • 内核升级前确认 DKMS 能编译:
# 确认 dkms 装好了
dkms status
# 内核头文件在不在
ls /usr/src/kernels/$(uname -r)

内核升级后驱动模块没了,就是 DKMS 没装或头文件不对。

  • 固件升级(VBIOS)不可逆,升级前用 nvidia-smi -q | grep -i vbios 记下版本, 确认有备份工具(NVFlash)再动手。
  • 温度问题降级处理:临时限制功耗上限,比换硬件快。
nvidia-smi -pl 200    # 限制到 200W

坑

  • nvidia-smi 显示的 CUDA 版本是驱动上限,不是实际用的版本,别照着它装 toolkit。
  • torch.version.cuda 和 nvidia-smi 的 CUDA 版本不一样是正常的。
  • 驱动升级完不重启内核不生效,模块还在内存里,得重启。
  • 12VHPWR 电源线没插紧是掉卡头号原因,而且接口会发热变色,要插到底。
  • 数据中心卡是被动散热,装塔式机箱里必热。
  • 装完卡要在 BIOS 里开 Above 4G Decoding 和 ReBAR,否则性能打折甚至不识别。
  • nvidia-smi --gpu-reset 在有进程占用时会失败,先清干净再复位。
  • 多机训练不指定 NCCL_SOCKET_IFNAME 很可能 bootstrap 失败。
  • TensorRT 缓存对不上驱动版本,换驱动后清缓存:rm -rf ~/.cache/torch。
  • 别在有 checkpoint 之前就反复重试训练,卡反复掉可能损坏 checkpoint 文件。
  • ECC 报错别忽略,Volatile 涨可以清,Aggregate 涨就得换卡了。
  • 国产卡(华为昇腾、寒武纪等)的 CUDA 生态不一样,这套命令大多不通用, 得看厂商自己的工具链。