三、vhost-net--------动手使用vhost-net

Vhost-net已经悄悄地成为了基于qemu-kvm的虚拟环境中默认的流量卸载机制,利用了标准的virtio网络接口。这种机制允许在一个内核模块中执行网络处理,从而释放了qemu进程,提高了整体的网络性能。

在之前的博客文章中,我们以高层次的概述介绍了构成这种架构的不同元素,并详细解释了这些元素如何协同工作。在本文中,我们将提供一个逐步操作的实际操作指南,教你如何设置这个示例架构。一旦运行起来,我们将能够检查主要组件的运行情况。

在阅读本文后(希望您能在自己的计算机上重新创建这个环境),您将熟悉虚拟化中常用的工具(如virsh),知道如何设置vhost-net环境,以及如何检查运行中的虚拟机并测量其网络性能。

安装环境

要求:

运行Linux发行版的计算机。本指南专注于Fedora 30,但对于其他Linux发行版,命令应该没有显著变化。
具有sudo权限的用户
家目录中约25GB的可用空间
至少8GB的RAM

首先,让我们安装需要的软件包:

user@host $  sudo dnf install qemu-kvm libvirt-daemon-qemu libvirt-daemon-kvm libvirt iperf3 virt-install libguestfs-tools-c kernel-tools

如果要进行基准测试,还需要安装netperf。为此,您可以获取适用于您的操作系统的软件包之一。在编写本文时,您可以执行以下命令安装最新版本:

user@host $  sudo dnf install https://raw.githubusercontent.com/rpmsphere/x86_64/master/n/netperf-2.7.0-2.1.x86_64.rpm

在以后需要时,您还需要在虚拟机中安装netperf!
为了能够使用libvirt,您的用户必须是libvirt组的一部分:

user@host $ sudo usermod -a -G libvirt $(whoami)

在修改用户的组成员身份后,您可能需要重新登录以使更改生效。此外,您需要重新启动libvirt:

user@host $ sudo systemctl restart libvirtd

Creating a VM

首先,下载最新的Fedora-Cloud-Base镜像:

user@host $ sudo wget -O /var/lib/libvirt/images/Fedora-Cloud-Base-30-1.2.x86_64.qcow2 http://fedora.inode.at/releases/30/Cloud/x86_64/images/Fedora-Cloud-Base-30-1.2.x86_64.qcow2

