上一篇 下一篇 分享链接 返回 返回顶部

如何在香港服务器的Windows Server 2022 Hyper-V 中配置vGPU直通支持AI训练

发布人:Minchunlin 发布时间:2025-09-04 09:37 阅读量:940


凌晨 2:40,我站在香港荃湾机房 7 楼的过道,客户在电话那头催:“早上 8 点要把第一版模型训起来,数据已经就位了。”我对着一台刚上架的 2U 机器,拎着一个 A10 和一把十字螺丝刀,心里盘着两件事:用 Hyper-V 的 DDA(离散设备直通)把整卡给到 Windows 训练虚机,同时留一条“Plan B”:如果要多人共享,就走 NVIDIA vGPU(SR-IOV/授权)。

这篇文章就是那一晚我从 BIOS 到驱动、从直通到训练、一路把坑踩完的完整记录。我会用第一人称把每一步的命令、参数、表格、报错与解决办法交代清楚,希望你照着做,能在自己的香港服务器上顺利把卡“喂”给训练环境。

场景与目标

场景:香港机房、单/多张 NVIDIA 数据中心 GPU(A10/T4/L40S/RTX 6000 Ada 等),宿主系统 Windows Server 2022 Datacenter,虚拟化平台 Hyper-V。

目标:

  • 以 DDA(Discreet Device Assignment)直通整卡 到 Windows 客户机,跑 CUDA 训练;
  • 可选:在许可允许的前提下,部署 NVIDIA vGPU 以在多虚机之间共享一张卡;
  • 过程里记录我真实遇到的坑和可复现的修复步骤。

1. 我用到的硬件与拓扑(可对照替换)

型号/参数 备注
机型 2U 双路(如 Supermicro/戴尔 R 系列) 前 8 盘位 NVMe,后置风扇
CPU Xeon Silver 4310 ×2(或 Gold/Platinum) 需要 VT-d/IOMMU
主板 支持 Above 4G Decoding / SR-IOV 建议更新到最新 BIOS
内存 256–512 GB DDR4/DDR5 ECC 训练任务常驻大内存
系统盘 2 × NVMe (RAID1) 宿主机 OS
数据盘 4–8 × NVMe (RAID10/单盘直通) 训练数据/缓存
GPU NVIDIA A10(或 T4/L40S/RTX6000 Ada 等) 建议数据中心驱动线
网卡 2 × 25/10 GbE(SR-IOV 能开) 训练/数据同步
电源与散热 1+1 冗余 / 风墙 香港机房温度偏高,风道要正

注意:消费级卡可做 DDA,但更容易踩驱动/Code 43 的坑;如果要稳定生产,优先 A10/T4/A30/A100 这类 DC SKU。

2. 方案选择:DDA vs vGPU vs GPU-P(我当晚的选择与权衡)

方案 能力 适用 OS 许可 适配难度 适配 AI 训练 我的结论
DDA 直通整卡 把整张 PCIe 设备直通给单个 VM Win/Linux Guest 无额外授权 最佳(原生 CUDA) 当晚采用
NVIDIA vGPU(SR-IOV) 一卡多 VM,profile 切分 Win/Linux Guest 需 vGPU 授权 + 许可服务器 中-高 佳(需按 profile 选版驱动) 适合共享/多用户
GPU-P 分区 Windows 的 GPU 分区 Windows Guest 中(文档少) 不稳定/受限 实验/桌面渲染为主

想要今天就训起来且最稳:DDA。需要多人共享才考虑 vGPU。GPU-P 我只在容器/渲染场景做过试验,这里不展开。

3. 前置准备(BIOS/固件/机房侧)

升级 BIOS / BMC / NVMe / GPU 固件 到厂商推荐版本。

BIOS 打开:

  • Intel VT-d / IOMMU:Enabled
  • Above 4G Decoding:Enabled(大显存/多设备必开)
  • Resizable BAR:Auto/Disabled(遇到 MMIO 紧张时我会关)
  • CSM:Disabled(UEFI 更稳)

电源策略:OS 与 BIOS 都设 Performance;风扇曲线拉高一点。

机房网络:准备一张 out-of-band 管理口和一张数据口,SR-IOV 能开的网卡先开着(供 vNIC 提升)。

4. 宿主:Windows Server 2022 基线

4.1 装 Hyper-V 与基本角色

# 以管理员进入 PowerShell
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All
Restart-Computer

4.2 安装 NVIDIA 数据中心驱动(宿主)

我倾向在 宿主也装上对应线的驱动,便于后续 vGPU/监控。但 DDA 直通前会“卸载/脱挂(dismount)”,宿主不会再占用。

安装后执行 nvidia-smi 确认能看到卡(仅用于校验,直通前会移除宿主占用)。

4.3 电源/更新策略

# 电源:高性能
powercfg /S SCHEME_MIN
# 关闭“自动更新立即重启”
sconfig  # -> 5: Windows Update 设置为手动/维护窗口

5. 用 DDA 给 VM 直通整卡(完整、可复现脚本)

我的实践顺序:定位 GPU → 让宿主“放手”(dismount)→ 准备 VM(Gen 2、静态内存、MMIO)→ 把设备绑给 VM → 在客人机里装驱动 & CUDA → 验证。

5.1 创建/准备虚机(Generation 2 + 静态内存)

# 参数自定
$VmName = "win-train-01"
$VmPath = "D:\HyperV\$VmName"
$Vhdx = "D:\HyperV\$VmName\$VmName.vhdx"

New-VM -Name $VmName -Generation 2 -MemoryStartupBytes 128GB -NewVHDPath $Vhdx -NewVHDSizeBytes 1024GB -Path $VmPath -SwitchName "vSwitch-Data"

# 直通 GPU 需要“静态内存”,关掉动态内存
Set-VM -Name $VmName -DynamicMemoryEnabled $false

# CPU 给足一些,训练更稳
Set-VMProcessor -VMName $VmName -Count 24 -Reserve 80 -Maximum 100

# 开启 Guest 控制缓存,设置 MMIO 空间(依 GPU 调整)
# A10/RTX6000 Ada 建议:Low = 512MB,High = 32GB(显存越大 High 越大)
Set-VM -Name $VmName -GuestControlledCacheTypes $true -LowMemoryMappedIoSpace 512MB -HighMemoryMappedIoSpace 32GB

# 如有需要关闭安全启动(部分旧驱动/测试场景)
Set-VMFirmware -VMName $VmName -EnableSecureBoot On  # 出问题时可改 Off

MMIO 经验值(我踩出来的表):

GPU 显存 Low MMIO High MMIO
T4 16 GB 256 MB 16 GB
A10 24 GB 512 MB 32 GB
RTX 6000 Ada 48 GB 512 MB 64 GB
L40S 48 GB 512 MB 64 GB

如果启动报 MMIO/资源不足,先把 Resizable BAR 关掉,再把 High MMIO 提高一档。

5.2 找到 GPU 的 LocationPath,并让宿主“放手”

# 找到 NVIDIA 设备(含显示和音频功能)
$gpu = Get-PnpDevice -PresentOnly | Where-Object { $_.FriendlyName -like "*NVIDIA*" -and $_.Class -eq "Display" }
$gpuAudio = Get-PnpDevice -PresentOnly | Where-Object { $_.FriendlyName -like "*NVIDIA*" -and $_.Class -eq "Media" }

# 拿到 LocationPath(可能多条,尤其是带 Audio Function)
$locGpu = (Get-PnpDeviceProperty -InstanceId $gpu.InstanceId -KeyName DEVPKEY_Device_LocationPaths).Data
$locAud = (Get-PnpDeviceProperty -InstanceId $gpuAudio.InstanceId -KeyName DEVPKEY_Device_LocationPaths).Data

# 温柔点,先禁用宿主对设备的占用
Disable-PnpDevice -InstanceId $gpu.InstanceId -Confirm:$false
Disable-PnpDevice -InstanceId $gpuAudio.InstanceId -Confirm:$false

# 把设备 dismount,标记为可分配(Host Assignable)
$locGpu  | % { Dismount-VMHostAssignableDevice -LocationPath $_ -Force }
$locAud  | % { Dismount-VMHostAssignableDevice -LocationPath $_ -Force }

# 确认现在宿主能看到可分配设备
Get-VMHostAssignableDevice

这里最容易踩的坑是只 dismount 了 Display Function 没有 dismount Audio Function,Add-VMAssignableDevice 会失败。两条都要 dismount。

5.3 把 GPU 绑给 VM

# VM 必须关机状态
Stop-VM -Name $VmName

# 把两条 LocationPath 都加到 VM 上
$locGpu | % { Add-VMAssignableDevice -LocationPath $_ -VMName $VmName }
$locAud | % { Add-VMAssignableDevice -LocationPath $_ -VMName $VmName }

# 再确认一次
Get-VMAssignableDevice -VMName $VmName

# 启动 VM
Start-VM -Name $VmName

5.4 在客户机(Guest)里安装驱动 & CUDA

