在高性能计算、低延迟网络传输(如金融交易、电信网元 NFV)等场景下,普通的 VirtIO 虚拟网卡即使开启了 vhost-net,也无法满足极致的吞吐和延迟要求。**SR-IOV(Single Root I/O Virtualization)**技术通过将物理网卡虚拟出多个独立的 PCIe 虚拟通道(VF, Virtual Function),直接透传给虚拟机(VM),能提供接近物理硬件的裸金属级网络性能。
然而,传统的 PCI 直通通常需要在虚拟机关机状态下配置。如何在生产环境下不中断业务、不重启虚机,动态地将 SR-IOV 网卡热插、热拔?
下面将从宿主机配置、XML 定义、动态热插拔操作及避坑指南四个维度,给出完整的实战方案。
一、 准备工作:宿主机开启 IOMMU 与创建 VF
在进行热插拔之前,必须确保宿主机内核已启用 IOMMU,且物理网卡已经生成了虚拟函数(VF)。
1. 开启 IOMMU(以 Intel CPU 为例)
编辑宿主机的 Grub 配置文件 /etc/default/grub,在 GRUB_CMDLINE_LINUX 中追加:
intel_iommu=on iommu=pt
更新 Grub 并重启宿主机生效:
# CentOS/RHEL/Fedora
grub2-mkconfig -o /boot/grub2/grub.cfg
# Ubuntu/Debian
update-grub
reboot
重启后验证 IOMMU 是否正常启用:
dmesg | grep -i iommu
# 输出中应包含 "IOMMU enabled" 或 "Adding to iommu group" 相关字样
2. 生成物理网卡的 VF
假设宿主机的 10G/25G 物理网卡接口名为 ens1f0。
执行以下命令启用 4 个 VF:
echo 4 > /sys/class/net/ens1f0/device/sriov_numvfs
验证 VF 是否生成成功:
ip link show ens1f0
如果输出中看到 vf 0 MAC 00:00:00:00:00:00... 等多行 VF 信息,说明生成成功。
注意:上述
echo写入 sysfs 的方式在宿主机重启后会失效。建议将该命令写入/etc/rc.local,或者通过 Netplan/ifupdown 脚本进行持久化配置。
二、 确定待热插拔的 VF PCI 地址
我们需要通过 lspci 找到刚才生成的 VF 的 PCI 总线地址。
lspci -D | grep -i "Virtual Function"
输出示例:
0000:04:10.0 Ethernet controller: Intel Corporation Ethernet Virtual Function 700 Series (rev 02)
0000:04:10.1 Ethernet controller: Intel Corporation Ethernet Virtual Function 700 Series (rev 02)
0000:04:10.2 Ethernet controller: Intel Corporation Ethernet Virtual Function 700 Series (rev 02)
0000:04:10.3 Ethernet controller: Intel Corporation Ethernet Virtual Function 700 Series (rev 02)
我们选择使用第一个 VF,其 PCI 地址为 0000:04:10.0。
三、 配置 XML 模板(避坑关键:用 interface 还是 hostdev?)
在 KVM (libvirt) 中,直通 PCI 设备有两种 XML 定义方式:
<hostdev>:通用 PCI 直通。<interface type='hostdev'>:专为网络设备设计的直通。
强烈建议使用第二种 <interface type='hostdev'>。
因为通过 <interface> 方式,Libvirt 可以在宿主机侧帮我们集中管理虚拟机的 MAC 地址、VLAN tag 以及 IP 链路状态(Link State)。如果是纯 <hostdev>,虚拟机会随机生成 MAC 地址,甚至在热插拔后由于 MAC 地址变更导致网络不通。
新建一个临时配置文件 sriov-vf.xml:
<interface type='hostdev' managed='yes'>
<!-- 宿主机上该 VF 的物理驱动,通常为 vfio -->
<driver name='vfio'/>
<!-- 指定该 VF 对应的物理网卡 PCI 地址 -->
<source>
<address type='pci' domain='0x0000' bus='0x04' slot='0x10' function='0x0'/>
</source>
<!-- 预先规划好直通给 VM 后的 MAC 地址,防止热插拔后随机漂移 -->
<mac address='52:54:00:fa:19:bc'/>
<!-- 如果有 VLAN 需求,可在此定义,没有则删掉下面这一行 -->
<vlan><tag id='100'/></vlan>
</interface>
四、 零下行动态热插拔操作
假设我们要操作的虚拟机名称(Domain Name)为 prod-web-vm。
1. 动态热插(Attach)
在不关闭虚拟机的情况下,执行以下命令将 VF 动态挂载到虚机中:
virsh attach-device prod-web-vm sriov-vf.xml --live --config
--live:立即生效(作用于当前运行中的虚机内存)。--config:将配置写入虚机的 XML 配置文件中,确保虚机下一次重启后,该网卡依然存在。
验证热插结果:
登录到虚拟机内部,执行以下命令查看网卡是否已上线:
# 1. 检查 PCI 设备列表中是否出现新网卡
lspci | grep -i ether
# 2. 检查网卡接口
ip link show
此时会发现多出一个类似 eth1 或 enp0s8 的新接口。由于指定了 MAC 地址,该接口会自动获取 IP 启动业务。
2. 动态热拔(Detach)
当需要对宿主机硬件进行维护,或者需要将该 VF 回收给其他虚机时,可以进行动态热拔。
安全避坑第一步:在热拔前,建议在虚拟机内部先将该网卡接口 Down 掉,避免虚拟机内核或业务应用因底层 PCI 设备瞬间消失而产生死锁或报错。
在虚机内部执行:
ip link set dev eth1 down
然后在宿主机上执行热拔命令:
virsh detach-device prod-web-vm sriov-vf.xml --live --config
--live:立即从当前运行的虚机中剥离该设备。--config:同步更新虚机的 XML 配置,防止下次启动时再去寻找该 PCI 设备。
在虚拟机内部再次执行 lspci 或 ip link,会发现该网卡已无损、平滑地消失。
五、 高可用进阶:SR-IOV 与 VirtIO 的 Active-Backup 双网卡绑定(Bonding)
SR-IOV 直通虽然性能强悍,但带来了一个致命缺陷:无法进行虚拟机热迁移(Live Migration)。因为物理网卡的硬件状态无法在网络中传输。
为了兼顾“平时跑 SR-IOV 享受极速网络”与“维护时支持热迁移”,业界通用的解决方案是在虚机内部配置 Active-Backup 模式的 Bonding:
- 虚机配置两张网卡:
eth0:标准的 VirtIO 网卡(走 OVS/Linux Bridge,支持热迁移)。eth1:直通的 SR-IOV VF 网卡(动态热插拔)。
- 在虚机内创建主备 Bond(如使用
bond0采用mode 1主备模式):- 将
eth1(SR-IOV)设为主设备(Primary),平时所有业务流量走 SR-IOV,延迟极低。 - 将
eth0(VirtIO)设为备份设备。
- 将
- 当需要对宿主机进行维护需要热迁移虚机时:
- 在宿主机执行
virsh detach-device动态热拔移除 SR-IOV 网卡。 - 虚机内核检测到
eth1消失,流量无缝切换到eth0(VirtIO),业务不中断。 - 执行虚拟机热迁移到新宿主机。
- 在新宿主机上,动态热插新主机的 SR-IOV VF 网卡,流量再次切回主设备。
- 在宿主机执行
通过这种“硬直通 + 软虚拟化”的双网卡联动架构,可以在保障 99.99% 高可用的同时,压榨出物理网卡的最后一滴性能。