ovs+dpdk numa感知特性验证

0.介绍

本测试是为了验证这篇文章中提到的DPDK的NUMA感知特性。
简单来说,在ovs+dpdk+qemu的环境中,一个虚拟机牵涉到的内存共有三部分:

  1. DPDK为vHost User设备分配的Device tracking memory
  2. OVS为网络通信分配的mbufs
  3. QEMU为虚拟机分配的内存

未开启DPDK的NUMA感知特性时,所有Device tracking memory都会使用同一个NUMA节点中的内存,如果这时QEMU为两台虚拟机分配的内存刚好在两个不同的NUMA节点上,那么机器间的网络通信效率就会有损耗。

开启了DPDK的NUMA感知特性后,在QEMU创建虚拟机后,DPDK会将vHost User设备的Device tracking memory挪到与虚拟机相同的NUMA节点上,并且DPDK还会通知OVS,以让OVS把与该虚拟机相关的mbufs分配在与虚拟机相同的NUMA节点上。

理论上这应该能增加两虚拟机的网络通信效率,所以本文就来用实验探究一下

但在实验之前,我对结果的预期是悲观的,我感觉(感性的)虚拟机间的通信效率更多的受制于虚拟机本身的配置,尤其是在虚拟机的规格比较差的情况下。

本文将进行对照测试,分别在“带NUMA感知的DPDK环境”“不带NUMA感知的DPDK环境”进行测试,每次测试都在两个不同的NUMA节点上分别启动一台配置相同的虚拟机,使用netperf对两虚拟机间的网络通信效率进行测量。

虚拟机配置: 2*vcpu, 4G RAM, CentOS 7

1.测试环境配置

host的基本配置如下:

版本/配置
CPU 2 * [Intel(R) Xeon(R) CPU E5-2698 v3 @ 2.30GHz]
OVS ovs-vsctl (Open vSwitch) 2.7.0
DPDK stable-16.11.1
[root@compute2 zsb]# numactl --hardware
available: 2 nodes (0-1)
node 0 cpus: 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62
node 0 size: 130975 MB
node 0 free: 102930 MB
node 1 cpus: 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51 53 55 57 59 61 63
node 1 size: 131072 MB
node 1 free: 114196 MB
node distances:
node 0 1
0: 10 21
1: 21 10

1.1.numa库与命令行软件的安装

yum install numactl
yum install numactl-libs
yum install numactl-devel

1.2.内核启动参数配置

16个1G规格的大页,逻辑核心1-17,33-49不参与内核处理器调度

hugepagesz=1G
hugepages=16
iommu=pt
intel_iommu=on
isolcpus=1-17,33-49

1.3.hugepage挂载设置

移除原有的2M规格大页的挂载点,只挂载规格为1G的大页

umount /dev/hugepages
mkdir /mnt/huge_1GB
mount -t hugetlbfs -o pagesize=1G none /mnt/huge_1GB

1.4.dpdk的编译

打开numa感知的编译选项

CONFIG_RTE_LIBRTE_VHOST_NUMA=y

编译

export DPDK_DIR=<DPDK_DIR> 
export DPDK_TARGET=x86_64-native-linuxapp-gcc
export DPDK_BUILD=$DPDK_DIR/$DPDK_TARGET
make install T=$DPDK_TARGET DESTDIR=install

1.5.ovs的编译安装

export OVS_DIR=<OVS_DIR> 
cd $OVS_DIR
./configure --with-dpdk=$DPDK_BUILD CFLAGS="-Ofast -g"
make CFLAGS="-Ofast -g"
make install

1.6.ovs的启动与pmd轮询核心亲和性的配置

pmd-mask被设置为0x0c0000000c,即pmd亲和2 3 34 35四个逻辑核心。其中2与34是同一个物理封装里同一个core的两个超线程,位于NUMA 0,3与35是另外一个物理封装里的同一个core的两个超线程,位于NUMA 1。如下表

