現在位置: ホーム / みらくるブログ / #8 QEMU/KVMをvhostuserでOpen vSwitchに接続

#8 QEMU/KVMをvhostuserでOpen vSwitchに接続

vhostuser機構を使ってQEMU/KVMとOpen vSwitchを接続します。この方式は共有メモリによってパケットをVMに転送するので、従来のvhostnetやtapを使った方式より性能が向上することが期待されます。これまで使用してきたCentOS7.3上にQEMU 2.6をインストールします。また、VMとしてUbuntu 16.04を用い、その中でDPDK 17.02をビルドして使用しました。また、評価時にはOVSのPMDがポーリングするポートをカスタマイズします。

はじめに

前回は、DPDK + Open vSwitch (OVS)によって、OVSがユーザー空間で直接NICからパケットを転送する方法を紹介しました。今回は、OVSとQEMUをvhost-user機構を使って接続します。vhost-userは、現在、OVS, DPDK, QEMUでサポートされているネットワークによるデータ転送の仕組みです。従来方式と異なり、共有メモリを用いることによりデータのコピーを削減しています。

OVSのvhost-userポートの追加

第7回でセットアップしたOpen vSwitchにvhost-user(OVSでの呼称はdpdkvhostuser)ポートを下図のように2つ追加します。VMにはホストからアクセスしやすいようにLinuxブリッジ経由のNIC (tap)も追加しておきます。VMからはどれもvitio-netのNICに見えます。

08-ovs-eqmu.png

 具体的な追加コマンドは以下のとおりです。

# ovs-vsctl add-port br0 dpdkvhostuser0 -- set Interface dpdkvhostuser0 type=dpdkvhostuser
# ovs-vsctl add-port br0 dpdkvhostuser1 -- set Interface dpdkvhostuser1 type=dpdkvhostuser

OVSの構成を確認するには次のコマンドを実行します。上記コマンドで追加されたポートを水色で強調しています。

# ovs-vsctl show
54e7bf96-649c-4a7d-8839-860b022bbec7
    Bridge "br0"
        Port "dpdkvhostuser1"
            Interface "dpdkvhostuser1"
                type: dpdkvhostuser
        Port "nic2"
            Interface "nic2"
                type: dpdk
                options: {dpdk-devargs="0000:01:00.1"}
        Port "br0"
            Interface "br0"
                type: internal
        Port "dpdkvhostuser0"
            Interface "dpdkvhostuser0"
                type: dpdkvhostuser
        Port "nic1"
            Interface "nic1"
                type: dpdk
                options: {dpdk-devargs="0000:01:00.0"}

Virtulization SIGのQEMUのインストール

Virtualization SIGのQEMUを使用します。デフォルトのものはバージョンが低く、vhost-userに対応していないためです。下記の最初のコマンドで/etc/yum.repos.d/CentOS-QEMU-EV.repoを追加し、次いでそのレポジトリからQEMUをインストールします。

# yum install centos-release-qemu-ev
# yum install qemu-kvm-ev

QEMUの起動

以下のコマンドでQEMUを起動しました。

/usr/libexec/qemu-kvm -name testvm1 -vnc :0 \
-net nic,model=virtio,macaddr=02:cd:c6:a0:01:254 \
-net tap,script=/etc/qemu-ifup-add-bridge.sh \
-cpu host -enable-kvm -m 1024M \
-object memory-backend-file,id=mem,size=4096M,mem-path=/dev/hugepages,share=on \
-numa node,memdev=mem -mem-prealloc -smp sockets=1,cores=3 \
-drive file=/var/lib/libvirt/images/testvm1.qcow2 \
-chardev socket,id=char0,path=/usr/local/var/run/openvswitch/dpdkvhostuser0 \
-netdev type=vhost-user,id=mynet0,chardev=char0,vhostforce \
-device virtio-net-pci,mac=02:cd:c6:a0:01:00,netdev=mynet0,mrg_rxbuf=off \
-chardev socket,id=char1,path=/usr/local/var/run/openvswitch/dpdkvhostuser1 \
-netdev type=vhost-user,id=mynet1,chardev=char1,vhostforce \
-device virtio-net-pci,mac=02:cd:c6:a0:01:01,netdev=mynet1,mrg_rxbuf=off

