概要
KVM网络优化的文章有很多,写得也都非常好,包括virtio、vhost、macvtap、vepa、SRIOV 网卡等软、硬件优化技术。但是都没给出具体数据,比如千兆网卡virtio的发包能力能达到多少,又比如SRIOV能达到物理网卡性能的80%或者90%?这些都没有数据能直观体现。本文将通过压测直观的给出各项技术在延时、吞吐量、发包率的表现,并跟宿主机进行对比。
KVM网络优化
下图是几种网络优化方案的总结
本文将选取两种最常用的优化方案进行对比:
virtio+vhost
SRIOV
第一种方案根据所使用桥设备的不同,又分为Linux Bridge和Openvswitch两种,即三种方案加上宿主机本身四个对比进行比较。
本测试所使用的宿主机、虚拟机配置如下:
宿主机配置
服务器型号:DELL R720
CPU型号:Intel(R) Xeon E5-2620 v2 2.10GHz*2,每个CPU有12核,共24核
内存大小:48GB
硬盘大小:sda (scsi0): 299GB sdb: 898GB
网卡:Intel I350 Gigabit Ethernet * 4 (千兆网卡,8个队列)
虚拟机配置
vcpu:6
内存大小:8GB
硬盘大小:sda : 50GB sdb: 100GB
宿主机网卡发包率压测
本文首先对宿主机物理网卡进行压测,排除物理网卡本身性能问题造成对测试数据的影响。 网卡关键性能指标主要有:延时、吞吐量、发包率,其中,发包率测试最为麻烦,因为会受到其他因素如CPU、网卡中断等的影响。本文首先对网卡发包率进行压测,测试宿主机网卡发包率能否达到千兆网卡的理论值。 下图为千兆小报文理论瓶颈值
可以看出千兆网卡的发包率理论值为1.4M(跟payload有关)。
由于要测试的是网卡的发包率,唯一途径就是尽可能多地发包,同时为了减少其他开销,本文使用UDP进行发包率压测,而不是TCP。更准确的说是使用18 bytes payload的UDP,即网络层的64 bytes UDP包进行压测。
注:由于使用UDP包进行测试,最终测试结果跟上图的理论值会有所差别,主要是payload的差异。
本文使用4321端口作为UDP通讯端口,为确保流量不被iptables影响,我们将其关闭。
bash /etc/init.d/iptables stop
其中
接收端ip为:10.3.150.19、10.3.150.20、10.3.150.21(为什么要用三个后面会说明) 发送端ip为:10.3.150.248
下面是发送端和接收端的伪代码
发送端:
python fd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) fd.connect(("10.3.150.19", 4321)) while True: fd.sendmmsg(["\x00" * 18] * 1024)
接收端:
python fd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) fd.bind(("0.0.0.0", 4321)) while True: fd.recvmmsg()
测试结果
350k的pps,看起来还不错,但是离理论值1.4M还很远。为了接收端接收更多的包,必须发送更多的包,下面开启两个线程独立发包:
结果并没有明显的提升,通过ethtool -S和netstat -i查看数据包的接收情况
通过上面分析,发现只有6号队列在接收数据包,成功地接收了370k的数据包,有200k的数据包被丢弃或者忽略。 物理网卡已经开启了多队列,也可以看到总共有8个队列,为什么只有一个队列在接收数据包呢? 查询资料之后发现用于测试的这款intel网卡,多队列哈希算法只支持对源IP、目的IP进行哈希,由于测试用的源IP、目的IP都只有一个,所有无论发送端发送多少数据包,都只有一个队列在接收。 原因找到了,问题就容易解决了,有两种方法来分散数据包到多个队列: 1. 多个接收端IP 2. 多个发送端IP或者多台发送测试机
多个接收IP
shell sender# taskset -c 1,2 ./sender 10.3.150.19:4321 10.3.150.20:4321
使用ethtool查看各个队列接收情况
接收端的情况
这回并没有数据被丢弃了,当用两个队列接收数据包时,pps可以达到800k。 当然,如果再增加一个队列就可以达到更高的pps,但是很快就会有另一个瓶颈产生。这个时候表现为drop和over的包基本没有,但是netstat查看,发现receive errors却一直在增加
可以看到,网卡把所有数据包都发送给了内核,但是内核却没能把所有数据包发送给receiver程序。内核只能将700kpps的数据包发送成功,剩下的720kpps和16kpps被丢弃了,原因在于receiver程序处理不过来。
注:其实这里网卡的pps能力已经达到理论值1.4M(700k+700k),netstat -i |grep eth1也可以体现。下面的讨论只是为了验证单个程序能够处理内核发送过来数据包的极限。
多线程接收端
既然receiver处理不了内核发送过来的数据包,自然想到启用多线程,让更多的receiver线程处理数据包,但测试结果多线程却很不理想
和单线程的receiver比,多线程的接收能力反而有所下降,而且不稳定。原因就在于,UDP接收缓存上有锁的争抢。两个线程同时使用一个socket文件描述符时,大部分消耗都在争抢缓存锁上,这也可以从不稳定的接收能力反应出来。
socket端口复用
端口复用技术允许多个进程绑定在同一个端口上,端口的负载会在这些进程间均衡处理。端口复用需要打开SO_REUSEPORT标记,这样每个进程都会有自己的socket文件描述符,也就不会产生争抢缓存锁。
现在,程序数据包的处理能力终于达到1Mpps。
注:这里只是说明了程序的pps能力,跟网卡的无关。
发包率对比
本文花大篇幅证明了测试所使用的物理网卡pps可以达到理论值1.4M,目的是为无差别的对比Bridge、OVS和SRIOV的pps能力。 本文除了对比不同优化,pps的差异,同时也比较了包的大小对pps的影响。 包大小的影响用了两种包:64 bytes(小包),1518 bytes(大包),下面给出测试的结果。
结果可知: - 大包情况下,VM跟HOST的pps接收能力一致,都只能达到80kpps,原因是吞吐量已达到千兆网卡瓶颈 - 小包情况下,Linux原生网桥(Bridge)比ovs性能更优 - SRIOV小包pps能力能达到物理网卡的~60%
吞吐量
测试大包pps时,我们就知道无论哪种优化方案,VM的吞吐量都能达到物理网卡的吞吐
延时
测试延时用的工具是netperf,用的是request-response模式进行测试,并同时对TCP、UDP进行对比测试
结果表明: - TCP和UDP的延时非常接近 - Bridge延时比HOST几乎增加了100%,多了50µs 左右,ovs稍微好点,但也增加了近40µs - 由于SRIOV是直接将网卡接入到VM,所以在Bridge、ovs、SRIOV三者中延时是最低的
后记
本文测试pps时,为了更多更快的接收数据包,用的是UDP包进行测试,接收的进程对数据包不做任何处理,只对数据包进行统计,这样应用程序才能达到1Mpps的接收能力。在实际场景中,为了保证连接可靠性,使用的都是TCP,即使是使用UDP,也有其他逻辑需要处理,想达到1M的接收能力几乎是不可能的。
对比Bridge、ovs、SRIOV三者,综合性能最优的是SRIOV,但SRIOV有自身的局限性,比如需要特定型号网卡支持、千兆网卡只支持7个VF等;
Bridge在小包pps上比ovs更优,但ovs时延更低,就综合性能而言,还是推荐使用Bridge,相比时延,应用程序更多的是希望能够处理更多的包;
如果追求的是网络管理的便捷性,ovs当然就是首选。