一、先说说这件事有多让人头疼

做虚拟化时间长了,你总会遇到这么个情况:一台虚拟机因为用了PCIe直通,结果就像绑在一台特定的服务器上,想搬家都搬不了。比如给AI虚拟机直通了一块GPU,性能确实好,可到了要升级服务器、调整机房布局,或者把负载挪到另一台宿主机的时候,问题就来了:vMotion不让用,在线迁移直接被禁。你会发现这台虚拟机只能老老实实关机,挪到新主机上,再重新认一遍硬件,然后开机。如果业务是核心系统,这种“关机-等待-再开机”的流程,往往意味着半小时甚至几个小时的业务空窗。今天我跟你聊一套思路,尽量把这个空窗期压到最小,让整个过程更像一次真正的“无缝搬家”。

二、为什么PCIe直通设备不能随便乱搬

要搞懂怎么解决,先得知道它为什么难搞。PCIe直通是把物理设备直接分配给一台虚拟机用,中间不经过太多软件模拟层。好处是开销低、延迟小、性能接近裸机,坏处是这台虚拟机从本质上跟这个物理设备绑死了。因为设备寄存器、DMA内存、中断这些资源都被虚拟机直接占用了,宿主机之间没法把这种状态热切换过去。这就像你把一把钥匙直接交给了别人,别人搬走后,你手里就什么都没了。vSphere之所以不允许带着直通设备vMotion,就是怕在迁移过程中,源主机和新主机同时操作同一个物理设备,数据就乱了。

另外,直通设备在虚拟机配置里是以“虚拟PCI设备”的形式存在的,它跟虚拟机所在的宿主机上的具体PCI设备一一对应。换一台宿主机,设备地址、驱动状态都不一样,所以必须先把虚拟机上那一层直通配置摘掉,再在新的宿主机上重新挂。这就是为什么迁移总是绕不开关机。

三、破局思路:把“停机窗口”压到最小

那就没法优化了吗?当然有。我们要做的不是不关机,而是把“关机”这件事变成整个过程中最小、最可控的一步。这里有三件工具很关键:热添加、冷迁移和维护模式。

热添加是指虚拟机开机状态下就可以增加CPU、内存这类硬件,不打断业务。冷迁移是指虚拟机关机后把它从一台宿主机搬到另一台,这个动作没有共享存储限制,但必须断电。维护模式是vSphere里对宿主机做“暂停接客”的一个开关,开启后,vCenter会尝试把这台机器上的所有普通虚拟机自动迁到别的宿主机去。

聪明的你可能已经想到了:先把能提前干的活儿干完,让最后的冷迁移只做“换主机”这一件事。具体来说就是,先热添加好目标配置,再用Storage vMotion把磁盘搬到共享存储,接着打开维护模式,让普通虚拟机自动跑干净,最后让直通虚拟机在一个精心挑选的时间点关机、迁移、开机。整个业务中断时间可能就剩下几十秒到几分钟,这比硬生生把整个虚拟机拷贝一遍再开机要快得多。

四、动手前先看:你的环境具备这些条件吗

在做割接之前,有几项准备工作必须确认,不然等真动手才发现缺条件,那就尴尬了。

首先,宿主机需要开启CPU的VT-d或AMD-Vi。这是硬件虚拟化技术,允许虚拟机直接访问PCIe设备。通常在BIOS里叫“Intel VT-d”或者“AMD IOMMU”,具体名字各品牌不一样。

其次,目标宿主机必须也配置了同样的直通设备,并且该设备已经通过vSphere的“PCI直通”配置添加到了宿主机里,可以作为可用设备分配给虚拟机。你不能指望一台没有GPU的主机,能把GPU虚拟机接过去。

再就是,虚拟机要支持热添加。在虚拟机设置里,必须开启“CPU热添加”和“内存热添加”选项,否则后面的热添加步骤会失败。这个可以在虚拟机创建阶段或关闭状态下预先打开。

最后,网络和存储要保证。因为我们要做Storage vMotion,虚拟机磁盘必须放在共享存储上,比如光纤通道、iSCSI或者NFS。如果虚拟机是本地盘,那只能通过冷迁移时一起拷贝,时间就长了。