逻辑核心编号物理封装编号core编号属于的NUMA节点
2 0 1 0
34 0 1 0
3 1 1 1
35 1 1 1
rm -rf /usr/local/etc/openvswitch/conf.db
mkdir -p /usr/local/etc/openvswitch
ovsdb-tool create /usr/local/etc/openvswitch/conf.db $OVS_DIR/vswitchd/vswitch.ovsschema
mkdir -p /usr/local/var/run/openvswitch
ovsdb-server --remote=punix:/usr/local/var/run/openvswitch/db.sock \
--remote=db:Open_vSwitch,Open_vSwitch,manager_options \
--private-key=db:Open_vSwitch,SSL,private_key \
--certificate=db:Open_vSwitch,SSL,certificate \
--bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert \
--pidfile --detach --log-file
ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-init=true
ovs-vsctl --no-wait set Open_vSwitch . other_config:pmd-cpu-mask=0x0c0000000c
ovs-vsctl --no-wait set Open_vSwitch . other_config:dpdk-socket-mem="1024,1024"
export DB_SOCK=/usr/local/var/run/openvswitch/db.sock
ovs-vswitchd unix:$DB_SOCK --pidfile --detach --log-file

1.7.创建bridge与port

ovs-vsctl add-br br0 -- set bridge br0 datapath_type=netdev
ovs-vsctl add-port br0 port1 \
-- set interface port1 type=dpdkvhostuserclient \
-- set interface port1 options:vhost-server-path=/var/lib/openvswitch/port1
ovs-vsctl add-port br0 port2 \
-- set interface port2 type=dpdkvhostuserclient \
-- set interface port2 options:vhost-server-path=/var/lib/openvswitch/port2

1.8.启动虚拟机的配置

启动虚拟机的libvirt domain xml文件如下所示,注意两台虚拟机配置有差异的地方均有注释
注意虚拟机1配置vcpu时,将cpuset设置为6,38,在host上,6号与38号逻辑cpu属于NUMA node 0,而5,37属于NUMA node 1

