嵌套虚拟化的 vGPU

没有人知道我为什么想在物理机上拖一个虚拟机,再把显卡直通到虚拟机里面去开 vGPU。

先介绍一下我手头的硬件。今天的主角是一块服务器造型的计算卡 P100 12G。这卡没有风扇,也就只适合装到服务器上吹着用。然后用的服务器是前段时间从咸鱼买来的 H3C 的 2U 机器(此处应有图,但是懒得去机房拆机拍照了,凑合脑补吧)。

我的物理机上安装的是 Proxmox VE 8,内核是 6.2.16-3-pve。由于内核比较新,现在的 NVIDIA Grid 驱动安装上去会编译不通过,看了看报错,大概是 API 改了之类的。我不太懂 Linux 内核驱动的开发,也没有兴趣帮 NVIDIA 打补丁,更不希望降低我的系统内核。那要怎么做呢?一个比较容易想到的办法就是——在物理机(L0)里开一台内核版本比较低的虚拟机(L1),然后再把显卡通过 PCIe 直通挂进去;之后再在 L1 虚拟机里去做 vGPU 切分,由于内核使用的是 L1 虚拟机里的内核,这样 NVIDIA 的驱动就能够顺利地跑起来。在跑起来之后,我们再在 L1 虚拟机里用 KVM 启动 L2 的虚拟机,并设置其使用 mdev 设备,就可以成功用上了!

此处的梗图来源已不可考,但基本上就是这样

在嵌套虚拟化的环境中,我们通常使用 Lx 来标识不同的虚拟化级别。这里 L0 代表物理机 —— 安装了 Proxmox VE 8 的那台;之后的 L1 则代表在此上运行的虚拟机;而 L2 则代表在 L1 上运行的虚拟机。

配置物理机 (L0)

物理机配置其实没啥可讲的,嵌套虚拟化和 IOMMU 打开一下就行了。具体的设置过程可以参考 PVE Wiki 的 Nested Virtualization 页面,我这里默认都是打开的,直接就能用了。

验证环境设置:

cat /sys/module/kvm*/parameters/nested # 看到 1 或者 Y 就行;0 或者 N 代表不支持嵌套虚拟化
ls /sys/kernel/iommu_groups/ # 看到一堆数字就行;没有输出代表 IOMMU 没开启

启动 L1 虚拟机

在 PVE 里启动一个虚拟机本身没啥可讲的,我这边在 L1 里启动的是另一个 PVE – PVE 7.4-15,内核版本是 5.15.108-1-pve 。这个版本的内核就可以安装最新的 vGPU 16 的 GRID 驱动了。

启动虚拟机需要注意几点:

  • CPU 型号选择 host
  • 需要用 Q35 的机器类型
  • 需要用 OVMF UEFI 启动
  • 避免使用 virtio 系列存储、网卡 —— 根据 QEMU Wiki VT-d 页面的说明,Virtio 半虚拟化需要 IOTLB 的支持,我搞不懂那是啥,所以干脆避免使用;我用的是 SATA 虚拟盘和 e1000e 虚拟网卡

在创建完成虚拟机之后,需要给虚拟机增加虚拟 IOMMU 设备。还是参考前文的页面,执行:

qm set <VMID> --args '-device intel-iommu,intremap=on,caching-mode=on -machine accel=kvm,kernel-irqchip=split'

这样就可以添加上对应的设备。在 GitHub 上我也发现了一些指导,例如 bashtheshell/IOMMU-nested-pve 会说这个地方需要整个修改参数把 intel-iommu 设备加到最前面,我这边倒是没遇到这个问题,直接用 qm 把额外参数补上就行了。

然后就像普通的显卡直通一样,在硬件那边添加一个新的 PCIe 设备,选择你的显卡就行。全部配完大概是这样:

L1 虚拟机设置完的样子

设置完就可以开机装系统了。

配置 L1 虚拟机

装完 L1 之后,因为还需要在 L1 里开虚拟化和 vGPU 嘛,所以这个机器也要做对应的设置,启用 KVM, IOMMU 才行。执行以下命令可以检查:

ls /dev/kvm # 有 KVM 设备说明嵌套虚拟化启动成功了
ls /sys/kernel/iommu_groups/ # 看到一堆数字代表 IOMMU 启动成功了

不知道是因为我用 AMD CPU 还是因为 PVE 7 比较蠢,虽然 KVM 是有的,但是 IOMMU 没有自动开启。不过没关系,编辑 /etc/default/grub 文件,在 GRUB_CMDLINE_LINUX_DEFAULT 变量里加入 intel_iommu=on iommu=pt ,然后执行 update-grub 重新生成 grub,再重启就行了。注意因为 vIOMMU 设备就叫 intel-iommu,所以在 L1 虚拟机里,即使你使用的是 AMD CPU,也没有 amd_iommu,只有 intel_iommu。

设置完 IOMMU 就可以正常安装 vGPU 驱动啦,网上有很多教程我就不赘述了,装好依赖,直接运行对应的 .run 文件就行:

apt install pve-headers-$(uname -r) build-essential
./NVIDIA-Linux-x86_64-535.54.06-vgpu-kvm.run

装好之后输入 nvidia-smi 就能看到我们的显卡信息了。

启动 L2 虚拟机

因为我的 L2 也是 PVE,所以这里也很简单,在 PVE 界面里,新建一个 PCIe 设备,右边选择对应的 mdev 型号 —— 一般选 Q 系列的比较好用 —— 就行了。

在 PVE 中创建 mdev 设备

另外,众所周知,NVIDIA 的 vGPU 显卡需要 License 授权才可以不锁帧使用。我这边使用的是 P100 的 GPU,有一款叫 Quadro GP100 的 GPU 和我是同一个芯片。于是到网上查询,得知 Quadro GP100 的 PCIe ID 是 15F0,Subsystem ID 是 11C3,所以在做 PCIe 直通的时候把 ID 设置上去(NVIDIA 的 Vendor 都是 10DE):

在创建虚拟设备时指定设备 ID

这样 L2 虚拟机就会以为自己用的是 Quadro GP100 的 GPU 了,就可以装普通的 Studio 驱动了。

安装了 GP100 驱动的 vGPU

比较神奇的是,就算设备管理器和 GPU-Z 都显示为 GP100,NVIDIA 自己的驱动却能正常认出 P100-4Q 的真实身份。目前版本(536)的驱动下,运行了12个小时也没发现锁帧问题,应该是搞定了。

嵌套虚拟化的性能

我没有具体评估嵌套虚拟化的性能,但用起来基本能用,感觉性能损失没有特别明显。对于现代的 CPU,虚拟化小于等于两层的情况下,应该还是挺好用的。这里贴一篇红帽博客 2017 年的文章:Inception: How usable are nested KVM guests?,这个文章的作者结论似乎和我的差不多。

总而言之,这么一个 setup 我倒是挺满意的,性能过得去,也有显卡能做基本的图形加速,用来挂 QQ 是再好不过了……

评论

还没有评论。

发表评论

发表评论代表你授权本网站存储并在必要情况下使用你输入的邮箱地址、连接本站服务器使用的 IP 地址和用户代理字符串 (User Agent) 用于发送评论回复邮件,以及将上述信息分享给 Libravatar Akismet,用于显示头像和反垃圾。