#8 QEMU/KVMをvhostuserでOpen vSwitchに接続
はじめに
前回は、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に見えます。
具体的な追加コマンドは以下のとおりです。
# 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回のそれと同じです。
ホスト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つの実行することを意味します。
一方、ポートは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折り返し |
[参考] OVS折り返し (Gbps) |
64 | 3.20 | 9.00 |
128 | 5.63 | 16.1 |
256 | 9.81 | 20.0 |
512 | 15.2 | 未計測 |
1024 | 20.0 | 未計測 |