緑で示したスクリプトは、tapインターフェイスを自動でvirbr0に追加するためのものです。以下に例を示します。

#!/bin/sh
ifconfig $1 up
brctl addif virbr0 $1

黄色で示した行はHugeページから取得したメモリを使用することを指示しています。この領域の位一部がOVSとの共有され、そのエリアを通じてコピーレスなパケット転送が行われます。紫色で示したディスクイメージファイルは各自ご用意ください。以下ではこのイメージの中にUbuntu 16.04がインストールされているものとして解説します。水色で示した行とその下の2行が、1つのvhost-userの仮想NICを作成するための記述です。

VM内でのDPDKのビルドとセットアップ

次の節でVMでのパケット折返し性能測定をします。なるべく高い性能がでるように折り返しプログラムにDPDKのtestpmdを使います。ホストでのDPDKと同様に仮想NICからVMのLinuxカーネルを経由せずにデータを転送します。筆者は上述のとおり、VMにUbuntu 16.04を用いていますが、標準でインストールされるDPDKは少し古いので、17.02をビルドして使用しました。ビルド方法は第2回で紹介した方法と同じです。

$ curl http://fast.dpdk.org/rel/dpdk-17.02.tar.xz | tar xJvf -
(出力結果省略)
$ cd  dpdk-17.02
$ sudo make install T=x86_64-native-linuxapp-gcc DESTDIR=/usr/local EXTRA_CFLAGS="-O3 -g3"

また、ホストで使用する場合と同じく、DPDKはHuge pageを必要とします。そのため、/etc/default/grubに次の一行を追加します。

GRUB_CMDLINE_LINUX_DEFAULT="default_hugepagesz=1G hugepagesz=1G hugepages=1"

そして、grub.confを更新して再起動します。

# update-grub

再起動後、DPDKのビルドディクトリで、カーネルドライバをロードします。

# modprobe uio
# insmod /usr/local/lib/modules/3.10.0-514.10.2.el7.x86_64/extra/dpdk/igb_uio.ko

この状態でNICを確認します。

#  /usr/local/share/dpdk/usertools/dpdk-devbind.py -s

Network devices using DPDK-compatible driver
============================================
<none>

Network devices using kernel driver
===================================
0000:00:03.0 'Virtio network device' if=ens3 drv=virtio-pci unused=igb_uio *Active*
0000:00:04.0 'Virtio network device' if=ens4 drv=virtio-pci unused=igb_uio *Active*
0000:00:05.0 'Virtio network device' if=ens5 drv=virtio-pci unused=igb_uio *Active*
(以下略)

今回の構成ではens4とens5がvhost-userで接続されている仮想NICですので、次のとおり、DPDKにバインドします。

# /usr/local/share/dpdk/usertools/dpdk-devbind.py -b igb_uio 0000:00:04.0 0000:00:05.0
# /usr/local/share/dpdk/usertools/dpdk-devbind.py -s                                  

Network devices using DPDK-compatible driver
============================================
0000:00:04.0 'Virtio network device' drv=igb_uio unused=
0000:00:05.0 'Virtio network device' drv=igb_uio unused=

Network devices using kernel driver
===================================
0000:00:03.0 'Virtio network device' if=ens3 drv=virtio-pci unused=igb_uio *Active*
(以下省略)

2つのNICがNetwork devices using DPDK-compatible driverに移動しました。これで準備完了です。

VMでの折り返し性能測定

これまでと同じく以下のような経路でパケットの折り返し性能を測定しました。方法は第5回のそれと同じです。