下面用PowerCLI快速检查一下环境里所有的直通设备。注意,所有命令执行前都需要你先连上vCenter。

# 技术栈:PowerCLI
# 连接vCenter,输入管理员账号密码
Connect-VIServer -Server vcenter.lab.local -User admin@vsphere.local -Pass (Read-Host "输入密码")

# 查看所有ESXi主机上已启用或正在使用的PCI直通设备
Get-VMHost | Get-VMHostPciDevice |
    Where-Object { $_.PassthruEnabled -or $_.PassthruActive } |
    Format-Table VMHost, Id, Name, PassthruActive -AutoSize

结果会列出来哪些主机上有哪些设备正在被使用。你需要在目标主机上看到同一块显卡或者同一块SSD的设备ID,才能保证后面能挂给你那台虚拟机。注意,这里的方案主要针对GPU、加速卡这类设备,如果直通的是存储控制器,情况会更复杂,需要先对数据做备份或离线拷贝。

五、方案实战:一个完整的割接过程

下面用一台叫“AIWorker”的虚拟机来演示。这台虚拟机现在跑在ESXi01上,直通了一块NVIDIA T4 GPU,需要把它迁到ESXi02,ESXi02也插了一块同样的T4。

5.1 第一步:先把虚拟机“身体”准备好(热添加)

如果新主机资源不如源主机,或者你希望趁机给虚拟机扩容,可以在开机状态下把CPU和内存加好。这个动作虚拟机会自动识别,不需要重启。比如给虚拟机多分配两颗vCPU和16GB内存。

# 技术栈:PowerCLI
# 确认虚拟机当前配置
Get-VM -Name "AIWorker" | Select-Object Name, NumCPU, MemoryGB

# 热添加:增加CPU到8颗,内存加到64GB
Get-VM -Name "AIWorker" | Set-VM -NumCpu 8 -MemoryGB 64 -Confirm:$false

注意,热添加内存需要虚拟机操作系统支持,Windows和主流Linux都没问题。另外,热添加不是无限加的,最大上限取决于虚拟机配置和宿主机剩余资源。这一步纯粹是为了让虚拟机在迁到新主机后不需要再关机加配置,属于“提前量”。

5.2 第二步:先把数据搬走,别动虚拟机的运行

PCIe直通会影响主机迁移,但不会影响存储迁移。我们可以利用Storage vMotion把虚拟机的所有磁盘搬到共享存储去。这样即使后面冷迁移需要重新配置,也不用花时间复制几十GB的硬盘数据。

# 技术栈:PowerCLI
# 指定目标数据存储,比如名为 FASTER400G 的共享NFS
Move-VM -VM "AIWorker" -Datastore "FASTER400G" -Confirm:$false

执行过程中虚拟机会自动打开一个快照,磁盘内容在后台拷贝,业务不会中断。整个过程可能持续一段时间,取决于磁盘大小和网络速度。执行完后,你可以看到虚拟机的磁盘文件已经全部存放在共享存储上,这就为后续冷迁移省掉了大量等待时间。

5.3 第三步:让维护模式自动赶走“拖油瓶”

现在该让源主机ESXi01腾出来了。对ESXi01开启维护模式,vCenter会尝试把上面其他所有普通虚拟机通过vMotion迁到其他主机。注意,像“AIWorker”这种有直通设备的虚拟机,vCenter一般不会自动去迁,它会留在原处,但你也无法强行启动迁移,因为直通设备阻止vMotion。所以维护模式会把它留在原地,并且有可能阻止你进入维护模式,或者进入后会报警。

在实际操作中,你要先手动处理直通虚拟机。具体做法可以有两种:一种是把直通设备先移除,让虚拟机变成普通虚拟机,然后就可以正常vMotion;另一种是让维护模式保持在“等待”状态,直通虚拟机不处理,最后统一在窗口期手动关机迁移。我推荐后者,因为移除直通设备也会导致业务中断,不如留到最后一起做。

下面这个命令帮你把主机搞进维护模式,并且不自动迁移任何带直通的虚拟机。如果vSphere提示有虚拟机卡住,你可以先检查一下是不是只有直通虚拟机。

