最近在公司的一台生产服务器上遇到个kvm虚拟化服务的网络故障。故障的主要现象就是虚机网络通信异常,丢包严重。
系统和软件包版本信息是:

  • 3.10.0-1160.66.1.el7.x86_64
  • libvirt-4.5.0-36.el7_9.5.x86_64
  • bridge-utils-1.5-9.el7.x86_64

从抓包结果来看,在从虚拟化的宿主物理机上ping虚机时,可以看到一个很有意思的现象:当虚机网络开始丢包时,那些被弄丢的数据包实际上是发给了物理机的网卡。

我们的网络桥接配置结果如下所示:

# brctl show
bridge name	bridge id		STP enabled	interfaces
br0		8000.64EB943F6FEF	yes		em1
							vnet0

物理机网卡em1和虚机对应的tap网卡vnet0同时接入到了网桥br0中。网桥br0相当于是物理机本地的一个软件交换机,处理虚拟化网络的数据包通信转发工作。

物理机系统日志中的报错
在虚机网络丢包的同时,我们可以从物理机/var/log/message日志中看到如下报错信息:

Jun  3 21:03:17 testserver kernel: br0: received packet on em1 with own address as source address (addr:64:EB:94:3F:6F:EF, vlan:0)
Jun  3 21:04:15 testserver  kernel: br0: received packet on em1 with own address as source address (addr:64:EB:94:3F:6F:EF, vlan:0)
Jun  3 21:04:15 testserver  kernel: br0: received packet on em1 with own address as source address (addr:64:EB:94:3F:6F:EF, vlan:0)
Jun  3 21:06:35 testserver  kernel: br0: received packet on em1 with own address as source address (addr:64:EB:94:3F:6F:EF, vlan:0)
Jun  3 21:06:35 testserver  kernel: br0: received packet on em1 with own address as source address (addr:64:EB:94:3F:6F:EF, vlan:0)
Jun  3 21:07:24 testserver  kernel: br0: received packet on em1 with own address as source address (addr:64:EB:94:3F:6F:EF, vlan:0)

大概意思就是说,物理机的em1网卡收到了自己发出去的数据包,虽然目的地址不是自己。这些数据包直接就被丢弃了。那么,本该发到虚机网卡vnet0的数据包,为什么会直接回到了物理机的发包网卡em1了呢?
我们可以使用tcpdump抓包再观察下看:

tcpdump -i vnet0 icmp
tcpdump -i em1 icmp

在物理机上打开两个窗口,分别执行如上的抓包命令,同时打开第三个窗口从物理机上执行ping虚机ip地址的命令。
观察一会儿就发现这样的规律:正确的结果应该是所有数据包都在vnet0网口上能够看到。实际的结果是vnet0上只出现了一部分,所有丢失的数据包都出现在了em1网口上。
由于这个虚机就是在该物理机上运行的,虚机与该物理机的网络通信应该全部经由br0网桥转发处理,也就是说收包网卡应该是vnet0,不该出现em1。

查看网桥br0的mac/port映射表

# brctl showmacs br0
port no	mac addr		is local?	ageing timer
  1 52:54:00:B3:89:2A	yes		   18.00
  1	64:EB:94:3F:6F:EF	yes		   0.00
  1	64:EB:94:3F:6F:EF	yes		   0.00
  2	fe:54:00:B3:89:2A	yes		   0.00
  2	fe:54:00:B3:89:2A	yes		   0.00

br0网桥同样具备基础的网络交换机功能,通过查看br0网桥的mac/port映射关系表,我们发现有一条映射记录是错误的!
上面的结果中,虚机网卡的mac地址是52:54:00:B3:89:2A,vnet0的mac地址是fe:54:00:B3:89:2A,二者间的联系从mac地址上可以直观看出来。

这里存在的错误就是:网桥br0上,port 1是物理网卡em1占用的,port 2则是vnet0设备占用的。那么,作为该物理机上运行的一个虚机,其虚机网卡的mac地址,在正确的情况下应该是映射到br0网桥的port 2端口上。

经过反复的对比观察,我们还看到这样的现象是:当虚机网络不丢包时,查看br0网桥的mac/port映射表就是正确的结果;当虚机开始丢包时,mac/port表中就出现的是错误的记录!

那这是什么造成的呢?我也没找到根源原因。
我做了以下尝试:

  • 升级bridge-utils软件包版本,https://mirrors.edge.kernel.org/pub/linux/utils/net/bridge-utils/bridge-utils-1.7.1.tar.xz
  • 重置网桥配置
  • 变换虚机网卡
  • 创建新的虚机
  • 从bridge-utils源码中找线索

以上尝试,都没能帮助找到根源原因。

临时性处理方法
禁用网桥br0的本地mac/port映射关系学习功能,既然总是出现错误的学习记录,那不如没有。禁用br0网桥的该功能后,实际上是对br0的功能做了一个降级处理,原本在br0本地可以处理和维护的一份缓存数据,交给上一层的网络交换机去处理了。究竟这一操作会对虚机网络性能产生多大的影响?预计影响有限,后续在实际应用中再做进一步观察。

brctl setageing br0 0

在禁用了mac/port learning功能后,查看mac/port table的记录变成下面的样子:

# brctl showmacs br0
port no	mac addr		is local?	ageing timer
  1	64:EB:94:3F:6F:EF	yes		   0.00
  1	64:EB:94:3F:6F:EF	yes		   0.00
  2	fe:54:00:B3:89:2A	yes		   0.00
  2	fe:54:00:B3:89:2A	yes		   0.00

在记录表中只会出现br0网桥“物理”接入的设备的记录了。

此时,再测试虚机网络的通信功能,全部恢复正常了。