本文介绍基于 libvirt 的应用程序使用的常见网络配置。此信息适用于所有「Hypervisor」,无论是 Xen,还是 KVM,又或者其他;
两种常见设置是:
- 虚拟网络:「NAT 转发」。开箱即用,无需过多设置;
- 共享设备:「桥接网络」。需要进行特定的手动配置;
本文主要介绍这两种网络。最后一部分是「网络设备直通」相关的内容;
虚拟网络:NAT 转发
#1 对 Host 配置
如果 libvirt 是标准安装的,那它默认提供基于 NAT 的虚拟机连接,开箱即用:
# virsh net-list --all Name State Autostart Persistent ---------------------------------------------- default inactive yes yes
如果不存在,可以进行定义或者激活:
# virsh net-define /usr/share/libvirt/networks/default.xml Network default defined from /usr/share/libvirt/networks/default.xml # virsh net-autostart default Network default marked as autostarted # virsh net-start default Network default started
上面的 default.xml 文件内容类似于:
<!-- virsh net-dumpxml --inactive --network default --> <network> <name>default</name> <uuid>aa81e36e-28c6-4ba7-87d1-4599b2671550</uuid> <forward mode='nat'/> <bridge name='virbr0' stp='on' delay='0'/> <mac address='52:54:00:a3:b6:d1'/> <ip address='192.168.122.1' netmask='255.255.255.0'> <dhcp> <range start='192.168.122.2' end='192.168.122.254'/> </dhcp> </ip> </network>
有关的网络定义方面的内容,参考官方「Network XML format」一文;
在网络启动后,你会看到网桥「virbr0」和网卡「virbr0-nic」。注意,这张网卡没有添加任何物理设备,因为它通过「NAT」+「转发」来访外部,不要添加设备
# brctl show bridge name bridge id STP enabled interfaces virbr0 8000.525400a3b6d1 yes virbr0-nic
同时 libvirt 会在 INPUT, FORWARD, OUTPUT, POSTROUTING 链中添加防火墙规则,以允许来自(或发往)绑定到 virbr0 设备的 Guest 的流量。它也会尝试启用 ip_forward 设置(net.ipv4.ip_forward = 1);
和「虚拟网络」有关的另一个东西是 dnsmasq 服务,每个虚拟网络都会有一个对应的 dnsmasq 实例。参考「Libvirtd and dnsmasq」一文;
#2 对 Guest 配置
网络启动之后,就可以进行 Guest 的配置:
<!-- virsh edit --domain <guest> --> <interface type='network'> <source network='default'/> <mac address='00:16:3e:1a:b3:4a'/> </interface>
其中<mac>是可选的,如果没有设置会自动定义;
有关网络接口的定义,可以参考「Network interfaces」一文;
#3 生效网络修改
有时,需要编辑网络定义,并动态应用更改。最常见的场景是:为网络的 DHCP 服务器添加新的静态 MAC IP 映射。如果使用virsh net-edit编辑网络,则在网络被销毁并重新启动之后才会生效,但是这会导致所有 Guest 网络接口在重新连接之前失去与主机的网络连接;
对于这个问题,可以使用virsh net-update来解决。例如,添加 MAC IP 绑定:
virsh net-update default add ip-dhcp-host \ "<host mac='52:54:00:00:00:01' name='bob' ip='192.168.122.45' />" \ --live --config
除了add命令以外,还有delete、modify、add-first、add-last命令。除了ip-dhcp-host配置项,还有ip-dhcp-range、forward-interface、portgroup、dns-host、dns-txt、dns-srv配置项。详细的使用方法可以参考man 1 virsh手册;
注意:命令行中 XML 的内容应该是你要修改或者添加的内容;并且留意 XML 的转义;
当然,有些网络修改就是需要重启网络,这些修改无法通过virsh net-update来更新。重启会导致所有运行中的 Guest 从网络上断开连接。其中一个解决方案是在重启后连接所有的网络,官方给了一个「脚本」,但是那个连接打不开……应该是这个「脚本」;
# 转发“传入连接”
默认情况下,通过<forward mode='nat'/>的虚拟网络,「Guest」可以随意访问外部网络,也可以接受来自「Host」或者「同网络的 Guest」的连接;
但是除了上述几种的连接,其他的“传入连接”都会被 iptables 规则屏蔽;
如果要解决这个问题,你可以为 qemu 设置 libvirt 的“钩子”脚本来设置必要的 iptables 规则(DNAT),来转发传入的连接,从指定的端口到 Guest 的端口;
实际上就是:使用 libvirt 的钩子在 Guest 启动(停止)时,添加(移除)防火墙规则。参考「Forwarding_Incoming_Connections」手册;
共享设备:网络桥接
#1 对 Host 配置
基于 NAT 的连接在“快速简单的部署”或者“动态网络”方面非常有用。但更高级的用户将希望使用「完全桥接」,其中 Guest 直接连接到 LAN 中。此设置依赖于发行版,不同的发行版在操作上有所不同;
!!!遗憾的是,如果通过无线接口(“wlanX”)连接到外部网络,则无线接口无法连接到 Linux 主机桥接器,因此,无法为 Guest 虚拟机使用此网络模式;
!!!如果在尝试使用网桥接口后,您发现网络链路已经死亡并且拒绝再次工作,则可能是上游路由器 / 交换机阻止网络中的“未授权交换机”(例如,通过检测 BPDU 数据包)。您必须更改其配置以明确允许主机 / 网络端口作为“交换机”;
在 Host 上,如果使用 NetworkManager 来管理网络:
在 Host 上,不使用 NetworkManager 来管理网络:
- 对于 RHEL/Fedora 系列,使用传统的网桥配置方式及
- 对于 Debian 系列,参考「BridgeNetworkConnections」手册
- 对于其他的发行版,也是类似的操作思路
这一步的目的是:添加桥接网卡「br0 接口」(或其他),桥接到「eth0 接口」(或其他)上。配置 Guest 时要用到该网卡;
#2 对 Guest 配置
为了让 Guest 使用此网桥,其配置应包括接口定义,如「Bridge to LAN」中所述。实质上,是在指定要连接的网桥名称。假设桥接器是名为“br0”的共享物理设备,则 Guest 虚拟机 XML 为:
<interface type='bridge'> <source bridge='br0'/> <mac address='00:16:3e:1a:b3:4a'/> <model type='virtio'/> <!-- # try this if you experience problems with VLANs --> </interface>
其中,mac地址是可选的,如果忽略会自动生成;
使用virsh edit <VM name>来修改配置文件;
更多的问题参见 FAQ 中的条目「Where are VM config files stored? How do I edit a VM’s XML config?」
关于网络设备直通
可以把 PCI 网络设备“直通(Passthrough)”给 Guest 虚拟机。前提是 Host 必须支持「Intel VT-d」或「AMD IOMMU」技术;
有两种分配设备的方法:
- 使用<hostdev>分配
- 使用<interface type='hostdev'>分配(仅 SRIOV 设备)
#1 使用<hostdev>分配
设置一种传统的分配 PCI 设备的方法,不仅限于网卡,参考「libvirt PCI Device Assignment」一文
#2 使用<interface type=’hostdev’>分配(仅 SRIOV 设备)
SRIOV 网卡提供多个“虚拟功能”(VF),每个 VF 都可以使用“PCI 设备分配”单独分配给 Guest,每个都将充当完整的物理网络设备。这允许许多 Guest 获得直接 PCI 设备分配的性能优势,而仅使用物理机器上的单个插槽;
这些 VF 可以使用<hostdev>以传统方式分配给 Guest,但是这种方法最终会出现问题,因为(与常规网络设备不同)SRIOV VF 网络设备没有永久唯一的 MAC 地址,而是每次重启 Host 操作系统时给出了一个新的随机 MAC 地址。结果是,即使每次为 Guest 虚拟机分配相同的 VF,在每次重新启动主机后,Guest 虚拟机都会看到其网络适配器具有新的 MAC 地址,这将导致 guest 虚拟机认为连接了新硬件, 需要重新配置 Guest 的网络设置;
主机可以在将 VF 分配给 Guest 虚拟机之前设置 MAC 地址,但是在<hostdev>设置中没有为此设置(因为<hostdev>用于通用 PCI 设备,它对特定功能项目,如 MAC 地址,一无所知)。为了解决这个问题,libvirt-0.9.10 添加了一个新的<interface type ='hostdev'>(「文档」)。这种新型接口设备表现为<interface>和<hostdev>的混合体 – libvirt 将首先执行任何特定于网络的硬件 / 交换机初始化(例如设置 MAC 地址,并与 802.1Qbh 交换机关联),然后执行 PCI 设备分配给 Guest;
要使用<interface type ='hostdev'>,你必须具有支持 SRIOV 的网卡,并且支持 Intel VT-d 或 AMD IOMMU 扩展的 Host 硬件,并且您必须了解您希望分配的 VF 的 PCI 地址 (有关如何执行此操作的说明,请参阅此文档);
验证 / 了解上述信息后,您可以编辑 Guest 的域配置,设置如下设备条目:
<interface type='hostdev' managed='yes'> <source> <address type='pci' domain='0x0' bus='0x00' slot='0x07' function='0x0'/> </source> <mac address='52:54:00:6d:90:02' /> <virtualport type='802.1Qbh'> <parameters profileid='finance'/> </virtualport> </interface>
请注意,如果您不提供 mac 地址,则会自动生成一个 mac 地址,就像使用任何其他类型的接口设备一样。此外,仅当您连接到 802.11Qgh 硬件交换机(802.11)时才会使用<virtualport>元素。此模式当前不支持 Qbg(也称为“VEPA”)交换机;
当 Guest 虚拟机启动时,它应该看到物理适配器提供的类型的网络设备,具有配置的 MAC 地址。在 Guest 和 Host 重新启动后,此 MAC 地址将保持不变;
# 从“在 libvirt 的<network>定义的 SRIOV VF 池”中分配
将特定 VF 的 PCI 地址硬编码到 Guest 虚拟机配置中有两个严重的限制:
(2)、如果将 Guest 虚拟机移动到另一台主机,则该主机必须在 PCI 总线上的相同位置具有完全相同的硬件(或者,同样必须在启动之前修改 Guest 虚拟机配置);
从 libvirt 0.10.0 开始,可以避免这两个问题,通过创建一个“包含 SRIOV 设备的所有 VF 的设备池”的“libvirt 网络”,然后配置 Guest 虚拟机以引用该网络。每次启动 Guest 时,将从池中分配一个 VF 并分配给 Guest;当 Guest 停止时,VF 将返回到池中以供另一位访客使用;
以下是一个示例网络定义,它将为 SR-IOV 适配器提供所有 VF 池,其 PF(物理功能)位于主机上的“eth3”:
<network> <name>passthrough</name> <forward mode='hostdev' managed='yes'> <pf dev='eth3'/> </forward> </network>
要使用此网络,请将上述文本放在例如/tmp/passthrough.xml中(将“eth3”替换为您自己的 SR-IOV 设备的 PF 的 netdev 名称),然后执行以下命令:
#!/bin/sh virsh net-define /tmp/passthrough.xml virsh net-autostart passthrough virsh net-start passthrough.
虽然只显示了一个设备,但 libvirt 将在第一次使用接口定义启动 Guest 虚拟机时自动派生与该 PF 关联的所有 VF 的列表,如下所示:
<interface type='network'> <source network='passthrough'> </interface>
您可以在启动使用网络的第一个 Guest 虚拟机后运行“virsh net-dumpxml passthrough”来验证这一点;将获得类似于以下内容的输出:
<network connections='1'> <name>passthrough</name> <uuid>a6b49429-d353-d7ad-3185-4451cc786437</uuid> <forward mode='hostdev' managed='yes'> <pf dev='eth3'/> <address type='pci' domain='0x0000' bus='0x02' slot='0x10' function='0x1'/> <address type='pci' domain='0x0000' bus='0x02' slot='0x10' function='0x3'/> <address type='pci' domain='0x0000' bus='0x02' slot='0x10' function='0x5'/> <address type='pci' domain='0x0000' bus='0x02' slot='0x10' function='0x7'/> <address type='pci' domain='0x0000' bus='0x02' slot='0x11' function='0x1'/> <address type='pci' domain='0x0000' bus='0x02' slot='0x11' function='0x3'/> <address type='pci' domain='0x0000' bus='0x02' slot='0x11' function='0x5'/> </forward> </network>
应用场景
Guest to Guest
how to connect multiple kvm/qemu VM guests in to a subnet
Building a virtual network with qemu
参考文献