这周为了提高网络压测的压力,尝试了一下VPP,初次接触,好多东西不是很理解。但不管怎么样,先安装起来跑跑,积累一点初步的认识吧。 我的验证环境如下:

  • 虚拟化:kvm,创建两台4核4G的虚拟机;
  • 操作系统:Ubuntu 20.04
  • VPP版本:22.06
  • DPDK版本:19.11.12

只是很粗浅的尝试,如果你也正好是入门选手,希望对你有帮助。

创建虚拟机

我平时主要是用vagrant创建虚拟机,这次也不例外。Vagrant文件如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# Defaults for config options defined in CONFIG
$num_instances ||= 2
$hostname_prefix ||= "vpp"
$libvirt_nested ||= false
$vm_cpus ||= 4
$vm_memory ||= 4096

$subnet1_prefix ||= "172.18.10"
$subnet1_startip ||= 30
$subnet2_prefix ||= "172.18.11"
$subnet2_startip ||= 30

Vagrant.configure("2") do |config|
    config.vm.box_check_update = false
    config.vm.box = "generic/ubuntu2004"

    # copy files into vm
    config.vm.synced_folder ".", "/vagrant", type: "rsync", disabled: true

    (1..$num_instances).each do |i|
        config.vm.define vm_name = "%s-%01d" % [$hostname_prefix, i] do |node|

            node.vm.hostname = vm_name
            node.vm.provider :libvirt do |lv|
                lv.nested = $libvirt_nested
                lv.cpu_mode = "host-model"
                lv.cpus = $vm_cpus
                lv.memory = $vm_memory
                lv.disk_bus = 'scsi'
            end
            # network
            eth1ip = "#{$subnet1_prefix}.#{i+$subnet1_startip}"
            eth1net = "net-#{$subnet1_prefix}"
            node.vm.network :private_network, :ip => eth1ip, :libvirt__network_name => eth1net
            eth2ip = "#{$subnet2_prefix}.#{i+$subnet2_startip}"
            eth2net = "net-#{$subnet2_prefix}"
            node.vm.network :private_network, :ip => eth2ip, :libvirt__network_name => eth2net
            
        end
    end
end

通过上面的vagrant up我们就可以创建两台ubuntu 20.04的虚拟机。具体如下:

1
2
3
4
5
6
7
# 拉起虚拟机
$ vagrant up
...
# 注意:如果你缺少ubuntu 20.04的镜像,需要保证机器可以联网
# 创建成功后,通过下面的命令可以登录虚拟机
$ vagrant ssh vpp-1
$ vagrant ssh vpp-2

安装VPP

创建好了虚拟机之后,接下来就是安装vpp软件了。对于常见的系统,FD.io社区都有编译好的二进制安装文件,我们就直接用编译好的安装文件安装即可。具体如下:

1
2
3
4
5
6
7
8
9
# 配置软件源
$ curl -s https://packagecloud.io/install/repositories/fdio/release/script.deb.sh | sudo bash

# 创建日志文件及目录(缺少这步会导致启动失败,当然也可以通过修改配置文件解决)
$ mkdir -p /var/log/vpp
$ touch /var/log/vpp/vpp.log

# 安装vpp
$ apt install -y dpdk vpp vpp-plugin-core vpp-plugin-dpdk vpp-dev vpp-plugin-devtools  net-tools

若无异常,到此就安装完成了,接下来就是配置了。

配置VPP

DPDK相关配置

我们创建的虚拟机有三张网卡,其中第一张是用来管理的,其他两张是用来测试。VPP可以使用DPDK作为数据面,因此,我们需要先配置DPDK,修改网卡的驱动。首先,通过DPDK提供的工具看看现在网卡的状态。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ dpdk-devbind.py -s

Network devices using DPDK-compatible driver
============================================

Network devices using kernel driver
===================================
0000:00:05.0 'Virtio network device 1000' if=eth0 drv=virtio-pci unused=vfio-pci,uio_pci_generic *Active*
0000:00:06.0 'Virtio network device 1000' if=eth1 drv=virtio-pci unused=vfio-pci,uio_pci_generic *Active*
0000:00:07.0 'Virtio network device 1000' if=eth2 drv=virtio-pci unused=vfio-pci,uio_pci_generic *Active*
...

上面的输出可以看到,有三张Virtio的网卡,现在都是在使用内核驱动,不是DPDK兼容的驱动。我们需要修改一下,如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 加载dpdk兼容的uio_pci_generic驱动
$ modprobe uio_pci_generic

## 去掉eth1的ip地址,否则后面操作会失败
$ ifconfig eth1 0 up
$ dpdk-devbind.py -b uio_pci_generic 0000:00:06.0
# 若无错误提示,就是成功了,用同样的方法,设置eth2
$ ifconfig eth2 0 up
$ dpdk-devbind.py -b uio_pci_generic 0000:00:07.0

$ dpdk-devbind.py -s   ## 再次查看网卡的驱动情况

