「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来管理网络:

	* [[https://www.happyassassin.net/2014/07/23/bridged-networking-for-libvirt-with-networkmanager-2014-fedora-21/|Using nm-connection-editor UI]]
	* [[https://lukas.zapletalovi.com/2015/09/fedora-22-libvirt-with-bridge.html|Using the command line]]

在Host上,不使用NetworkManager来管理网络:

	* 对于RHEL/Fedora系列,使用传统的网桥配置方式及
	* 对于Debian系列,参考「[[https://wiki.debian.org/BridgeNetworkConnections|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>

参考文献

libvirt/Networking