「KVM」- Networking

  CREATED BY JENKINSBOT

本文介绍基于 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命令以外,还有deletemodifyadd-firstadd-last命令。除了ip-dhcp-host配置项,还有ip-dhcp-rangeforward-interfaceportgroupdns-hostdns-txtdns-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 虚拟机配置中有两个严重的限制:

(1)、在 Guest 虚拟机启动时,指定的 VF 必须可用,这意味着管理员必须将每个 VF 永久分配给单个 Guest 虚拟机(或者每次启动 Guest 虚拟机时修改 Guest 虚拟机的配置以指定当前未使用的 VF 的 PCI 地址);

(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

参考文献

libvirt/Networking