进到 VM(Windows 10/11/Server 2022 都可):

  • 装对应版本的 NVIDIA Data Center Driver(与计划用的 CUDA/框架匹配)。
  • nvidia-smi 能看到设备且 Compute Mode: Default。
  • 装 CUDA Toolkit/cuDNN 或者直接用 PyTorch/TensorFlow 的 CUDA 绑定版本。

我的“保守兼容”矩阵(训练第一晚不追新):

框架 版本 CUDA cuDNN 备注
PyTorch 2.2.x 12.1 内置 pip3 install torch torchvision --index-url https://download.pytorch.org/whl/cu121
TensorFlow 2.13/2.14 11.8 8.x Windows 上更稳的是 2.13 + CUDA 11.8

快速自测(Guest 里):

# PowerShell
python - << 'PY'
import torch, time
print("CUDA avail:", torch.cuda.is_available())
x = torch.randn(10240, 10240, device="cuda")
tic=time.time(); y = x @ x; torch.cuda.synchronize(); print("GEMM ms:", (time.time()-tic)*1000)
print("Device:", torch.cuda.get_device_name(0))
PY

6. 多卡/多 VM 调度(我在香港机房是这样分的)

多卡单 VM:把多张卡的 LocationPath 全绑到一个 VM,做数据并行。

一卡多 VM(不共享):每卡固定给一台 VM,DDA 不支持共享。

一卡多 VM(共享):必须走 NVIDIA vGPU(授权),按 profile 切分(比如 8G/12G/24G slice)。

7. 可选:NVIDIA vGPU(SR-IOV/授权)部署要点(我做共享时的做法)

只给关键要点,不同代卡/许可版本细节略有差异,但抓住这几条就不迷路:

确认 GPU 支持 vGPU(A10、A16、L40S、A100 等)并准备 vGPU 许可服务器(NLS)。

宿主装 vGPU Host Driver(不同于通用 DC Driver),重启后 nvidia-smi -q | findstr -i vgpu 可见相关字段。

在 Hyper-V 给 VM 选择/注入 vGPU Profile(显存/编码能力/数量),并确保 Guest Driver 版本与 Host 对齐。

Guest 内安装 vGPU 对应的 NVIDIA 驱动,激活 许可(指向 NLS)。

训练框架里确保每个进程只占自己的 slice(用 CUDA_VISIBLE_DEVICES 或框架内设备选择)。

vGPU 的收益:同一张卡给多组用户/多台 VM;代价是要管理许可/配置,并且某些 profile 对 FP16/FP8 特性有约束,选型前看清 Profile 说明。

8. 性能与稳定性调优(我在生产里默认开的)

电源:BIOS/OS 高性能;Windows 里关闭 Core Parking。

NUMA 亲和:多路 CPU 时,把 VM vCPU 绑在同一 NUMA Node(Hyper-V 自动,但大 VM 时我会控制 vCPU 数量)。

存储:NVMe 直通或将数据盘给 VM 使用 固定大小 VHDX(避免动态膨胀)。

网络:vSwitch 用 SR-IOV 的物理 NIC,VM 网卡开启 RSS/VMQ,大包支持。

监控:宿主和 Guest 里都部署 nvidia-smi dmon/Telegraf Exporter,盯显存/温度/功耗。

9. 故障与坑位手记(当晚我真遇到的)

现象/报错 场景 解决办法(我当时的操作)
Add-VMAssignableDevice 失败 只 dismount 了 Display,忘了 Audio Function Audio Function 的 LocationPath 一并 Dismount-VMHostAssignableDevice,再 Add-VMAssignableDevice
VM 启动报资源不足 / MMIO 错误 High/Low MMIO 不够 Resizable BAR;把 HighMMIO 提到 32–64GB;Low 256→512MB
Guest 内 Code 43 消费级卡/驱动不匹配 改用 Data Center Driver;优先 A10/T4 等 DC SKU;必要时更换驱动小版本
宿主更新后卡“回来”了 Windows 更新驱动 更新完重新执行 dismount;把自动驱动更新设为“仅通知”
nvidia-smi 看不到卡 VM 内驱动不对 Host/Guest 驱动线要一致(DDA 宿主可无驱动,但 Guest 必须对版)
训练速度异常慢 CPU 省电/内存频率 BIOS/OS 统一 Performance;确认内存四通道以上;关闭节能策略
vGPU 许可报错 NLS 未激活/网络 检查 NLS 状态与端口,Guest 指向正确的 License Server;Host/Guest 驱动匹配

10. 我常用的一键“拆卡回宿主”脚本(回滚/维护用)

$VmName = "win-train-01"
# 1) 从 VM 移除可分配设备
Get-VMAssignableDevice -VMName $VmName | Remove-VMAssignableDevice -VMName $VmName
Stop-VM -Name $VmName

# 2) 把设备挂回宿主
Get-VMHostAssignableDevice | % { Mount-VMHostAssignableDevice -LocationPath $_.LocationPath }

# 3) 重新启用宿主的 NVIDIA 设备
$gpu = Get-PnpDevice -PresentOnly | Where-Object { $_.FriendlyName -like "*NVIDIA*" }
$gpu | % { Enable-PnpDevice -InstanceId $_.InstanceId -Confirm:$false }

11. 交付清单与复盘(我给客户的标准落地)

  •  BIOS:VT-d、Above 4G、(必要时)关 ReBAR
  •  宿主:Hyper-V、(可选)DC 驱动安装校验
  •  VM(Gen2):静态内存、MMIO(Low/High)、CPU 资源
  •  DDA:Display + Audio Function 双通道 dismount → add 到 VM
  •  Guest:DC 驱动 + CUDA/框架;nvidia-smi 自检
  •  训练样例跑通(GEMM/Base 模型)
  •  监控与告警(显存/温度/功耗)
  •  回滚脚本(维护窗口)

12. 收尾:机房的黎明与第一条 Loss 曲线

天色发白的时候,我把最后一条 Add-VMAssignableDevice 回车,虚机起来了。nvidia-smi 打印出 A10 的 24GB 显存,PyTorch 的小脚本跑完,GEMM 延迟照着预期。客户把数据路径切到 NVMe,第一轮训练启了四个 worker。风墙的噪音突然没那么刺耳了,像是背景里的白色海浪。
我合上笔记本,把那张记满 LocationPath、MMIO 配额和报错代码的便签纸塞回工具包。vGPU 的活儿留到下一班——当他们要多人共享时我再回来。
你如果也在香港、也对着一台 2U 的机器急着把训练跑起来,就按上面的步骤做。能用 DDA 先稳住交付,再慢慢把 vGPU/多用户场景补齐。别忘了那两条 LocationPath:Display 和 Audio,一次 dismount 干净。
祝你凌晨的第一条 loss 曲线,也像那天一样,顺着预期滑下去。

附:一次成功的最小命令清单(可直接抄)

# Gen2 + 静态内存 + MMIO
New-VM -Name win-train-01 -Generation 2 -MemoryStartupBytes 128GB -NewVHDPath D:\HyperV\win-train-01\win-train-01.vhdx -NewVHDSizeBytes 1024GB -Path D:\HyperV\win-train-01 -SwitchName vSwitch-Data
Set-VM -Name win-train-01 -DynamicMemoryEnabled $false -GuestControlledCacheTypes $true -LowMemoryMappedIoSpace 512MB -HighMemoryMappedIoSpace 32GB
Set-VMProcessor -VMName win-train-01 -Count 24 -Reserve 80 -Maximum 100

# 定位并 dismount GPU(含音频功能)
$gpu = Get-PnpDevice -PresentOnly | ? { $_.FriendlyName -like "*NVIDIA*" -and $_.Class -eq "Display" }
$aud = Get-PnpDevice -PresentOnly | ? { $_.FriendlyName -like "*NVIDIA*" -and $_.Class -eq "Media" }
$lg = (Get-PnpDeviceProperty -InstanceId $gpu.InstanceId -KeyName DEVPKEY_Device_LocationPaths).Data
$la = (Get-PnpDeviceProperty -InstanceId $aud.InstanceId -KeyName DEVPKEY_Device_LocationPaths).Data
Disable-PnpDevice -InstanceId $gpu.InstanceId -Confirm:$false
Disable-PnpDevice -InstanceId $aud.InstanceId -Confirm:$false
$lg | % { Dismount-VMHostAssignableDevice -LocationPath $_ -Force }
$la | % { Dismount-VMHostAssignableDevice -LocationPath $_ -Force }

# 绑定到 VM 并启动
Stop-VM win-train-01
$lg | % { Add-VMAssignableDevice -LocationPath $_ -VMName win-train-01 }
$la | % { Add-VMAssignableDevice -LocationPath $_ -VMName win-train-01 }
Start-VM win-train-01

有了这些,你就具备了在 Windows Server 2022 + Hyper-V 上把 vGPU/整卡直通用于 AI 训练 的全部关键步骤与可复现脚本。

目录结构
全文