Ubuntu虚拟机NVIDIA L20显卡驱动异常排查处置过程
记录时间:2026-08-07
环境:KVM/QEMU 虚拟机(i440FX-PIIX-1996 主板,Ubuntu 24.04,GPU 透传 NVIDIA L20 49G)
一、问题现象
这台虚拟机显卡是 NVIDIA L20 49G,做了 KVM GPU 透传,lspci 能正常识别显卡,但 nvidia-smi 一执行就报错,驱动始终加载不上:
nvidia-smi
# 报错信息
NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver. Make sure that the latest NVIDIA driver is installed and running.
同样的现象出现了两次:第一次是刚装好驱动时,第二次是 111.10 这台机器上又复发了一次。第二次排查时,我注意到终端里残留着 docker run --help 的提示,说明之前跑过 docker run 命令,当时第一反应是”该不会又是没把显卡挂载进容器吧”。
二、排查过程
2.1 先怀疑容器没挂载显卡(这个方向走偏了)
第二次复现时,因为平时 GPU 都是给容器里跑 AI 任务用的,所以最先怀疑的是容器侧:docker run 时没加 GPU 挂载参数,导致容器里用不了显卡。终端里残留的 docker run --help 提示也让我更加确信是这个方向。
但仔细一想不对:容器里用不了显卡,宿主机直接执行 nvidia-smi 应该是正常的才对。于是我在宿主机上验证了一下,结果发现宿主机本机执行 nvidia-smi 同样报错。既然宿主机驱动层都有问题,容器那条线就先放一边,回头查宿主机。
2.2 回到宿主机排查驱动与内核状态
确认是宿主机驱动问题后,按老套路逐项确认:
# 确认 GPU 透传正常
lspci | grep -i nvidia
# 当前运行的内核
uname -r
# 输出:7.0.0-28-generic(问题就在这)
# DKMS 编译状态
dkms status
# nvidia/590.48.01 已装到 6.17.0-14-generic,但没有 7.0 的模块
# 内核模块加载情况
lsmod | grep nvidia
# 无任何输出
结果和第一次一模一样:当前跑在 7.0.0-28-generic 内核上,而这个内核根本没有可用的 NVIDIA 模块。modprobe nvidia 也直接报模块不存在。
2.3 定位根因
到这里已经清楚了:NVIDIA 590.48.01 驱动不支持 Linux 7.0 系列内核,驱动源码没有适配 7.0 的补丁,DKMS 必然编译失败、生成不了对应模块。
三、根因分析
- 核心故障:NVIDIA 590.48.01 驱动与 Linux 7.0 新内核不兼容,DKMS 无法为 7.0 编译驱动模块;
- 环境矛盾:DKMS 只给 6.17.0-14-generic 编译好了 590 模块,但系统默认启动 7.0.0-28-generic 内核,该内核没有可用模块;
- 为什么会复发:QEMU-UEFI 虚拟机的 GRUB 字符串配置解析异常、启动菜单默认隐藏,升级拉取到 7.0 HWE 内核后,重启就自动进了不兼容内核;
- 排除项:GPU 透传正常、无 IOMMU 配置问题,纯软件层面的内核与驱动版本不匹配。
四、解决方案
4.1 修复 GRUB,固定启动 6.17 可用内核
QEMU-UEFI 环境里 GRUB 的字符串配置(如 GRUB_DEFAULT="Advanced options for Ubuntu>Ubuntu, with Linux 6.17.0-14-generic")会解析异常,所以改用数字索引 1>2 强制指定启动项,并把启动菜单改成强制显示,方便开机时确认进入的是哪个内核。
编辑 /etc/default/grub,关键字段如下:
# /etc/default/grub 关键配置(其余 distributor/cmdline 保持系统默认)
GRUB_DEFAULT="1>2" # 指向「Advanced options」子菜单第 2 项,即 6.17 内核
GRUB_TIMEOUT_STYLE=menu # 强制显示启动菜单,不倒计时隐藏
GRUB_TIMEOUT=10 # 菜单显示 10 秒
| 参数 | 值 | 说明 |
|---|---|---|
GRUB_DEFAULT |
"1>2" |
数字索引定位内核,规避 QEMU-UEFI 下字符串配置解析异常;1 为主菜单「Advanced options for Ubuntu」,2 为该子菜单下索引为 2 的条目 |
GRUB_TIMEOUT_STYLE |
menu |
强制显示 GRUB 菜单,避免默认隐藏导致看不到内核选择项 |
GRUB_TIMEOUT |
10 |
菜单停留 10 秒,留出人工确认内核的时间 |
1>2怎么对应到内核:GRUB 菜单从 0 开始编号,1是主菜单第二项「Advanced options for Ubuntu」,进入它的子菜单后再取第2项。结合本机update-grub输出,子菜单顺序是7.0.0-28-generic(索引 0、1,正常+恢复模式)、6.17.0-14-generic(索引 2、3,正常+恢复模式),所以1>2正好命中 6.17 正常启动项。这个编号依赖本机内核条目顺序,换机器不能照搬,必须对照该机update-grub输出重新数。
改完执行:
update-grub
这步执行时 update-grub 输出里 7.0 和 6.17 两个内核都还在——清理动作放在第 4.3 步,要等切到 6.17 重启后再做,避免在 7.0 下误删运行中的内核镜像:
Found linux image: /boot/vmlinuz-7.0.0-28-generic
Found initrd image: /boot/initrd.img-7.0.0-28-generic
Found linux image: /boot/vmlinuz-6.17.0-14-generic
Found initrd image: /boot/initrd.img-6.17.0-14-generic
4.2 重启切换内核
reboot
重启后留意虚拟机控制台,确认 GRUB 自动选中并启动了 6.17.0-14-generic。
4.3 清理无效 7.0 内核
内核切回可用版本后,把不兼容的 7.0 内核及依赖一起卸掉,防止下次再自动启动进去:
# 卸载无效内核镜像
apt remove -y linux-image-7.0.0-28-generic linux-headers-7.0.0-28-generic
# 清理冗余依赖包
apt autoremove
# 重新生成 GRUB 配置
update-grub
4.4 容器侧挂载显卡验证
宿主机驱动恢复后,再回到容器这条线:docker run 时用 --gpus all 把显卡挂载进容器,确认容器内也能正常用 GPU:
# 宿主机 nvidia-smi 正常后,带 GPU 挂载起容器
docker run --gpus all --rm -it <镜像> bash
# 容器内验证
nvidia-smi
# 正常识别 L20,驱动版本 590.48.01
五、验证
# 宿主机验证
uname -r
# 输出:6.17.0-14-generic
nvidia-smi
# 正常识别 NVIDIA L20 49G,驱动 590.48.01 正常
# 容器内验证
docker run --gpus all --rm -it <镜像> nvidia-smi
# 同样正常,CUDA 支持 13.1
六、注意事项 / 长期避坑规范
- 版本适配原则:NVIDIA 590 系列驱动不兼容 Linux 7.0 及以上内核,严禁在该驱动环境下升级 7.x 内核;
- 系统升级限制:禁止执行
apt full-upgrade,会拉取高版本 HWE 内核并设为默认启动项,这是故障复发的主要原因; - 故障判断顺序:遇到 GPU 用不了,先在宿主机直接执行
nvidia-smi验证——宿主机正常才去查容器挂载;宿主机自己都报错,别先怀疑容器; - QEMU-UEFI 的 GRUB 适配:虚拟机优先用数字索引配置默认内核,规避字符串解析失效;
- 容器挂载:容器里用 GPU 必须加
--gpus all(或--device挂载/dev/nvidia*),依赖nvidia-container-toolkit支撑。