引言
第二天回到工位,准备查看昨晚的运行结果。nvidia-smi 直接抛出版本不匹配:
Failed to initialize NVML: Driver/library version mismatch``NVML library version: 580.159

更诡异的是------btop 的 GPU 监控栏位全部消失,但 Triton 推理服务的日志还在滚动,吞吐数字看起来一切正常。


直到跑了一个新测试,发现 YOLO 推理耗时从 3ms 变成了 20ms,输出还是空的。问题才浮出水面。

/.venv/lib/python3.12/site-packages/torch/cuda/__init__.py:182: UserWarning: CUDA initialization: Unexpected error from cudaGetDeviceCount(). Did you run some cuda functions before calling NumCudaDevices() that might have already set an error? Error 804: forward compatibility was attempted on non supported HW (Triggered internally at /pytorch/c10/cuda/CUDAFunctions.cpp:119.)
return torch._C._cuda_getDeviceCount() > 0
案发现场:四层症状矩阵
| 现象 | 接口层 | 状态 | 根因 |
|---|---|---|---|
nvidia-smi 拒绝启动 |
NVML | ❌ 完全失效 | 用户态库 580.159 vs 内核模块旧版本,严格版本锁拒绝握手 |
btop GPU 监控消失 |
NVML | ❌ 完全失效 | 同走 libnvidia-ml.so,初始化即返回错误 |
| YOLO 测试报 Error 804 后 fallback CPU | CUDA Runtime | ⚠️ 静默降级 | 新 CUDA 进程初始化时 forward compatibility 失败,PyTorch 静默回退 CPU |
| Triton 服务仍在推理 | CUDA Driver / Context | ✅ 残留运行 | 驱动更新前已建立 CUDA Context,旧内核模块仍为其服务 |
这不是驱动"损坏"。这是驱动更新只完成了一半。
真凶:unattended-upgrades 的半成品更新
Ubuntu 默认启用的 unattended-upgrades 服务会在后台自动安装安全更新。NVIDIA 驱动包(nvidia-driver-580、libnvidia-* 等)被它悄无声息地刷到了 580.159,写入磁盘。
但 Linux 内核模块(nvidia.ko)一旦加载就驻留内存。APT 不会、也不能替换运行中的内核模块。于是出现版本撕裂:

bash
# 用户态库版本
dpkg -l | grep nvidia-utils-580
# ii nvidia-utils-580 580.159-0ubuntu1 amd64
# 内核模块版本(仍在内存中)
cat /proc/driver/nvidia/version
# NVRM version: NVIDIA UNIX x86_64 Kernel Module 580.126.09
dkms 本应在新内核或新驱动安装后自动重建模块,但这里有两个陷阱:
-
DKMS 构建需要匹配的内核头文件 ,如果
linux-headers-$(uname -r)缺失或版本不对,构建会静默失败; -
即使 DKMS 构建成功,新模块也需要重启才能加载 (除非你手动
rmmod+modprobe)。
所以 unattended-upgrades 留下了一个"更新了一半"的状态:磁盘上全是新代码,内存里跑着旧内核。
第一层:NVML 为什么必须拒绝服务
NVML(NVIDIA Management Library)是运维监控的唯一入口。它的设计是零容错版本锁:
bash
// libnvidia-ml.so 内部逻辑(示意)
if
(
user_space_version != kernel_module_version
)
{
return NVML_ERROR_DRIVER_NOT_LOADED; // 直接拒绝
}
nvidia-smi、btop、nvitop、gpustat 全部依赖 NVML。当 580.159 的 libnvidia-ml.so 尝试通过 /dev/nvidiactl 与内核对话时,内核驱动发现版本号不匹配,直接返回 NVRM: API mismatch。
结果不是"部分监控不可用",而是整个管理平面被连根拔起。 你看不到显存、看不到温度、看不到功耗,甚至无法判断 GPU 是否还在物理层面正常运行。
第二层:CUDA Error 804 的精确含义
YOLO 测试报错:
Error 804: forward compatibility was attempted on non supported HW
这是 CUDA Runtime 的**向前兼容(Forward Compatibility)**机制失败。流程如下:
-
PyTorch 内置的 CUDA Runtime(CUDA 12.x)启动时调用
cudaGetDeviceCount(); -
Runtime 检测到内核驱动版本(580.126.09)低于自身要求的最低版本;
-
Runtime 尝试进入 Forward Compatibility 模式 ------这是 NVIDIA 允许新 CUDA Toolkit 在旧驱动上运行的一种降级机制,但仅在数据中心驱动(Data Center/Tesla)且安装了
cuda-compat-*包时可用; -
你的环境不满足 forward compatibility 条件(驱动类型不对、或缺少 compat 包、或版本差距过大),Runtime 抛出
cudaErrorCompatNotSupportedOnDevice(Error 804); -
PyTorch 捕获错误后,
torch.cuda.is_available()返回False,整个推理链 silently fallback 到 CPU。
所以那 20.1ms 的 YOLO 推理不是在 GPU 上完成的。 是 CPU 在跑。(no detections) 和空 tensor 只是模型在 CPU 上的正常输出------你以为是 GPU 推理出错了,实际上它根本没拿到 GPU 门票。
第三层:Triton 为什么还在跑?
这是最反直觉的部分。如果驱动都"裂了",Triton 怎么还能推理?
答案是:CUDA Driver API 和 NVML 是两个完全独立的信任域。
bash
┌─────────────────────────────────────────┐
│ 用户空间工具层 (nvidia-smi, btop) │ ← NVML 接口,版本敏感
│ libnvidia-ml.so (580.159) │
├─────────────────────────────────────────┤
│ CUDA Runtime / cuDNN / PyTorch │ ← CUDA 驱动 API,向后兼容
│ libcuda.so (580.159) │
├─────────────────────────────────────────┤
│ NVIDIA 内核驱动 (nvidia.ko) │ ← 仍在内存中 (580.126.09)
│ ├─ GPU 计算调度 (正常) │
│ ├─ 显存管理 (正常) │
│ └─ NVML 管理接口 (版本校验失败) │ ← 只有这里拒绝新库
└─────────────────────────────────────────┘
Triton 在驱动更新前已经完成了 cuInit() → cuCtxCreate() → cuMemAlloc() 的全套初始化。它的 CUDA Context 像一根钉子一样钉在旧内核模块里。只要进程不退出、不调用 NVML、不重新初始化 CUDA,它就能继续用旧驱动的计算能力。
但这是一种残留运行状态:
-
旧 Triton 进程:✅ 正常推理
-
新启动的任何 CUDA 进程:❌ Error 804,直接 fallback CPU 或报错
-
如果此时
systemctl restart triton-server,它会立刻在你面前失效
诊断:确认每一层的状态
bash
# 1. 确认内核模块版本(预期比 580.159 旧)
cat /proc/driver/nvidia/version
# 2. 确认用户态库版本(预期 580.159)
dpkg -l | grep -E "nvidia|cuda"
# 3. 确认 DKMS 构建状态(看有没有 build error)
sudo dkms status | grep nvidia
# 4. 确认系统日志中的 API mismatch
sudo dmesg | grep -i "NVRM.*mismatch"
sudo grep NVRM /var/log/syslog
# 5. 确认 PyTorch 实际使用的设备
python -c "
import torch
print(f'CUDA available: {torch.cuda.is_available()}')
print(f'Device count: {torch.cuda.device_count()}')
"
# 6. 确认当前占用 GPU 的进程(旧 Triton 应该还在)
sudo fuser -v /dev/nvidia*
解决:重启,以及永远不要让这事再发生
在确保不严重影响业务的时间下重启
sudoreboot
重启后内核从磁盘加载 nvidia.ko 580.159,用户态和内核态对齐,NVML 和 CUDA Runtime 双双恢复。
检查 DKMS 是否真的工作了
如果重启后问题依旧,说明 DKMS 没把模块编出来:
bash
sudo dkms status
# 如果看到 nvidia/580.159 状态不是 installed,或内核版本不匹配:
sudo apt install linux-headers-$(uname -r)
sudo dkms autoinstall
sudo update-initramfs -u
sudo reboot
永久防御:把 NVIDIA 加入 unattended-upgrades 黑名单
bash
sudo vim /etc/apt/apt.conf.d/50unattended-upgrades
# 在 Unattended-Upgrade::Package-Blacklist 中加入:
"nvidia-";
"libnvidia-";
"linux-modules-nvidia-";
"linux-headers-nvidia-";
"linux-image-nvidia-";
"linux-objects-nvidia-";
然后重启服务:
sudo systemctl restart unattended-upgrades
如果你不想彻底关闭自动更新,也可以设置更新后在不影响业务的时间段自动重启(确保新模块生效):
bash
# 同样在 50unattended-upgrades 中
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:00";
结论:一次典型的静默故障
这次事件出现分层失效,危险性不在于破坏力,而在于隐蔽性****:
-
监控层(NVML)先失效,失去观测能力;
-
新计算层(CUDA Runtime)半失效,让人以为模型还在 GPU 跑,实际上在 CPU 上磨洋工;
-
旧计算层(已有 CUDA Context)残留运行,给"系统正常"的假象。
| 故障类型 | 业务影响 | 发现难度 |
|---|---|---|
| 服务直接宕机 | 完全中断,用户立刻投诉 | ⭐ 极易发现 |
| GPU 静默 fallback CPU | 推理延迟 ×6~10,吞吐量暴跌,但 API 仍返回 200 | ⭐⭐⭐ 极难发现 |
Triton 服务没有崩溃,HTTP 端口还在监听,请求还在返回 200。只是每张图片的推理从 3ms 变成了 20ms,吞吐量从 1000 QPS 掉到了 100 QPS。如果你的负载均衡器没有设置 latency 告警,你可能要过几天才从用户投诉里发现异常。
真正的损失不是电费,而是:
-
服务质量降级:用户等待时间增加
-
资源挤占:CPU 被推理任务占满,影响其他服务
-
排查成本 :你花了半天时间才发现
nvidia-smi失效和 Error 804 的关联
把 NVIDIA 包加入 unattended-upgrades 黑名单,更新驱动后手动重启验证,是运维的必修课。
创作不易,禁止抄袭,转载请附上原文链接及标题