这片文章里我们将会配置一个环境,然后在虚拟机里运行一个基于DPDK应用。我们将介绍所有用来在host系统上配置一个虚拟交换机需要的步骤,并通过这个虚拟交换机连接到虚拟机的应用。正文包括描述如何创建,安装和运行虚拟机,以及安装里面的应用。你将会学习到如何创建并设置一个简单的通过guest内的应用发送并接收网络数据包到host的虚拟交换机。基于这些设置,你将会学习到如何如何调整设置来获得最优的吞吐性能。
Setting up
对于乐意使用DPDK但不希望配置和安装相关软件的,我们提供了一个ansible playbooks在github的repo里,自动化了所有步骤,我们就基于这个配置开始吧。
Requirements:
- 一台运行了Linux发行版的电脑。本文使用Centos 7,不过不同的Linux发行版之间命令的差别也不会特别大,特别是Red Hat Enterprise Linux 7
- 一个有sudo权限的用户
- home目录下有大于25GB的空闲空间
- 至少8GB的RAM
首先我们先安装我们需要的包
1 | sudo yum install qemu-kvm libvirt-daemon-qemu libvirt-daemon-kvm libvirt virt-install libguestfs-tools-c kernel-tools dpdk dpdk-tools |
Creating a VM
首先从下面的网站下载一个最新的Centos-Cloud-Base镜像
1 | sudo wget -O /var/lib/libvirt/images/CentOS-7-x86_64-GenericCloud.qcow2 http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud.qcow2 |
这个下载的是一个预安装的Centos7,用来在OpenStack环境运行的。因为我们不使用OpenStack,所以我们需要清理一下这个虚拟机。不过首先我们需要做一个镜像的copy。以此保证我们之后能重复使用这个镜像。
1 | sudo qemu-img create -f qcow2 -b /var/lib/libvirt/images/CentOS-7-x86_64-GenericCloud.qcow2 /var/lib/libvirt/images/vhuser-test1.qcow2 20G |
通过下面的配置我们可以允许非特权用户使用libvirt命令(推荐):
1 | export LIBVIRT_DEFAULT_URI="qemu:///system" |
然后使用清理命令:
1 | sudo virt-sysprep --root-password password:changeme --uninstall cloud-init --selinux-relabel -a /var/lib/libvirt/images/vhuser-test1.qcow2 --network --install "dpdk,dpdk-tools,pciutils" |
这个命令回挂载文件系统并自动应用一些基础配置,然后这个镜像就可以用来启动虚拟机了
我们需要一个网络来连接我们的虚拟机,Libvirt处理网络的方式类似管理虚拟机,你可以通过XML文件定义网络并且通过命令行控制它的启动和停止。
举个例子,我们将使用一个叫做’default’的网络libvirt自带的方便网络。用下面的命令定义’default’网络,启动并检查网络运行状态
1 | [root@10-0-117-158 ~]# virsh net-define /usr/share/libvirt/networks/default.xml |
最后我们使用virt-install来创建虚拟机。这个命令行工具包含了一系列常用的操作系统配置定义。然后我们可以基于这个基本定义做一些改动:
1 | virt-install --import --name vhuser-test1 --ram=4096 --vcpus=3 \ |
这些参数分别制定了。vCPUs的数量,RAM的大小,磁盘的路径,以及虚拟机要连接的网络。
出了通过我们指定的这些参数定义VM之外,virt-install会把虚拟机同时创建出来,所以我们应该可以看到:
1 | [root@10-0-117-158 ~]# virsh list |
很好,虚拟机已经运行了。接下来我们先把虚拟机停下来然后做一些额外的配置
1 | virsh shutdown vhuser-test1 |
Preparing the host
DPDK对内存缓存的分配和管理做了优化。在Linux上这个需要使用hugepage的支持,所以必须要在kernel上打开。使用的page大小通常需要大于4K,以此通过使用更少的page数量,以及更少的TLB来提升性能。在翻译虚拟地址到物理地址的时候会产生这些查询。为了在启动时分配hugepage,我们需要在bootloader配置里加上kernel参数
1 | sudo grubby --args="default_hugepagesz=1G hugepagesz=1G hugepages=6 iommu=pt intel_iommu=on" --update-kernel /boot/vmlinuz-3.10.0-957.27.2.el7.x86_64 |
当然我们来解释一下这些参数做了什么:
default_hugepagesz=1G
默认创建出来的hugepages默认是1GB
hugepagesz=1G
启动过程中创建出来的hugepage大小也是1GB
hugepages=6
最开始启动的时候创建6个大小为1GB的hugepage,这个在重启之后可以在/proc/meminfo里看到
注意,补充说明hugepages的设置增加了两个IOMMU相关的参数 iommu=pt intel_iommu=on
这个会初始化Intel VT-d以及IOMMU Pass-Through模式,在Linux用户态处理IO的时候需要用到他们。因此我们修改了kernel参数,现在刚好可以做一下重启。
等到重启完成之后,我们可以通过命令行查看对应的参数已经生效了
1 | [root@10-0-117-158 ~]# cat /proc/cmdline |
Prepare the guest
virt-install命令通过libvirt创建并启动了一个虚拟机。为了将基于DPDK的vswitch TestPMD连接到QEMU,我们需要增加如下的定义到XML的device部分:
1 | virsh edit vhuser-test1 |
在<device>
部分增加
1 | <interface type='vhostuser'> |
另一个和vhost-net不同的guest配置就是hugepages。因此我们需要给guest增加如下定义:
1 | <memoryBacking> |
这样就有内存了,然后再修改guest里的配置,这是非常重要的配置,没有的话就没办法收发数据包了:
1 | <cpu mode='host-passthrough' check='none'> |
然后我们需要启动我们的guest。因为我们配置了让虚拟机连接到vhost-user的UNIX sockets,因此我们需要确保guest启动的时候这些sockets时可用的。这是通过启动testpmd实现的,这个操作会创建我们需要的sockets。
1 | sudo testpmd -l 0,2,3,4,5 --socket-mem=1024 -n 4 \ |
最后,这个实验需要连接到vhost-user unix sockets,因此启动QEMU的时候需要用root。所以在/etc/libvirt/qemu.conf
中设置 user=root
。 这是因为我们呢的特殊验证场景需要这样配置,生产环境通常不建议这样配置。实际上读者需要在本文演示结束之后把 user=root
这个配置去掉。
现在我们可以通过命令启动虚拟机了:
1 | virsh start vhuser-test1. |
通过root登陆之后,我们要做的第一件事就是绑定virtio设备到vfio-pci驱动。为了能够完成这个操作,我们需要加载一些内核模块
1 | [root@localhost ~]# modprobe vfio enable_unsafe_noiommu_mode=1 |
然后找出virtio-net设备的PCI地址:
1 | [root@localhost ~]# dpdk-devbind --status net |
在dpdk-devbind的输出中找到virtio-devices的部分,并且没有被标记Active状态的。我们可以用这些设备来进行实验。注意:地址可能是不一样的。当我们首次启动这些设备的时候将会自动绑定到virtio-pci驱动,因为我们需要和非kernel的驱动一起使用,首先就是要将这些设备和virtio-pci设备解绑,然后再绑定到vfio-pci驱动
1 | [root@localhost ~]# dpdk-devbind -b vfio-pci 0000:00:08.0 0000:00:09.0 |
Generating traffic
我们已经安装并配置好所有东西了,接下来就是运行网络负载了。首先在host上我们需要启动testpmd实例作为虚拟交换机。然后设置它转发所有在net_vhost0收到的数据包到net_vhost1。testpmd需要在虚拟机启动之前启动,因为它会尝试连接到由QEMU创建的属于vhost-user设备初始化出来的unix sockets。
1 | testpmd -l 0,2,3,4,5 --socket-mem=1024 -n 4 \ |
然后我们来启动之前准备好的虚拟机:
1 | virsh start vhuser-test1 |
注意这时候我们能够在testpmd看到vhost-user收到的数据包了:
1 | Port 1: link state change event |
当guest启动之后我们就能够启动testpmd了。testpmd会初始化端口以及DPDK实现的virtio-net驱动。另外还有virtio特性的协商以及其他一些通用功能的协商也都在这一步发生了。
在我们启动testpmd之前,需要确认vfio内核模块已经加载并绑定了virtio-net设备到vfio-pci驱动:
1 | dpdk-devbind -b vfio-pci 0000:00:08.0 0000:00:09.0 |
然后可以启动testpmd:
1 | testpmd -l 0,1,2 --socket-mem 1024 -n 4 \ |
现在我们可以检查testpmd处理了多少数据包了,我们可以通过输入命令 show port stats all
来看对应(RX/TX)方向的信息,比如:
1 | testpmd> show port stats all |
testpmd有不同的转发模式,这个例子里面我们用的是macswap,此模式会交换目标和源头的mac地址。另外的转发模式,比如’io’则不会处理包,所以会给出更高深职很不现实的数据。另外一个转发模式就是’noisy’,可以模拟调整包的缓存/内存的查找。