08-vm-return.png

ホスト2での設定

OVSのフロールールを設定しておきます。各ポートの番号をホスト2で次の様に入力して調べます

# ovs-ofctl show br0
(略)
 1(nic1): addr:a0:36:9f:64:cc:84
     config:     0
     state:      0
     current:    10GB-FD
     speed: 10000 Mbps now, 0 Mbps max
 2(nic2): addr:a0:36:9f:64:cc:86
     config:     0
     state:      0
     current:    10GB-FD
     speed: 10000 Mbps now, 0 Mbps max
 3(dpdkvhostuser0): addr:00:00:00:00:00:00
     config:     0
     state:      0
     speed: 0 Mbps now, 0 Mbps max
 4(dpdkvhostuser1): addr:00:00:00:00:00:00
     config:     0
     state:      0
     speed: 0 Mbps now, 0 Mbps max
(略)

nic1とdpdkvhostuser0、および、 nic2とdpdkvhostuser1のみが疎通するようにします。

# ovs-ofctl del-flows br0
# ovs-ofctl add-flow br0 in_port=1,action=output:3
# ovs-ofctl add-flow br0 in_port=3,action=output:1
# ovs-ofctl add-flow br0 in_port=2,action=output:4
# ovs-ofctl add-flow br0 in_port=4,action=output:2

また、ホスト2のOVSは第7回で以下のように設定していました。これはPMDスレッドを2つの実行することを意味します。

# ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=0x6

一方、ポートは4つあります。どのPMDスレッドがどのポートをポーリングしているかは次のコマンド見ることができます。

# ovs-appctl dpif-netdev/pmd-rxq-show
pmd thread numa_id 0 core_id 1:
        isolated : false
        port: dpdkvhostuser0    queue-id: 0
        port: dpdkvhostuser1    queue-id: 0
pmd thread numa_id 0 core_id 2:
        isolated : false
        port: nic1      queue-id: 0
        port: nic2      queue-id:

上記の場合、1つのPMDがvhost-userの2つのポートを、他方のPMDが2つの物理NICをポーリングしています。負荷のバランスをとるため、どちらのPMDも1つのvhost-userと1つの物理NICをポーリングするようにします。

# ovs-vsctl set interface nic1 other_config:pmd-rxq-affinity="0:1"
# ovs-vsctl set interface nic2 other_config:pmd-rxq-affinity="0:2"
# ovs-vsctl set interface dpdkvhostuser0 other_config:pmd-rxq-affinity="0:1"
# ovs-vsctl set interface dpdkvhostuser1 other_config:pmd-rxq-affinity="0:2"
# ovs-appctl dpif-netdev/pmd-rxq-show
pmd thread numa_id 0 core_id 1:
        isolated : true
        port: dpdkvhostuser0    queue-id: 0
        port: nic1      queue-id: 0
pmd thread numa_id 0 core_id 2:
        isolated : true
        port: dpdkvhostuser1    queue-id: 0
        port: nic2      queue-id: 0

VM

VMでは以下のオプションでtestpmdを起動しました。

# testpmd -- -i --nb-core=2
(略)
# start

ホスト1

ホスト1ではこれまで通り、pktgenを起動して、rangeモードで使用します。


# app/app/x86_64-default-linuxapp-gcc/app/pktgen -- -m "1.0,2.1"
(略)
Pktget> range all on
(略)

結果

以下の表に計測結果を示します。一番右の列は第7回で取得したOVSで折り返した場合のスループットです。これに比べ、VMでパケットを折り返した場合、スループットは1/3程度になっています。virtio-netのオーバーヘッドによりスループットが低下していると考えられます。

パケット
サイズ

VM折り返し
(Gbps)

[参考] OVS折り返し
(Gbps)
64 3.20 9.00
128 5.63 16.1
256 9.81 20.0
512 15.2 未計測
1024 20.0 未計測