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內存,與預期相符

Alt text
另外查看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工具是單線程程序的原因。

總的來說,配置如下圖所示:

Alt text

接下來分別在兩台虛擬機上進行測試:
使用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測試中):

Alt text

總的來說,配置如下圖所示:

Alt text

接下來依然同上進行測試:
在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.1 10.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的關系

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM