如何在 Windows Server 2019 的香港 GPU 服务器上做「可落地」的 GPU 虚拟化,让多租户训练更高效

那天香港刚过台风,香港葵涌机房外头的风还没散尽。我抱着一台刚下架的 2U GPU 服务器,和机房小哥一起抬进 18U 的机柜第三层。PDU 的氖灯一闪一闪,ToR 交换机的风扇声像在催我快点把这活干完。老板一句话:“把这台 Windows Server 2019 的 GPU 机器改成能多租户训练的环境,资源利用率至少提升一倍。”
我看了看排队等资源的几个团队,再看了看这台机器上那几块“吃灰”的 GPU,心里只有一个念头:要用得聪明,不能再傻冲。
1)现场环境与目标
目标:在 Windows Server 2019 体系下,支持多个团队/项目并发训练,提高 GPU 利用率与隔离性,同时要保证“遇事可回滚”,尽量不打断在跑的训练任务。
实机与网络(真实参数):
| 组件 | 型号/参数 |
|---|---|
| 机型 | Dell PowerEdge R7525(2U) |
| CPU | 2 × AMD EPYC 7543(32C/64T,2.8GHz) |
| 内存 | 512GB DDR4-3200 |
| GPU | 4 × NVIDIA A10(24GB GDDR6/卡,支持 vGPU) |
| 系统盘 | 2 × 960GB SATA SSD(RAID1) |
| 训练盘 | 4 × 3.84TB NVMe U.2(RAID10,约 7TB 可用) |
| 网卡 | 2 × 25GbE(Broadcom 57414)上联 ToR |
| OS(宿主层) | 见方案分叉(A/B) |
| 来访 OS(租户) | Windows Server 2019 Datacenter(多数团队要求) |
| 机柜/电源 | 双 PSU,A/B 路 PDU,冷通道前出风 |
现实约束:几个团队历史上都用 Windows Server 2019 的工具链(VS、.NET 推理服务、一些商业 DCC 插件),短期内不能全切 Linux。
2)路线选型:怎么“在 Windows 体系里”把一块卡切给多人用?
关键事实(踩过坑才会刻在心里的那种):
Hyper-V 在 Windows Server 2019 上,官方支持的是 DDA(Discrete Device Assignment) —— 把整块 GPU 直通给某个 VM。它不支持真正的共享 vGPU(RemoteFX vGPU 已下线多年)。
要实现同一块物理 GPU 多租户共享,业界稳妥路线是用 VMware vSphere/Citrix/Red Hat KVM + NVIDIA vGPU。Windows Server 2019 更适合当来访客系统(Guest),而不是做“支持 vGPU 的宿主”。
因此我落地了两个可切换方案:
方案 A(推荐):vSphere + NVIDIA vGPU。宿主换成 ESXi(Type-1),每个租户跑 Windows Server 2019 VM,给它分配 A10-8C/A10-12C 等 vGPU Profile。这是真正的 GPU 共享,资源可弹性。
方案 B(兼容保底):Windows Server 2019 + Hyper-V + DDA。每个 VM 独占一整块 GPU。不是共享,但可多卡分配,改动小、上线快,适合“先跑起来、逐步演进”。
我最后在周末夜里把 方案 A 上线,把 方案 B 留作回滚预案。下面我把两套都写清楚,遇到你的环境不允许换宿主时,直接走 B 也能把活成一半。
3)方案 A:vSphere + NVIDIA vGPU(Windows 2019 做 Guest)
3.1 BIOS 与硬件基础设置(任何虚拟化都建议做)
打开 SR-IOV / IOMMU(AMD-Vi)、Virtualization、Above 4G Decoding、MMIO 扩展。
若主板支持,打开 Resizable BAR(部分新卡/新驱动会更友好)。
固件升级到一致版本(BMC/BIOS/NIC/RAID)。这个能省下很多玄学 Bug。
3.2 宿主安装与网存布局
安装 VMware ESXi 8.x 到 RAID1 系统盘;NVMe 组 RAID10 做数据存储。
vSwitch 做两条:
vSwitch0(管理):上联 25GbE-A,打管理 VLAN。
vSwitch1(数据):上联 25GbE-B,Trunk 训练/数据/备份 VLAN。
NTP 指到同一源,关闭主机自带“节能降频”(保持训练稳定)。
3.3 安装 vGPU Host 驱动与 License
把 NVIDIA vGPU Manager(VIB 包) 上到 ESXi:
# 在 ESXi Shell
esxcli software vib install -v /vmfs/volumes/datastore1/NVD-VMW-<version>.vib
reboot
部署 NVIDIA License Server(我放在同机房的一个小 VM),分配 vComputeServer 许可证。
在 vCenter 中验证主机的 Graphics 页面能看到 A10 并显示 Shared 能力。
3.4 创建 Windows Server 2019 模板 VM(关键点)
VM Hardware v 尽量用新(例如 v20+),Firmware 选 UEFI,启用 TPM(不少客户基线需要)。
磁盘:OS 80–120GB、数据盘按团队常规数据集大小规划;内存保留(Reservation)防止气球。
添加 vGPU:在 Add New Device → Shared PCI Device → NVIDIA vGPU 里选择 Profile:
- A10-8C(8GB 计算型)适合中等批量训练
- A10-12C(12GB 计算型)适合稍重模型
- A10-24C(整卡直通等价)
CPU/NUMA:以 8 vCPU / 16 vCPU 起步;跨 NUMA 的 VM 记得做 vNUMA 拆分(自动即可),并确保 VM 主要线程落在接近 GPU 的 NUMA 节点(同主板通常做不到硬绑,但尽量保持对称)。
Profile 选择经验:同一块 A10(24GB)可切 3 个 A10-8C,或者 2 个 A10-12C。训练负载显存常常是瓶颈,宁可给足显存,别把团队卡在 Out-of-Memory 上。
3.5 来宾(Guest)安装 GPU/AI 组件(Windows Server 2019)
NVIDIA vGPU Guest Driver(版本要与宿主 vGPU Manager 匹配)
静默安装(适合自动化模板):
Start-Process -FilePath ".\setup.exe" -ArgumentList "-s -noreboot -clean -noeula -log C:\nvidia_install.log" -Wait
设置 License(vComputeServer)
常规在“NVIDIA 控制面板 → Licensing”里指向 License Server;也可用注册表批量推送:
reg add "HKLM\SOFTWARE\NVIDIA Corporation\Global\GridLicensing" /v ServerAddress /t REG_SZ /d 10.10.10.5 /f
reg add "HKLM\SOFTWARE\NVIDIA Corporation\Global\GridLicensing" /v FeatureType /t REG_DWORD /d 2 /f # 2=vComputeServer
Restart-Service NVDisplay.ContainerLocalSystem -Force
CUDA / cuDNN / PyTorch
用官方兼容矩阵对齐版本(经验:优先用和驱动同一代的 CUDA)。
验证:
& "C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi.exe"
应出现 vGPU 设备及 License: Licensed。
TCC 模式(A10 属于数据中心卡,默认 TCC,一般无需改)
& "C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi.exe" -dm 1
3.6 快速压力验证(PyTorch)
# test_train.py
import torch, time
assert torch.cuda.is_available()
x = torch.randn(8192, 8192, device="cuda")
t = time.time()
for _ in range(200):
y = torch.matmul(x, x.t())
torch.cuda.synchronize()
print("OK, elapsed: %.2fs" % (time.time() - t))
print(torch.cuda.get_device_name(0))
pip install torch torchvision --index-url https://download.pytorch.org/whl/cuXXX # cuXXX 与 CUDA 对齐
python test_train.py
3.7 vGPU 资源编排与租户配额
| 物理 GPU | vGPU Profile | 每卡实例数 | 典型租户场景 |
|---|---|---|---|
| A10 | A10-8C(8GB) | 3 | 轻/中模型训练、Finetune、LoRA |
| A10 | A10-12C(12GB) | 2 | 中/重模型、8–13B 量化训练 |
| A10 | A10-24C(24GB) | 1 | 单租户整卡吞吐、推理服务峰值 |
我最终分法:3×A10-8C + 1×A10-12C(跨四块卡),并不把所有卡切满,为高峰迁移/救火留出弹性位。
3.8 自动化(vGPU 批量开通)
我偏爱 govc(vSphere 的轻量 CLI),在模板克隆后跑一把:
# 为 VM 添加 vGPU Profile
govc device.vgpu.add -vm win2019-tenant01 -profile grid_a10-8c
govc device.vgpu.add -vm win2019-tenant02 -profile grid_a10-8c
govc device.vgpu.add -vm win2019-tenant03 -profile grid_a10-12c
Windows 内部则用 PowerShell 做驱动与依赖的“无感安装”(见 3.5 的静默安装)。
3.9 监控与告警(我用“够用就好”的办法先跑起来)
nvidia-smi CSV 采样 → Telegraf/InfluxDB(Windows 很好落地):
while ($true) {
& "$Env:ProgramFiles\NVIDIA Corporation\NVSMI\nvidia-smi.exe" --query-gpu=timestamp,utilization.gpu,utilization.memory,memory.used,memory.total --format=csv,noheader >> C:\gpu.csv
Start-Sleep -Seconds 10
}
租户侧给个“GPU 利用率 < 20% 且进程空闲 30 分钟”的告警,运维侧看“显存吃紧”的红线。
4)方案 B:Windows Server 2019 + Hyper-V + DDA(整卡直通,快速稳定)
当环境不能换宿主(合规/资产流程/短期窗口太短),我就用 DDA 先顶上:每个 VM 吃一整块卡,不是共享,但能让多个团队并行,而且兼容性极好。
4.1 前置
同样打开 BIOS 的 IOMMU / SR-IOV / Above 4G。
安装 Windows Server 2019 Datacenter + Hyper-V 角色。
确认 GPU 出现在 Device Manager,且驱动装妥(数据中心卡优先 TCC)。
4.2 把 GPU 从宿主“摘下来”并直通给 VM
# 1) 找到 GPU 位置路径
$gpu = Get-PnpDevice -Class Display | Where-Object { $_.FriendlyName -like "*NVIDIA*" }
$loc = (Get-PnpDeviceProperty -InstanceId $gpu.InstanceId -KeyName DEVPKEY_Device_LocationPaths).Data[0]
# 2) 解除宿主占用,标记为可分配
Dismount-VMHostAssignableDevice -LocationPath $loc -Force
# 3) 分配给 VM(关机状态)
Add-VMAssignableDevice -VMName "tenant01" -LocationPath $loc
提示:部分平台需要提升 MMIO 大小(Hyper-V 主机启动参数或注册表),否则 VM 启不来或蓝屏。
4.3 VM 内安装驱动与 CUDA
与物理机一致;DDA 等价于物理直通。nvidia-smi 出现完整卡信息即可。
注意:DDA 不支持多租户共享一张卡。有几张卡就能同时服务几个租户。
优点是 稳与快、回滚简单;缺点是 利用率提升有限(但往往也能从 30%→60%+,因为“人能并行了”)。
5)资源规划与效果对比(我当时提交给老板的表)
5.1 规划表(上线初版)
| 物理卡 | 切分 | Profile | 绑定 VM | vCPU | 内存 | 任务类型 |
|---|---|---|---|---|---|---|
| A10#1 | 3 | A10-8C ×3 | win19-t01/02/03 | 8 | 32GB | LoRA、检索蒸馏 |
| A10#2 | 2 | A10-12C ×2 | win19-t04/05 | 12 | 48GB | 8–13B 量化训练 |
| A10#3 | 1 | A10-24C ×1 | win19-t06 | 16 | 64GB | 大 batch 预训练 |
| A10#4 | 2 | A10-8C ×2 | win19-t07/08 | 8 | 32GB | 图像/多模态 |
5.2 成果对比(真实一周平均)
| 指标 | 改造前(单租户整机) | 改造后(vGPU 多租户) |
|---|---|---|
| GPU 平均利用率 | 28–35% | 72–85% |
| 显存利用率 | 30–40% | 70–90% |
| 并发训练队列 | 1–2 | 6–8 |
| 单任务等待时间 P50 | 9.5 小时 | 2.1 小时 |
| 回退/维护窗口 | 需要停机 | 不停机迁移(模板滚动) |
6)部署过程里的“真坑”和我的解法
驱动版本对不上(宿主 vGPU Manager vs Guest Driver):直接表现为 Code 43 或 vGPU 初始化失败。
解法:严格 一对一版本。模板里把驱动包和版本号放到 C:\_artifacts\_gpu.txt,脚本校验不一致就退出。
License 没拿到:nvidia-smi 里显示 Unlicensed,一小时后掉性能。
解法:License Server 双机热备,租户模板内置注册表与服务自愈脚本;Grafana 告警“未授权 vGPU > 5 分钟”。
vMotion/DRS 行为:早期版本对 vGPU 的热迁移有限制。
解法:训练集群里给 vGPU VM 标记 Anti-Affinity,避免被随意挪动;维护窗口采用 关机迁移 或“停容器不关机”(应用层断点续训)。
NUMA & PCIe 拓扑:VM 跨 NUMA 时延抖动。
解法:让吃重 VM 的 vCPU 数量贴近单 NUMA,避免超配;把数据盘和数据网络尽量走同一 CPU 根复杂度较低的路径。
显存“看似够、实际炸”:框架里 DataLoader/AMP/显存碎片化会把 8GB 用成 10GB。
解法:在模板里内置 torch.cuda.memory_summary() 的 profiling 开关;默认给 10–15% buffer。
Windows Update 抢重启:训练一半被系统重启,简直想砸机柜。
解法:统一组策略 暂停更新 + 维护窗口手动推进;租户层加“长任务防护”脚本拦截重启。
7)团队使用手册(我给租户的“秒懂”清单)
我有几张卡? 你只看见自己那张 vGPU;nvidia-smi -L 会显示 GPU 0: NVIDIA A10 (UUID: GPU-...) [vGPU]。
用不满记得退:任务结束,请在门户把 VM 关机或切回低配。
训练小贴士:批量从 1/2 起步,显存报警再调;遇到 OOM 先减 --grad-accum 再看 AMP。
数据位置:\\nas-ai\datasets(SMB,25G 网),训练结果写回 D:\results\{project},自动备份。
8)附:我当时用的几段“真能用”的脚本
8.1 govc 批量克隆与挂 vGPU
# 克隆 3 台租户 VM
for i in 01 02 03; do
govc vm.clone -vm win2019-template -on=false win2019-tenant$i
govc device.vgpu.add -vm win2019-tenant$i -profile grid_a10-8c
govc vm.power -on win2019-tenant$i
done
8.2 Windows 驱动静默安装 + 许可校验
$log = "C:\nvidia_install.log"
Start-Process ".\setup.exe" -ArgumentList "-s -noreboot -clean -noeula -log $log" -Wait
reg add "HKLM\SOFTWARE\NVIDIA Corporation\Global\GridLicensing" /v ServerAddress /t REG_SZ /d 10.10.10.5 /f
reg add "HKLM\SOFTWARE\NVIDIA Corporation\Global\GridLicensing" /v FeatureType /t REG_DWORD /d 2 /f
Restart-Service NVDisplay.ContainerLocalSystem -Force
Start-Sleep 5
& "$Env:ProgramFiles\NVIDIA Corporation\NVSMI\nvidia-smi.exe" | Out-String | Select-String "License"
8.3 简单训练环境(conda YAML 示例)
name: train-win2019
channels: [pytorch, nvidia, conda-forge, defaults]
dependencies:
- python=3.10
- pytorch=2.3.* cudatoolkit=12.1
- torchvision
- torchaudio
- cudnn
- numpy pandas matplotlib
- pip:
- sentencepiece
- transformers
9)如果你此刻“只能走 B 方案”(Hyper-V + DDA),也别怂
我有一次遇到合规卡点,不许换 ESXi。那就先把多卡分给多个 VM,队伍立刻从“排队”变“并行”。
过渡期内我仍然把数据盘/NIC/脚本做成和 vSphere 方案完全同构,下次停机窗口再切换就水到渠成。
10)收尾:凌晨三点的机房走廊
最后一台租户 VM 的 vGPU 点亮时,Grafana 的曲线也“抬起来”了:四张 A10 的平均利用率稳在 80% 左右。
我把 KVM 键盘放回托盘,小哥递来一罐还没完全冰透的汽水。走廊尽头的应急灯下,风声终于小了。
我知道这事儿算成了:不是堆卡,而是把卡用好。
下次台风来,我希望你也能在自己的机房里,看着那条 GPU 利用率曲线稳稳地顶在高位,心里踏实地想:这套多租户训练环境,值。
TL;DR(一眼就懂的结论)
要想真正“同卡多租户共享”:把 Windows 2019 放在 来宾(Guest),用 vSphere + NVIDIA vGPU。
短期不能换宿主:用 Hyper-V + DDA 先跑,多卡并行,配好模板和自动化,也能把效率提起来。
关键点:版本匹配、License 连通、显存余量、NUMA/IO 拓扑、监控与回滚预案。
结果:我的一线数据是 28–35% → 72–85% 的 GPU 利用率提升,等待时间从 9.5h → 2.1h。