<domain type=‘kvm‘>
<!-- 另外一台虚拟机:NUMA_Awareness_test_2 -->
<name>NUMA_Awareness_test_1</name>
<memoryBacking>
<hugepages/>
</memoryBacking>
<!-- 另外一台虚拟机:cpuset="5,37" -->
<vcpu placement=‘static‘ cpuset="6,38">2</vcpu>
<cpu mode=‘host-model‘>
<model fallback=‘allow‘/>
<topology sockets=‘2‘ cores=‘1‘ threads=‘1‘/>
<numa>
<cell id=‘0‘ cpus=‘0-1‘ memory=‘4194304‘ unit=‘KiB‘ memAccess=‘shared‘/>
</numa>
</cpu>
<resource>
<partition>/machine</partition>
</resource>
<os>
<type arch=‘x86_64‘ machine=‘pc‘>hvm</type>
<boot dev=‘hd‘/>
</os>
<features>
<acpi/>
<apic/>
<pae/>
</features>
<clock offset=‘utc‘>
<timer name=‘rtc‘ tickpolicy=‘catchup‘/>
<timer name=‘pit‘ tickpolicy=‘delay‘/>
<timer name=‘hpet‘ present=‘no‘/>
</clock>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>restart</on_crash>
<pm>
<suspend-to-mem enabled=‘no‘/>
<suspend-to-disk enabled=‘no‘/>
</pm>
<devices>
<emulator>/usr/libexec/qemu-kvm</emulator>
<disk type=‘file‘ device=‘disk‘>
<driver name=‘qemu‘ type=‘qcow2‘/>
<!-- 另外一台虚拟机 file=‘/export/zsb/image/NUMA_Awareness_test_2.qcow2‘ -->
<source file=‘/export/zsb/image/NUMA_Awareness_test_1.qcow2‘/>
<target dev=‘hda‘ bus=‘ide‘/>
<address type=‘drive‘ controller=‘0‘ bus=‘0‘ target=‘0‘ unit=‘0‘/>
</disk>
<controller type=‘pci‘ index=‘0‘ model=‘pci-root‘/>
<controller type=‘ide‘ index=‘0‘>
<address type=‘pci‘ domain=‘0x0000‘ bus=‘0x00‘ slot=‘0x01‘ function=‘0x1‘/>
</controller>
<controller type=‘virtio-serial‘ index=‘0‘>
<address type=‘pci‘ domain=‘0x0000‘ bus=‘0x00‘ slot=‘0x06‘ function=‘0x0‘/>
</controller>
<interface type=‘vhostuser‘>
<!-- 另外一台虚拟机:path=‘/var/lib/openvswitch/port2‘ -->
<source type=‘unix‘ mode=‘server‘ path=‘/var/lib/openvswitch/port1‘/>
<model type=‘virtio‘/>
<driver name=‘vhost‘/>
</interface>
<serial type=‘pty‘>
<target port=‘0‘/>
</serial>
<console type=‘pty‘>
<target type=‘serial‘ port=‘0‘/>
</console>
<input type=‘tablet‘ bus=‘usb‘>
<address type=‘usb‘ bus=‘0‘ port=‘1‘/>
</input>
<input type=‘mouse‘ bus=‘ps2‘/>
<input type=‘keyboard‘ bus=‘ps2‘/>
<graphics type=‘vnc‘ port=‘-1‘ autoport=‘yes‘ listen=‘0.0.0.0‘>
<listen type=‘address‘ address=‘0.0.0.0‘/>
</graphics>
<sound model=‘ich6‘>
<address type=‘pci‘ domain=‘0x0000‘ bus=‘0x00‘ slot=‘0x04‘ function=‘0x0‘/>
</sound>
<video>
<model type=‘cirrus‘ vram=‘16384‘ heads=‘1‘ primary=‘yes‘/>
<address type=‘pci‘ domain=‘0x0000‘ bus=‘0x00‘ slot=‘0x02‘ function=‘0x0‘/>
</video>
<memballoon model=‘virtio‘>
<address type=‘pci‘ domain=‘0x0000‘ bus=‘0x00‘ slot=‘0x08‘ function=‘0x0‘/>
</memballoon>
</devices>
<seclabel type=‘none‘ model=‘none‘/>
<seclabel type=‘dynamic‘ model=‘dac‘ relabel=‘yes‘/>
</domain>

在虚拟机启动后,编辑虚拟机的网络设置脚本,给两个虚拟机分别配上10.0.0.1与10.0.0.2的IP地址

vim /etc/sysconfig/network-scripts/ifcfg-eth0
--------------------------------
DEVICE=eth0
TYPE=Ethernet
BOOTPROTO=none
ONBOOT=yes
PREFIX=24
# 对于另外一台虚拟机,设置IP地址为10.0.0.2
IPADDR=10.0.0.1

为了方便ssh到虚拟机中,我们在host上创建一个namespace,并创建一个veth-pair设备,把veth-pair的一端放在namespace中,另一端接在ovs-bridge上,从该namespace中ssh到两个虚拟机
其中namespace的名为numa_awareness_test
veth-pair设备中,接入ovs-bridge的设备名为veth-ovs,接入namespace的设备名为veth-ns

ip netns add numa_awareness_test
ip link add name veth-ovs type veth peer name veth-ns
ip link set veth-ovs up
ip link set veth-ns netns numa_awareness_test
ip netns exec numa_awareness_test ip link set dev lo up
ip netns exec numa_awareness_test ifconfig veth-ns 10.0.0.3/24 up
ovs-vsctl add-port br0 veth-ovs

为了能登录到虚拟机中,还需要关闭掉namespace中veth-ns的checksum offload功能

ip netns exec numa_awareness_test ethtool -K veth-ns tx off

之后通过在namespace中执行ssh就可登录到虚拟机上

ip netns exec numa_awareness_test ssh 10.0.0.1
ip netns exec numa_awareness_test ssh 10.0.0.2

另外为了方便监控hugepage的使用情况,可以用以下一个简单的bash脚本进行监测

#! /bin/bash

