- 虚拟化的IO路径
VNF由于虚拟化层的引入导致I/O性能下降非常大,在没有优化的情况下,运行在虚拟机中的VNF性能只有在物理机上的大概1/4。
左图是基于KVM+Qemu虚拟化下的总体架构,运行在内核的KVM模块负责CPU和内存的虚拟化,外设由用户态的Qemu模拟,包括虚拟网卡。右图展示了网络部分的基本架构,VM的虚拟网卡被连接到host的桥接模块中,类似于VM通过一个根网线接到了一个台交换机上,通过host桥接模块实现了VM和外部网络、VM和VM、VM和host之间的通信。我们将VM的报文传输路径分按以下两种场景来分析:
- VM和外部网络通信
- VM和内部网络通信,内部网络包括VM和host
2. VM到物理网卡的IO优化
上图描述了VM到外部网络经过的路径,从图中可以看出,优化VM到网卡的性能有三个点:虚拟网卡、虚拟化层和内核网桥。
2.1 虚拟网卡的优化技术:
虚拟网卡分为全虚拟化网卡和半虚拟化网卡。所谓的全虚拟化即VM感知不到网卡是由hypervisor模拟的,VM认为是一个在真实世界中存在的网卡(比如Intel的e1000网卡),也会加载这个网卡对应的驱动。这种方式的好处在于,操作系统并不需要修改就可以在虚拟机中运行,特别是一些老版本操作系统,很可能已经无人维护。这在虚拟化推广的早期是一个非常大的亮点,一些很古老的业务系统可能需要在一些特殊的操作系统上运行,而这些操作系统支持的硬件可能已经买不到,虚拟化技术很好的解决了这个问题。
在KVM-Qemu虚拟化环境下,全虚拟化网卡由Qemu负责模拟,Guest OS对全虚拟化网卡的任何操作都需要host截获,并模拟真实网卡的行为,因此性能非常低。在虚拟化技术被大规模应用后,针对IO的半虚拟化技术很快就被提出来并得到大规模应用。IO的半虚拟化技术是指在IO驱动层面是知道自己运行在虚拟化环境中,和Host之间需要按照一定约定进行通信。下图是全虚拟化和半虚拟化的区别。
Linux的IO半虚拟化技术是virtio,virtio是一整套半虚拟化解决方案,其中针对的网络的是virtio-net,即virtio网卡。virtio分为前端(front-end)和后端(back-end),前端运行在Guest OS中,即virtio-net驱动。因此针对虚拟网卡的优化,目前的主流方案就是使用virtio-net网卡。VM中运行是virtio-net的前端,这部分是确定,后端的实现有几种不同的方式,这部分我们归类为虚拟化层优化技术。
2.2 虚拟层优化技术:
virtio-net的后端运行在host上,有三种主要的方式:传统方式、vhost-net、vhost-user。
- 传统方式:Qemu实现back-end,还是首先经过host的内核,由内核将vm的请求转交给Qemu。Qemu再通过tap接口将报文发送到Host的内核协议栈。流程: VM--> Kernel' kvm-->user's Qemu-->kernel's协议栈。这个过程有两次用户态和内核态的切换。
- vhost-net:back-end在内核中实现,KVM虚拟化的基本思路是由内核完成最重要的CPU和内存的虚拟化,外设的虚拟化由用户态的Qemu去完成。vhost-net解决方案为了提高性能,打破了这个基本原则,将网卡的back-end实现在了内核中,命名为vhost-net,由vhost-net在内核中直接操作tap设备。流程:vm-->kernel's kvm-->kernel's协议栈。这个流程只有一次用户态和内核态的切换。
- vhost-user:Qemu实现的一种新的back-end技术,利用共享内存机制,在用户态直接和VM通信,不再陷入内核,减少了报文在用户态和内核态的拷贝动作。更重要的是,由于Qemu在用户态得到了数据,vhost-user可以和其它的用户态优化技术(如:dpdk)配合使用,进一步提升性能。流程:vm-->user's qemu-->kernel'协议栈,有一次用户态和内核态的切换。vhost-user的另一个有优点是保持了KVM虚拟化的原则,外设由用户态的程序去模拟,kernel只负责最重要的CPU和内存的虚拟化。
vhost-net和vhost-user的结构对比:
- ivshmem,直接在vm和vm之间,vm和host之间共享内存,这是Qemu提供的能力。对VM来说,Qemu将共享内存以PCI设备的方式加入到vm中,vm可以通过PCI的memory空间操作内存。严格说来这不是针对网卡性能的优化技术,这只是一种基础机制,但可以在这种机制之上构建报文交换的功能,不再局限于是不是虚拟网卡。(DPDK在老版本中,有一些针对ivshmem的api,但新版本已经将ivshmem删除,估计是共享的方式造成了系统紧耦合,对系统的灵活性和扩展性造成了障碍)
3 内核网桥的虚拟化技术:
3.1 软件优化技术--macvtap
VM发出的报文通过Host的桥接模块转发,Linux的网桥模块是协议栈的一部分,协议栈的公共处理流程是必须过的,这对于VM报文收发来说过于厚重,对于和外部通信的场景,最简单的优化是不经过内核网桥,解决好多个vm共享物理网卡的问题就行了。对于VM发包来说,多个VM共享物理网卡比较简单,解决好资源的共享操作。VM收包需要一个简单的分流,一般每个VM的虚拟网口都有一个独立的MAC地址,需要一个根据MAC地址进行分流的软件模块,内核中的macvtap模块实现了这个功能。
3.2 硬件优化技术--SR-IOV
最高性能的方式是完全不绕过host,VM直接和硬件打交道。将硬件分配给VM,由VM的驱动控制硬件,硬件dma使用VM的虚拟物理地址。但一台设备的硬件资源毕竟有限,而虚拟机数量是可以按需分配的,为了解决硬件资源有限的问题,PCI-SGI制定了SR-IOV规范,即PCIE设备的虚拟化方案。支持SR-IOV的PCI设备可以动态划分出多个功能子单元,每个子单元都具备完整的PCIE寄存器。SR-IOV的设备分为PF和VF两个功能单元,其中PF固定只有一个,VF可以动态生成多个。PF作为整个PCI设备的管理单元,一般由host的驱动控制,host通过PF生成多个VF,每个VF有PCIE配置寄存器、网卡的功能寄存器、报文收发队列。VF被虚拟化平台添加到VM中,VM加载网卡的VF驱动就可以直接操作硬件。这种方式即解决了硬件设备共享的问题,也绕过了host软件处理的环节,实现了接近物理机运行的性能。但这种方式也有几个关键问题需要解决:
- 硬件DMA可以直接访问内存,因此vm可以通过给dma任意地址来访问整个系统的内存,进而破坏虚拟机的隔离性。另外,VM只有虚拟物理地址,没有真实的物理地址,是不能直接给硬件用于dma的。问题的解决办法是引入总线的IO虚拟化技术(Intel叫vx-d),在外设和内部总线之间增加一个IOMMU单元,对dma操作的地址进行映射(vm使用的是虚拟物理地址,需要翻译成物理地址)和权限控制。每个dma操作都会附带一个所属vm的id(由host配置的,vm无法修改),IOMMU单元维护了每个vm所能访问的地址范围,如果dma超出了所属VM的地址范围,访问被拒绝。dma地址在合法范围内,IOMMU会将地址翻译成物理地址,然后送到总线上。
- mac收到的报文需要确定分发给哪个VF,这需要mac具备一定的分流能力,或者具备多mac接收能力,将不同的mac地址的报文分发给对应的vf。这些分流功能在支持多队列的网卡中几乎都具备。
- vm的收发流程不再有host介入,因此vm之间的数据交互就只能靠外部交换机来完成。目前各个网卡厂商都在将交换机功能引入到网卡中,以实现通过网卡完成vm间的数据交换。但是这种应用目前还不是主流,未来是否会成为主流也不确定。
4 VM间的报文交换优化
VM间报文交换的最高性能是SR-IOV技术加硬件交换机。但是如果不希望增加额外的硬件交换机,那么SR-IOV技术也用不上,只能由host来完成交换。最简单的方式是使用host内核的桥接模块完成:
报文从vm拷贝到内核的桥接模块,然后由内核的桥接模块完成转发,再从内核拷贝到vm中。这个过程有两次报文拷贝,且内核协议栈效率也不高。在NFV技术栈中,通常使用OVS来替代内核桥接模块,OVS也是运行在内核态,性能其实弱于桥接模块,采用OVS是因为其功能强大,可扩展性强。因此针对软件交换机的优化主要是优化OVS的性能。
4.1 OVS数据面移植到用户态
OVS数据面移植到用户态是为了和DPDK结合,将DPDK作为OVS数据面的优化技术。
- OVS数据面采用DPDK的运行方式:使用hugepage页、独占并绑定核、查询方式收发报文。注意,这就需要OVS的数据面独占部分CPU核。
- dpdk和qemu的vhost-net结合,实现dpdk和vm之间以共享内存方式传递报文,避免内存拷贝带来的性能损耗。
采用OVS+DPDK+vhost-net可以实现VM之间基于共享内存转发数据,理论上是软件能实现的最高性能。
- VM内的转发加速
VM的软件是客户自己编写的,因此无论采用哪种网卡,都可以实现用户态收发包,目前最高性能的虚拟网卡是virtio-net,物理网卡是VF方式。考虑到virtio-net作为一种纯软件技术相对复杂,且dpdk对virtio-net和VF都支持,建议在VM内,数据面采用dpdk作为驱动收发包技术。