# 技术栈:PowerCLI
# 让ESXi01进入维护模式,普通虚拟机会自动迁移
Set-VMHost -VMHost "esx01.lab.local" -State Maintenance -EvacuateAllVMs -Confirm:$false

如果vSphere提示这台主机上还有使用了PCI直通设备的虚拟机无法迁出,你可以用下面的脚本找出所有直通虚拟机,核实一下是不是只有“AIWorker”这一个。

# 技术栈:PowerCLI
# 找出所有带有直通设备的虚拟机
Get-VM |
    Where-Object { $_.ExtensionData.Config.Hardware.Device |
        Where-Object { $_ -is [VMware.Vim.VirtualPCIPassthrough] } } |
    Select-Object Name, PowerState, VMHost

确认后,这台“AIWorker”就成了维护模式下唯一还没迁走的虚拟机。

5.4 第四步:处理直通虚拟机——冷迁移

现在进入最关键的窗口。在业务允许的维护时间,把“AIWorker”关机,然后在关机状态下解除直通设备的绑定,再迁移到ESXi02,最后重新挂载直通设备并开机。

我们先关机。如果是Windows虚拟机,优雅关机比较好;如果超时,再强制断电。

# 技术栈:PowerCLI
# 优雅关闭虚拟机操作系统,等待60秒
Shutdown-VMGuest -VM "AIWorker" -Confirm:$false
Wait-Tools -VM "AIWorker" -For "GuestShutdown" -TimeoutSeconds 60

# 如果希望保险,再检查一次电源状态
if ((Get-VM -Name "AIWorker").PowerState -ne "PoweredOff") {
    Stop-VM -VM "AIWorker" -Confirm:$false
}

关机后,我们先把“AIWorker”上的PCI直通设备从配置里移除。这一步要通过vSphere API做,PowerCLI没有现成的单条命令。

# 技术栈:PowerCLI
$vm = Get-VM -Name "AIWorker"
$spec = New-Object VMware.Vim.VirtualMachineConfigSpec

# 遍历虚拟机的每一个设备,如果是PCI直通设备就删除
foreach ($device in $vm.ExtensionData.Config.Hardware.Device) {
    if ($device -is [VMware.Vim.VirtualPCIPassthrough]) {
        $deviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec
        $deviceChange.Operation = [VMware.Vim.VirtualDeviceConfigSpecOperation]::remove
        $deviceChange.Device = $device
        $spec.DeviceChange += $deviceChange
    }
}

# 执行重构配置
$vm.ExtensionData.ReconfigVM_Task($spec)
Write-Host "PCI直通设备已移除,虚拟机现在可以迁移了。"

移除后,虚拟机变成一台普通虚拟机。虽然它是关机状态,但我们已经把它变成可以跨主机迁移的状态。接下来执行冷迁移,也就是把虚拟机从ESXi01挪到ESXi02。由于磁盘已经在共享存储上,这一步通常非常快,实际上只是切换虚拟机文件和重新注册的过程。

# 技术栈:PowerCLI
Move-VM -VM "AIWorker" -Destination (Get-VMHost -Name "esx02.lab.local") -Confirm:$false

迁移完成后,需要再把PCI直通设备加回去。同样需要使用API方式,因为PowerCLI没有内置的“给虚拟机添加直通设备”的简单命令。

# 技术栈:PowerCLI
# 目标主机上找到我们要直通的那块T4 GPU
$pciDevice = Get-VMHostPciDevice -VMHost "esx02.lab.local" |
    Where-Object { $_.Id -eq "0000:06:00.0" }   # 这里的设备ID以实际为准

# 如果设备存在,就创建配置
if ($pciDevice) {
    $pci = New-Object VMware.Vim.VirtualPCIPassthrough
    $pci.Backing = New-Object VMware.Vim.VirtualPCIPassthroughDeviceBackingInfo
    $pci.Backing.DeviceId = $pciDevice.Id

    $spec = New-Object VMware.Vim.VirtualMachineConfigSpec
    $deviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec
    $deviceChange.Operation = [VMware.Vim.VirtualDeviceConfigSpecOperation]::add
    $deviceChange.Device = $pci

    $spec.DeviceChange = @($deviceChange)

    # 重新配置虚拟机
    Get-VM -Name "AIWorker" | ForEach-Object {
        $_.ExtensionData.ReconfigVM_Task($spec)
    }
    Write-Host "PCI直通设备已重新挂载。"
} else {
    Write-Warning "没找到指定的PCI设备,请检查目标主机上的设备ID。"
}