function printRed() {
printf "\e[1;31m$1\e[1;0m"
}

printRed "----------------\n"
printRed "Hugepagesz = 1GB\n"
total=$(cat /sys/devices/system/node/node0/hugepages/hugepages-1048576kB/nr_hugepages)
free=$(cat /sys/devices/system/node/node0/hugepages/hugepages-1048576kB/free_hugepages)
printRed " Numa Node 0: Total: $total\n"
printRed " Free: $free\n"
total=$(cat /sys/devices/system/node/node1/hugepages/hugepages-1048576kB/nr_hugepages)
free=$(cat /sys/devices/system/node/node1/hugepages/hugepages-1048576kB/free_hugepages)
printRed " Numa Node 1: Total: $total\n"
printRed " Free: $free\n"

printRed "----------------\n"
printRed "Hugepagesz = 2MB\n"
total=$(cat /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages)
free=$(cat /sys/devices/system/node/node0/hugepages/hugepages-2048kB/free_hugepages)
printRed " Numa Node 0: Total: $total\n"
printRed " Free: $free\n"
total=$(cat /sys/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages)
free=$(cat /sys/devices/system/node/node1/hugepages/hugepages-2048kB/free_hugepages)
printRed " Numa Node 1: Total: $total\n"
printRed " Free: $free\n"

printRed "----------------\n"

2.测试结果

2.1.采用NUMA感知

在两台虚拟机启动成功之后,查看hugepage的使用情况,如下:

[root@compute2 xml]# ../sh/checkHugepages.sh 
----------------
Hugepagesz = 1GB
Numa Node 0: Total: 8
Free: 3
Numa Node 1: Total: 8
Free: 3
----------------
Hugepagesz = 2MB
Numa Node 0: Total: 1024
Free: 1024
Numa Node 1: Total: 1024
Free: 1024
----------------

可以看到两个NUMA节点上每个虚拟机占用了4G内存,ovs分别在两个NUMA节点上占用1G内存,与预期相符

技术分享
另外查看htop命令的输出,可以看到2号逻辑CPU(在htop中显示为3)与35号逻辑CPU(在htop中显示为36)被PMD轮询使用,这是符合预期的,2号逻辑CPU属于NUMA node 0,而35号逻辑CPU属于NUMA node 1。
虽然PMD轮询线程的掩码设置了4个逻辑CPU,但只有两个在工作,这是因为在环境中只有两个vHost User类型的ovs-port存在的原因。
另外,2号和34号其实是同一颗物理核心虚出来的两个超线程,3号与35号也是如此,虽然显示上3号逻辑核心与34号逻辑核心并没有工作,但其实已经在工作的2号核心与35号核心基本能榨干两个物理核心的性能。
而两个虚拟机在运行性能测试工具时,分别导致了逻辑核心5号与6号(在htop中分别显示为6号与7号)高占用,这也是符合预期的,说明两个虚拟机分别运行在两个不同的NUMA节点上。但对应的37号和38号逻辑核心没有什么占用,基本上是因为netperf工具是单线程程序的原因。

总的来说,配置如下图所示:

技术分享

接下来分别在两台虚拟机上进行测试:
使用netperf工具进行测试
在10.0.0.1上运行服务端,在10.0.0.2上运行客户端,五分钟带宽测试的结果如下:

[root@localhost ~]# netperf -H 10.0.0.1 -t TCP_STREAM -l 300
TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.0.0.1 (10.0.0.1) port 0 AF_INET
Recv Send Send
Socket Socket Message Elapsed
Size Size Size Time Throughput
bytes bytes bytes secs. 10^6bits/sec

87380 16384 16384 300.00 6063.37

将netperf的客户端与服务端对换,再次进行5分钱带宽测试,结果如下:

[root@localhost ~]# netperf -H 10.0.0.2 -t TCP_STREAM -l 300
TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.0.0.2 (10.0.0.2) port 0 AF_INET
Recv Send Send
Socket Socket Message Elapsed
Size Size Size Time Throughput
bytes bytes bytes secs. 10^6bits/sec

87380 16384 16384 300.00 5826.15

2.2.不采用NUMA感知

测试配置上与采用NUMA感知配置不同的地方在于

  • 关闭dpdk编译时的CONFIG_RTE_LIBRTE_VHOST_NUMA选项,重新编译dpdk与ovs
  • 配置及启动ovs的过程中,将PMD轮询线程的掩码设置为0x1400000014,即使用2 4 34 36四颗核心,这四颗核心都属于NUMA node 0,且物理是是为两颗物理核心虚拟出来的四个超线程
  • 配置及启动ovs的过程中,将dpdk-socket-mem设置为2048,0,这样ovs+dpdk会占用NUMA node 0上的2G内存自用
  • 两台虚拟机的virsh domain xml配置文件不做更改,即虚拟机的配置是完全相同的

在这个场景中,

  • ovs+dpdk占用NUMA node 0上的2G hugepage 内存,即ovs的mbufs与dpdk的deivce tracking memory都将分配在NUMA node 0上
  • 而两个虚拟机的内存分配和之前一样,即qemu-kvm为虚拟机分配的内存依然分散在两个NUMA节点上
  • PMD轮询依然使用了四个逻辑核心,实际上是两个物理核心,与上相同,不同的是这次四个轮回核心将位于同一个NUMA节点上

虚拟机启动后,hugepage的使用情况如下:

[root@compute2 xml]# ../sh/checkHugepages.sh 
----------------
Hugepagesz = 1GB
Numa Node 0: Total: 8
Free: 2
Numa Node 1: Total: 8
Free: 4
----------------
Hugepagesz = 2MB
Numa Node 0: Total: 1024
Free: 1024
Numa Node 1: Total: 1024
Free: 1024
----------------

其中Node 0上有2G内存供ovs+dpdk使用,再就是每个Node都有4G内存供虚拟机使用

htop的输出如下所示(运行netperf测试中):

技术分享

总的来说,配置如下图所示:

技术分享

接下来依然同上进行测试:
在10.0.0.1上运行服务端,在10.0.0.2上运行客户端,五分钟带宽测试的结果如下:

[root@localhost ~]# netperf -H 10.0.0.1 -t TCP_STREAM -l 300
TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.0.0.1 (10.0.0.1) port 0 AF_INET
Recv Send Send
Socket Socket Message Elapsed
Size Size Size Time Throughput
bytes bytes bytes secs. 10^6bits/sec

87380 16384 16384 300.00 3453.29

将netperf的客户端与服务端对换,再次进行5分钱带宽测试,结果如下:

[root@localhost ~]# netperf -H 10.0.0.2 -t TCP_STREAM -l 300
TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 10.0.0.2 (10.0.0.2) port 0 AF_INET
Recv Send Send
Socket Socket Message Elapsed
Size Size Size Time Throughput
bytes bytes bytes secs. 10^6bits/sec

87380 16384 16384 300.00 5701.31

2.3.数据对比

以下为简单直白的测试数据对比

是否开启NUMA感知10.0.0.2 -> 10.0.0.110.0.0.1->10.0.0.2
6063.37 5826.15
3453.29 5701.31

测试数据比较迷,从10.0.0.2向10.0.0.1打流提升很明显,但反过来就基本没差别了,这后面是什么原因还有待再观察
基本的猜测是由于10.0.0.1所属的虚拟机,其内存与ovs+dpdk同属于一个NUMA节点,这样10.0.0.1发送流量时,流量能更有效率的到达ovs。

3.更多的思考

首先开启了NUMA感知后性能看起来是有提升的,但这个数据比较迷,目前也说不好是为什么

其次,同一host内的虚拟机通信效率可能通过NUMA感知特性进行优化
如果是不同host下的虚拟机要进行通信效率的优化,可能还需要考虑到物理网卡与NUMA的关系

 

文章来自:http://www.cnblogs.com/neooelric/p/7114747.html
© 2021 jiaocheng.bubufx.com  联系我们
ICP备案:鲁ICP备09046678号-3