Network devices using DPDK-compatible driver
============================================
0000:00:06.0 'Virtio network device 1000' drv=uio_pci_generic unused=vfio-pci
0000:00:07.0 'Virtio network device 1000' drv=uio_pci_generic unused=vfio-pci

Network devices using kernel driver
===================================
0000:00:05.0 'Virtio network device 1000' if=eth0 drv=virtio-pci unused=vfio-pci,uio_pci_generic *Active*

注意:每块网卡的PCI地址不一样,具体内容以dpdk-devbind.py -s的输出为准。 修改之后,通过ifconfig -a应该看不到eth1和eth2两张网卡了,但它们可以被VPP使用。

VPP配置文件

VPP默认的配置文件路径是/etc/vpp/startup.conf,我对其内容做了一下简化,如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
$ cat /etc/vpp/startup.conf
unix {
    nodaemon
    log /var/log/vpp/vpp.log
    full-coredump
    cli-listen /run/vpp/cli.sock
    gid vpp
    startup-config /etc/vpp/setup-interface.txt
}

api-trace {
    on
}

api-segment {
    gid vpp
}

socksvr {
    default
}

cpu {
    main-core 0
}

dpdk {
    dev 0000:00:06.0 {
        name eth1
    }
    dev 0000:00:07.0 {
        name eth2
    }
}

上面的配置文件中,除了dpdk部分及startup-config,其他的都是默认配置。我们可以把一些接口的配置信息放到startup-config中,这样整个配置文件不会太复杂。startup-config具体的内容如下:

1
2
3
4
5
6
$ cat /etc/vpp/setup-interface.txt
set interface state eth1 up
set interface state eth2 up

set interface ip address eth1 172.18.10.31/24
set interface ip address eth2 172.18.11.31/24

上面的配置,是给激活VPP的两个网口eth1和eth2,并给它们配置IP地址。

配置完成后,我们可以重启vpp,并查看其状态。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
$ systemctl restart vpp
$ systemctl status vpp
$ vppctl
    _______    _        _   _____  ___
 __/ __/ _ \  (_)__    | | / / _ \/ _ \
 _/ _// // / / / _ \   | |/ / ___/ ___/
 /_/ /____(_)_/\___/   |___/_/  /_/

## 查看接口的IP地址
vpp# show int addr
eth1 (up):
  L3 172.18.10.31/24
eth2 (up):
  L3 172.18.11.31/24
local0 (dn):
vpp#

网络测试

安装同样的方法,配置两台虚拟机(注意:IP地址不要重了)。

测试连通性

我们在vpp-1这台虚拟机上ping下vpp-2看看。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
root@vpp-1:~$ vppctl ping 172.18.10.32
116 bytes from 172.18.10.32: icmp_seq=1 ttl=64 time=.1754 ms
116 bytes from 172.18.10.32: icmp_seq=2 ttl=64 time=.0893 ms
116 bytes from 172.18.10.32: icmp_seq=3 ttl=64 time=.1191 ms
116 bytes from 172.18.10.32: icmp_seq=4 ttl=64 time=.2431 ms
116 bytes from 172.18.10.32: icmp_seq=5 ttl=64 time=.1220 ms

Statistics: 5 sent, 5 received, 0% packet loss

root@vpp-1:~$ vppctl ping 172.18.11.32
116 bytes from 172.18.11.32: icmp_seq=2 ttl=64 time=.1408 ms
116 bytes from 172.18.11.32: icmp_seq=3 ttl=64 time=.1069 ms
116 bytes from 172.18.11.32: icmp_seq=4 ttl=64 time=.0985 ms
116 bytes from 172.18.11.32: icmp_seq=5 ttl=64 time=.1252 ms

Statistics: 5 sent, 4 received, 20% packet loss

从上面的结果可以看出,两个IP都是可达的。

测试带宽

下面我们再用iperf3跑跑带宽测试。为了使用VPP里面提供的网口,iperf3需要加载libvcl_ldpreload.so这个动态链接库,来替换部分网络编程接口。 此外,我们还需要配置一下VCL,具体如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 添加VCL的配置文件
$ cat > /etc/vpp/vcl.conf << EOF
vcl {
    rx-fifo-size 4000000
    tx-fifo-size 4000000
    app-scope-local
    app-scope-global
    api-socket-name /run/vpp/api.sock
    use-mq-eventfd
}
EOF

$ export VCL_CONFIG=/etc/vpp/vcl.conf
$ export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libvcl_ldpreload.so
$ iperf3 -s --bind 172.18.10.31  # 启动服务端

用同样的方法,我们在vpp-2上启动客户端,发起测试。

1
2
3
$ export VCL_CONFIG=/etc/vpp/vcl.conf
$ export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libvcl_ldpreload.so
$ iperf3 -c 172.18.10.31 -p 5201 -t 30  # 启动客户端,测试30秒

初步测试来看,性能并不好。不知道是不是跑在虚拟机中的原因,比直接跑iperf3测试出来的带宽低很多。