最后启动虚拟机,确认系统能识别到GPU,驱动正常,业务恢复。

# 技术栈:PowerCLI
Start-VM -VM "AIWorker" -Confirm:$false
# 等待VMware Tools报告启动完成
Wait-Tools -VM "AIWorker" -TimeoutSeconds 120

如果GPU的驱动是原厂的,启动后一般会直接识别到新的GPU实例,应用如果要使用CUDA之类的,也会正常加载。

5.5 第五步:收尾,退出维护模式

确认一切正常后,把ESXi01退出维护模式,让它可以重新接新虚拟机。

# 技术栈:PowerCLI
Set-VMHost -VMHost "esx01.lab.local" -State Connected -Confirm:$false

到这里,整个割接就算完成了。你看,真正让业务中断的只有关机到重新开机的那一段时间,前面的热添加、存储迁移都是在业务运行状态完成的。

六、这方案有哪些好处,哪些坑

先说好处。这套组合拳把大部分耗时行动转移到了在线状态,尤其是Storage vMotion,几百GB的数据拷贝不影响业务。维护模式自动迁移普通虚拟机,省去了手动一台台迁移的麻烦。最后只剩下直通虚拟机本身需要断一下电,停机时间能压到很短。

再说坑。第一,不是所有PCIe设备都支持被虚拟机直通,也不是所有支持直通的设备在某个新主机上都能被识别,提前要踩点测试。第二,热添加功能必须在虚拟机创建时开启,如果没开启,等需要时只能关机改配置,那就没有意义了。第三,移除PCI直通设备的过程涉及对虚拟机配置的修改,如果脚本写得不严谨,可能会把其他设备也误删除,所以代码里的过滤一定要管好。第四,共享存储必须足够大,否则Storage vMotion可能因为空间不足中断。

七、注意事项与避让指南

在实际项目里,有几点经验分享给你。

第一,先做一次空跑。找一台测试虚拟机,按同样的流程走一遍,确认命令、参数和时序都对了,再动生产。不要在生产环境临时试错。

第二,注意设备ID。不同ESXi主机对同一个物理设备的PCI ID可能完全不同。在脚本里写死ID之前,先用查看PCI设备的命令查清楚,并且确认目标主机上的设备没有被别的虚拟机占用。

第三,热添加内存时,客户机的操作系统可能不会立即使用新增内存,有些系统需要手动刷新或重新分配。别以为加完内存业务就自动变快,必要时可以重启一下业务进程。

第四,维护模式有超时和失败的可能性。如果其他普通虚拟机因为资源不足无法vMotion,维护模式会一直卡住。先查看DRS规则,或者提前为这些虚拟机找到归宿。

第五,冷迁移时虚拟机关机后,可能还有快照需要处理。如果有快照,建议在迁移前合并到基础盘,否则冷迁移会把快照也带过去,后续还是风险。

第六,直通设备在目标主机上重新挂载后,虚拟机里看到的设备地址可能会变。如果操作系统里配置了固定PCI地址,需要重新调整。好在这种情况不常见,但遇到了也不要慌。

八、总结

回到开头那个难题:PCIe直通设备让虚拟机没法在线迁移,这是硬件直通天生的脾气。但我们不需要硬碰硬,而是用“热添加 + Storage vMotion + 维护模式 + 冷迁移”的组合,把必须断开的窗口压缩到最小。整个过程就像搬精密的仪器,先把房子里的电线插座都迁好,最后只关一下灯,把仪器搬到新房间,再开灯。对业务来说,那一次短暂的黑灯,是完全可以接受的。

这套方法不需要额外购买软件,只要合理利用vSphere自带的功能和PowerCLI脚本,就能完成一次有惊无险的迁移。希望你在遇到类似场景时,能想起今天聊的思路,把“不能迁”变成“可以迁”,把“长中断”变成“短中断”。