(请注意上面的URL可能会更改,请更新为http://fedora.inode.at/releases/30/Cloud/x86_64/images/中的最新qcow2镜像)

这将下载一个预先安装了Fedora30的版本,可以在OpenStack环境中运行。由于我们没有运行OpenStack,所以我们必须清理镜像。为此,首先我们将复制该镜像,以便将来重复使用:

user@host $ sudo qemu-img create -f qcow2 -b  /var/lib/libvirt/images/Fedora-Cloud-Base-30-1.2.x86_64.qcow2  /var/lib/libvirt/images//virtio-test1.qcow2 20G

如果我们导出以下变量,可以使用非特权用户(推荐)执行以下libvirt命令

user@host $ export LIBVIRT_DEFAULT_URI="qemu:///system"

现在,执行清理命令(将密码更改为您自己的密码):

user@host $ sudo virt-sysprep --root-password password:changeme --uninstall cloud-init --selinux-relabel -a /var/lib/libvirt/images/virtio-test1.qcow2

这个命令会挂载文件系统,并自动应用一些基本的配置,以便镜像可以重新启动。

我们还需要一个网络来连接我们的虚拟机。Libvirt以与管理虚拟机类似的方式处理网络,您可以使用XML文件定义网络,并通过命令行启动或停止它。

在这个示例中,我们将使用一个名为“default”的网络,它的定义已经包含在libvirt中以方便使用。以下命令定义了“default”网络,启动它并检查它是否正在运行。

user@host $ virsh net-define /usr/share/libvirt/networks/default.xml
Network default defined from /usr/share/libvirt/networks/default.xml
user@host $ virsh net-start default
Network default started
user@host $virsh net-list
 Name      State    Autostart   Persistent
--------------------------------------------
 default   active   no          yes

最后,我们可以使用virt-install来创建虚拟机。这个命令行实用程序会为一组知名操作系统创建所需的定义。这将为我们提供基本的定义,然后我们可以进行自定义:

user@host $ virt-install --import  --name virtio-test1 --ram=4096 --vcpus=2 \
--nographics --accelerate \
       --network network:default,model=virtio --mac 02:ca:fe:fa:ce:01 \
      --debug --wait 0 --console pty \
      --disk /var/lib/libvirt/images/virtio-test1.qcow2,bus=virtio --os-variant fedora30

除了根据我们指定的选项定义虚拟机之外,virt-install命令还应该已经为我们启动了虚拟机,因此我们应该能够列出它:

user@host $ virsh list
 Id   Name           State
------------------------------
 1    virtio-test1   running

大功告成!我们的虚拟机正在运行。
作为一个提醒,virsh是与libvirt守护程序通信的命令行接口。您可以通过运行以下命令来启动虚拟机:

user@host $ virsh start virtio-test1

通过运行以下命令跳转到控制台:

user@host $ virsh console virtio-test1

通过运行以下命令停止虚拟机:

user@host $ virsh shutdown virtio-test1

通过运行以下命令删除虚拟机(如果您不想再次创建虚拟机,请勿立即执行!):

user@host $ virsh undefine virtio-test1

检查客户端

如前所述,virt-install 命令已自动使用 libvirt 创建并启动了一个虚拟机。使用 libvirt 创建的每个虚拟机都由一个 XML 文件描述,该文件定义了正在模拟的硬件。让我们通过转储其内容来查看此文件的相关部分:

user@host $ virsh dumpxml virtio-test1

具体来说,让我们查看virt-install为我们创建的网络设备:

 <devices>
...     
             <interface type='network'>                                                                                                                                                                                 
      <mac address='02:ca:fe:fa:ce:01'/>   
      <source network='default' bridge='virbr0'/>                
      <target dev='vnet0'/>   
      <model type='virtio'/>   
      <alias name='net0'/>  
      <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>                                                                                                                               
    </interface></devices>

我们可以看到已经创建了一个virtio设备,并且它连接到一个网络中,其中包括一个Linux桥接(名为virbr0)。 此外,该设备已被分配了一个PCI域、总线和插槽。
现在,让我们进入虚拟机的控制台,看看它从内部是什么样子的:

user@host $ virsh console virtio-test1

一旦登录(使用您在virt-sysprep步骤中配置的密码),在继续之前,让我们安装一些软件包:

[root@guest ~]# dnf install pciutils iperf3

现在,让我们四处看看。我们可以看到我们的虚拟PCI总线上确实有一个网络设备(根据您的XML修改PCI总线,或者首先使用lspci检查):

[root@localhost ~]# lspci -s 0000:01:00.0 -v
01:00.0 Ethernet controller: Red Hat, Inc. Virtio network device (rev 01)
     Subsystem: Red Hat, Inc. Device 1100
     Physical Slot: 0
     Flags: bus master, fast devsel, latency 0, IRQ 21
     Memory at fda40000 (32-bit, non-prefetchable) [size=4K]
     Memory at fea00000 (64-bit, prefetchable) [size=16K]
     Expansion ROM at fda00000 [disabled] [size=256K]
     Capabilities: [dc] MSI-X: Enable+ Count=3 Masked-
     Capabilities: [c8] Vendor Specific Information: VirtIO: <unknown>
     Capabilities: [b4] Vendor Specific Information: VirtIO: Notify
     Capabilities: [a4] Vendor Specific Information: VirtIO: DeviceCfg
     Capabilities: [94] Vendor Specific Information: VirtIO: ISR
     Capabilities: [84] Vendor Specific Information: VirtIO: CommonCfg
     Capabilities: [7c] Power Management version 3
     Capabilities: [40] Express Endpoint, MSI 00
     Kernel driver in use: virtio-pci

除了典型的PCI信息(如内存区域和功能),我们还看到使用的驱动程序是virtio-pci。此驱动程序实现了通用的PCI上的virtio功能,并创建了一个virtio设备,然后由virtio_net驱动,如果我们稍微进一步检查PCI设备,我们可以看到:

[root@localhost ~]# readlink /sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0/virtio0/driver
../../../../../bus/virtio/drivers/virtio_net

virtio_net驱动程序负责为操作系统的其余部分创建一个网络接口:

[root@localhost ~]# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 02:ca:fe:fa:ce:01 brd ff:ff:ff:ff:ff:ff
[root@localhost ~]#

在主机上检查

好的,我们已经查看了客户端,现在让我们查看主机。请注意,如果我们配置一个类型为“network”的接口,则默认行为是使用vhost-net。
首先,让我们查看是否加载了vhost-net:

user@host $ sudo lsmod | grep vhost
vhost_net              32768  1
vhost                  53248  1 vhost_net
tap                    28672  1 vhost_net
tun                    57344  4 vhost_net

我们还可以通过检查QEMU与tun、kvm和vhost-net设备以及QEMU进程为它们分配的文件描述符之间的交互来检查它们(实际的文件描述符ID可能会有所变化),检查/proc文件系统:

user@host $ ls -lh /proc/$(pgrep qemu)/fd | grep '/dev'
...
lrwx------. 1 qemu qemu 64 Aug 27 06:38 13 -> /dev/kvm
lrwx------. 1 qemu qemu 64 Aug 27 06:38 30 -> /dev/net/tun
lrwx------. 1 qemu qemu 64 Aug 27 06:38 31 -> /dev/vhost-net

这意味着除了打开了一个kvm设备来执行实际的虚拟化并创建了一个tun/tap设备外,qemu进程还打开了一个vhost-net设备。此外,我们还可以看到与我们的qemu实例相关联的vhost内核线程已经创建:

user@host $ ps -ef | grep '\[vhost'
root     21743     2  0 14:11 ?        00:00:00 [vhost-21702]
$ pgrep qemu
21702

最后,我们可以看到由qemu进程创建的tun接口(也可以看到qemu打开的文件描述符),以及连接主机和客户机的桥接接口。请注意,尽管tap设备附加到了qemu进程,但vhost-net内核线程才是实际的tap读写者。

user@host $ # ip -d tuntap
virbr0-nic: tap persist
    Attached to processes:
vnet0: tap vnet_hdr
    Attached to processes:qemu-system-x86(21702)

好的,vhost已经启动并运行了,qemu也连接到了它。现在,让我们生成一些流量,看看系统的性能如何。

生成流量

如果您正确地按照先前的步骤操作,可以使用它们的IP地址从主机发送数据到虚拟机,反之亦然。例如,使用iperf3测试网络性能。请注意,这些测量不是正式的基准测试,任何参数的微小变化,如软件或硬件版本或不同的网络堆栈参数,都可能显著改变所获得的结果。性能调优或特定用途的基准测试不在本文档的范围之内。

首先检查虚拟机的IP地址,并在其上执行iperf3服务器(或任何您想要用于检查连通性或进行基准测试的工具):

[root@guest ~]# ip addr
...
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 02:ca:fe:fa:ce:01 brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.41/24 brd 192.168.122.255 scope global dynamic noprefixroute eth0
       valid_lft 2534sec preferred_lft 2534sec
    inet6 fe80::ca:feff:fefa:ce01/64 scope link
       valid_lft forever preferred_lft forever
[root@localhost ~]# iperf3 -s
user@host $ iperf3 -c 192.168.122.41
...
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  26.3 GBytes  22.6 Gbits/sec    0             sender
[  5]   0.00-10.04  sec  26.3 GBytes  22.5 Gbits/sec                  receiver

以及主机上的iperf3客户端:

user@host $ iperf3 -c 192.168.122.41
...
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  26.3 GBytes  22.6 Gbits/sec    0             sender
[  5]   0.00-10.04  sec  26.3 GBytes  22.5 Gbits/sec                  receiver

在iperf3的输出中,我们可以看到在两个方向上的传输速度约为22.5 Gbit/sec(请记住,网络带宽取决于许多因素,所以不要期望在您的环境中获得相同的速度)。我们可以修改数据包的大小(使用-l选项)以增加数据平面的负载。
如果在iperf3测试期间运行top命令,我们可以看到vhost-$pid内核线程正在使用一个完整的核心进行数据包转发,而QEMU几乎使用了两个核心。

user@host $ top
...
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
14548 qemu      20   0 6094256   1.1g  22304 S 180.5   3.5   1:37.80 qemu-system-x86     
14586 root      20   0       0      0      0 R  99.7   0.0   0:20.83 vhost-14548
14753 root      20   0    3904   2360   2116 R  57.0   0.0   0:13.39 iperf3

要测量延迟,我们使用netperf命令启动netperf服务器,然后使用以下命令来测量延迟:

user@host $ netperf -l 30 -H 192.168.122.41 -p 16604 -t TCP_RR
Socket Size   Request  Resp.   Elapsed  Trans.
Send   Recv   Size     Size    Time     Rate      
bytes  Bytes  bytes    bytes   secs.    per sec

212992 212992 1        1       30.00    28822.60

因此,该示例中的延迟为1/28822.60 = 0.000000347秒,因为所有请求都是串行的。正如之前所说,我们并未进行适当的基准测试或精细调整。我们只是为了让您熟悉这项技术而进行这些操作。

Extra: Disable vhost-net

正如我们已经看到的,由于它带来的性能提升,vhost-net的使用是默认行为。然而,由于我们在这里是为了动手实践和学习,让我们禁用vhost-net并查看性能上的差异。通过这样做,我们将看到qemu必须处理所有数据包处理的繁重工作,以及它如何影响性能。

首先,停止虚拟机:

​user@host $ virsh shutdown virtio-test1

然后,编辑虚拟机并在网络定义中添加 :

user@host $ virsh edit virtio-test1   # it will open your editor

修改网络接口,使其如下所示:

<devices>
...     
        <interface type='network'>                                                                                                                                                                                 
      <mac address='02:ca:fe:fa:ce:01'/>   
      <source network='default' bridge='virbr0'/>                
      <target dev='vnet0'/>   
      <model type='virtio'/>
             <driver name="qemu"/>
      <alias name='net0'/>  
      <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
        </interface></devices>

退出编辑器,然后使用以下命令重新启动虚拟机:

user@host $ virsh start virtio-test1

您可以检查没有指向 /dev/vhost-net 的文件描述符了。

分析性能影响

如果我们在没有vhost-net的情况下重复上一节的基准测试,现在可以看到在top输出中没有显示vhost-net内核线程,并且我们可以看到性能下降了(约为19.2 Gb/sec):

user@host $ iperf3 -c 192.168.122.41
...
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  22.4 GBytes  19.2 Gbits/sec    2             sender
[  5]   0.00-10.00  sec  22.4 GBytes  19.2 Gbits/sec                  receiver

此外,如果我们使用top命令,可以看到qemu进程的CPU使用率增加,从大约180%上升到190-260%,当然也没有vhost-$pid内核线程的迹象。

user@host $ top
...
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
 1954 qemu      20   0 6007188 569192  22020 R 220.6   1.7   2:39.32 qemu-system-x86

如果我们比较两种架构的TCP和UDP延迟,我们会发现使用vhost-net相对于QEMU提供了一致的性能提升:

在这里插入图片描述

另一个了解情况的好指标是qemu必须发送给KVM的IOCTL数量。这是因为每当有一个需要由qemu处理的I/O事件时,它需要处理它并发送一个IOCTL给KVM,以便将上下文切换回客户端。我们可以使用strace命令分析qemu在每个系统调用上花费的时间。以下表格比较了使用和不使用vhost-net驱动程序时获得的结果:在这里插入图片描述
在这个表格中,我们可以看到没有vhost-user时,qemu花费了大量时间读取数据并发送IOCTL

Ansible脚本现已提供!

设置和运行此环境是为了理解、调试和测试这个架构的第一步,也是基本的步骤。为了尽快、尽可能地简化这个过程,红帽的virtio-net团队已经开发了一组Ansible脚本供每个人在GitHub上使用。

只需按照README中的说明操作,Ansible应该会处理剩下的事情。

结论

在这篇文章中,我们通过创建一个带有QEMU和vhost-net的虚拟机,检查了宿主机和客户机,以便了解这个架构的各个方面。我们还展示了vhost-net流量卸载带来的性能改进。

这是一次旅程的最后一站,旅程始于“virtio-networking和vhost-net简介”,在那里介绍了该架构的概述。接着是“深入了解virtio-networking和vhost-net”,其中详细解释了所有组件。现在,通过展示如何实际设置事物,我们总结了这个主题,希望为IT专家、架构师和开发人员提供足够的资源,以了解这项技术的优势并开始使用它。

敬请关注我们将要讨论的下一个主题:用户空间网络和DPDK!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

写一封情书

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值