使用 Ubuntu 16.04 和 KVM,如果不使用 NAT,我无法让虚拟机联网

使用 Ubuntu 16.04 和 KVM,如果不使用 NAT,我无法让虚拟机联网

我正在尝试让虚拟机在 KVM 中运行,使其在本地网络上具有自己的物理网络接口。我认为这称为桥接,我尝试过按照网上的许多不同指南操作,但都没有成功。每次我得到的虚拟机的虚拟网络适配器都无法与我的本地网络通信。

我的主机和客户机都运行 Ubuntu 服务器 16.04。

br0我首先向我的文件中添加一个/etc/network/interfaces,现在看起来像这样:

~$ cat /etc/network/interfaces
source /etc/network/interfaces.d/*

auto lo
iface lo inet loopback

iface enp2s0 inet manual

auto br0
iface br0 inet dhcp
  bridge_ports enp2s0
  bridge_stp off
  bridge_fd 0
  bridge_maxwait 0

重启后,我的 ifconfig 如下所示:

~$ ifconfig
br0       Link encap:Ethernet  HWaddr d0:50:99:c0:25:fb
          inet addr:192.168.113.2  Bcast:192.168.113.255  Mask:255.255.255.0
          ...

docker0   Link encap:Ethernet  HWaddr 02:42:dc:4f:96:9e
          ...

enp2s0    Link encap:Ethernet  HWaddr d0:50:99:c0:25:fb
          inet6 addr: fe80::d250:99ff:fec0:25fb/64 Scope:Link
          ...

lo        Link encap:Local Loopback
          ...

veth009cb0a Link encap:Ethernet  HWaddr 66:d6:6c:e7:80:cb
          inet6 addr: fe80::64d6:6cff:fee7:80cb/64 Scope:Link
          ...

virbr0    Link encap:Ethernet  HWaddr 52:54:00:1a:56:65
          inet addr:192.168.122.1  Bcast:192.168.122.255  Mask:255.255.255.0
          ...

主机在我的 DHCP 服务器中有一个静态条目,因此它始终获取 192.168.113.2,因此 br0 上的 IP 是正确的。据我了解,我现在需要做的就是使用 br0 接口启动一个新的虚拟机。因此我运行此命令:

sudo virt-install --virt-type=kvm --name myvm \
--hvm --ram 4096 --vcpus=2 --graphics vnc \
--network bridge=br0 \
--os-type=linux --os-variant=ubuntu16.04 \
--cdrom=/var/lib/libvirt/boot/ubuntu-16.04.2-server-amd64.iso \
--disk path=/var/lib/libvirt/images/myvm.qcow2,size=16,bus=virtio,format=qcow2

此时我可以通过 VNC 进入虚拟机并继续安装,直到进入“使用 DHCP 配置网络”阶段,此时它会超时并且永远无法获取 IP。

如果我使用默认的 NAT 接口,它可以正常工作,获取 192.168.112.xxx 范围内的 IP,并且可以访问本地网络和更大的互联网,没有问题。如果我随后更改此工作虚拟机上的 virsh 配置以桥接至 br0,那么我无法与任何网络(本地或其他网络)通信。DHCP 无法获取 IP,而设置静态 IP 不会产生外部流量。

为了帮助起见,当安装程序运行时,我打开了另一个终端并从主机获取了更多信息:

~$ brctl show
bridge name     bridge id               STP enabled     interfaces
br0             8000.d05099c025fb       no              enp2s0
                                                        vnet0
docker0         8000.0242dc4f969e       no              veth009cb0a
virbr0          8000.5254001a5665       yes             virbr0-nic

~$ brctl showmacs br0
port no mac addr                is local?       ageing timer
  1     00:04:20:eb:7e:96       no                 3.90
  1     00:11:32:63:9c:cf       no                 1.86
  1     30:46:9a:0f:81:cd       no                 3.39
  1     44:8a:5b:9e:d1:90       no                 0.00
  1     88:de:a9:13:86:48       no                 0.29
  1     b8:ae:ed:73:3e:ca       no                 3.89
  1     d0:50:99:c0:25:fb       yes                0.00
  1     d0:50:99:c0:25:fb       yes                0.00
  1     d0:50:99:e0:21:46       no                 2.90
  1     f0:f6:1c:e3:7f:be       no               173.56
  2     fe:54:00:6f:b8:64       yes                0.00
  2     fe:54:00:6f:b8:64       yes                0.00

~$ ip route
default via 192.168.113.1 dev br0
172.17.0.0/16 dev docker0  proto kernel  scope link  src 172.17.0.1
192.168.113.0/24 dev br0  proto kernel  scope link  src 192.168.113.2
192.168.122.0/24 dev virbr0  proto kernel  scope link  src 192.168.122.1 linkdown

如果有人需要的话,我愿意把整个 dumpxml 放到这里,但是这里只是网络部分:

<interface type='bridge'>
  <mac address='52:54:00:6f:b8:64'/>
  <source bridge='br0'/>
  <model type='virtio'/>
  <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</interface>

更新日期 2017-03-25:通过更改以下内容:

    <interface type='network'>
      <mac address='52:54:00:6f:b8:64'/>
      <source network='default'/>
      <model type='virtio'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
    </interface>

然后 NAT 工作了,我得到了 192.168.122.xxx IP,并且可以与外部服务通信等。那么...我的主机 br0 有问题吗?如果是这样,那么为什么主机可以正常获取 IP?是否有一些以太网设备不支持桥接?以下是主机上 lspci 的结果:

~$ lspci | grep Ethernet
02:00.0 Ethernet controller: Intel Corporation I210 Gigabit Network Connection (rev 03)
03:00.0 Ethernet controller: Intel Corporation I210 Gigabit Network Connection (rev 03)

我根本没有设置第二个以太网控制器,也许我会设置它并尝试桥接它。

更新日期 2017-03-25 b:第二个接口似乎没有改变结果。以下是结果 /etc/network/interfaces:

source /etc/network/interfaces.d/*

auto lo
iface lo inet loopback

auto enp2s0
iface enp2s0 inet dhcp

auto enp3s0
iface enp3s0 inet manual

auto br0
iface br0 inet dhcp
  bridge_ports enp3s0
  bridge_stp off
  bridge_fd 0
  bridge_maxwait 0

当我 时ip a,结果是:

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
      valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
      valid_lft forever preferred_lft forever
2: enp2s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether d0:50:99:c0:25:fb brd ff:ff:ff:ff:ff:ff
    inet 192.168.113.2/24 brd 192.168.113.255 scope global enp2s0
      valid_lft forever preferred_lft forever
    inet6 fe80::d250:99ff:fec0:25fb/64 scope link
      valid_lft forever preferred_lft forever
3: enp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq master br0 state UP group default qlen 1000
    link/ether d0:50:99:c0:25:fa brd ff:ff:ff:ff:ff:ff
    inet6 fe80::d250:99ff:fec0:25fa/64 scope link
      valid_lft forever preferred_lft forever
4: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether d0:50:99:c0:25:fa brd ff:ff:ff:ff:ff:ff
    inet 192.168.113.100/24 brd 192.168.113.255 scope global br0
      valid_lft forever preferred_lft forever
    inet6 fe80::d250:99ff:fec0:25fa/64 scope link
      valid_lft forever preferred_lft forever
5: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
    link/ether 52:54:00:1a:56:65 brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
      valid_lft forever preferred_lft forever
6: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast master virbr0 state DOWN group default qlen 1000
    link/ether 52:54:00:1a:56:65 brd ff:ff:ff:ff:ff:ff
7: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:18:2c:73:bb brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 scope global docker0
      valid_lft forever preferred_lft forever
    inet6 fe80::42:18ff:fe2c:73bb/64 scope link
      valid_lft forever preferred_lft forever
9: vethaa3cd40@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
    link/ether ae:05:f7:1b:f9:9e brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::ac05:f7ff:fe1b:f99e/64 scope link
      valid_lft forever preferred_lft forever
10: vnet0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master br0 state UNKNOWN group default qlen 1000
    link/ether fe:54:00:3a:54:b3 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::fc54:ff:fe3a:54b3/64 scope link
      valid_lft forever preferred_lft forever

当被告知使用 br0 时,VM 继续存在完全相同的问题。

答案1

解决了。

问题出在 br_netfilter 模块的默认设置上,该模块将桥接数据包发送到 iptables。libvirt 文档确实提到了这一点他们的社交页面,但我所关注的大多数教程都没有涉及它。

由于某种原因,iptables 吃了这些数据包(可能是 docker 添加的什么东西?),但无论如何,将这些数据包发送到 iptables 显然效率低下,所以解决方法是通过更改这些设置来绕过 iptables。

请注意,我在此处概述的方法实际上是sysctl.d 手册页

创建一个/etc/udev/rules.d/99-bridge.rules,并插入以下行:

ACTION=="add", SUBSYSTEM=="module", KERNEL=="br_netfilter", RUN+="/lib/systemd/systemd-sysctl --prefix=/net/bridge"

然后,创建一个/etc/sysctl.d/bridge.conf,并插入以下三行:

net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
net.bridge.bridge-nf-call-arptables = 0

然后我只需要恢复到我原来的桥接设置,其中包括/etc/network/interfaces如下所示的内容:

source /etc/network/interfaces.d/*
auto lo
iface lo inet loopback
auto enp2s0
iface enp2s0 inet manual
auto br0
iface br0 inet dhcp
  bridge_ports enp2s0
  bridge_stp off
  bridge_fd 0
  bridge_maxwait 0

virsh 网络接口定义如下:

<interface type='bridge'>
  <mac address='52:54:00:37:e1:55'/>
  <source bridge='br0'/>
  <model type='virtio'/>
  <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</interface>

一切就绪后,我的虚拟机启动了,它获得了一个 IP,我就可以自由地与我的主机、本地网络和互联网通信。

答案2

我让它工作了,使用直接而不是桥接:

使用这个/etc/network/interfaces

source /etc/network/interfaces.d/*
auto lo
iface lo inet loopback
auto enp2s0
iface enp2s0 inet dhcp
auto enp3s0
iface enp3s0 inet manual

这是 virsh 设置:

<interface type='direct'>
  <mac address='52:54:00:37:e1:55'/>
  <source dev='enp3s0' mode='vepa'/>
  <target dev='macvtap0'/>
  <model type='rtl8139'/>
  <alias name='net0'/>
  <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</interface>

我的本地网络看到 52:54:00:37:e1:55 mac 地址,DHCP 服务器为其分配一个 IP,我可以通过该 IP ssh 进入这台机器,所以一切似乎都很好。我可以同时运行第二台虚拟机,它的 MAC 也获得一个 IP,所以我似乎找到了我想要的解决方案。

也许下次我会尝试在原始以太网端口上执行所有这些操作。我也很好奇桥接到底是什么,以及它解决了直接无法解决的问题,如果有人读到这篇文章,可以回答的话。谢谢!

更新:此解决方案的问题是,共享单个物理接口的任何虚拟机都无法相互通信。当主机和虚拟机共享同一个物理接口时,主机也会出现同样的问题。

我怀疑这正是桥接应该解决的问题,但我确实需要一些有这方面经验的人的指导。

答案3

我花了一周时间研究这个问题,结果发现是 Docker。Docker 运行时会将运行时规则放入 netfilter 中,目的是隔离 Docker 容器。但这样做的副作用是阻止了到桥接虚拟机的流量。

我通过 systemctl 停止并禁用 Docker,重新启动主机系统,一切正常。

我想我会在桥接虚拟机中安装 Docker,在那里它可以独自发挥作用。

相关内容