Lagopus 0.2 をqemu/kvm with virtio-netで動かす (dpdk 2.1.0 + Lagopus 0.2)

#Lagopus 0.2をqemu/kvm with virtio-netで動かす

本記事では

* Ubuntu14をインストールしたQemuにdpdk,ryu,Lagopusをインストールして
* pingの疎通を
 * netnsを使用してHypervisor側のlinux側から確認する
 * 他のQemuのVMと接続して確認する

までの手順を説明する。

#前回との相違点
[Lagopusをqemu/kvm with virtio-netで動かす (dpdk 1.7.1/lagopus 0.1.2)
](http://qiita.com/s1061123/items/ffa3974e4635c06faa26)とは以下の点が異なる。

* virtio-netのパッチ ([lagopus_fix_virtio_tx_mtu_02.diff](http://s1061123.net/lagopus_fix_virtio_tx_mtu_02.diff))
* /usr/local/etc/lagopus/lagopus.conf の書式
* lagopus起動時のオプション ('-b'オプションを追加)

#環境
以下の環境で動作を確認した。

* Hypervisor
 * Linux kernel 4.0.5
 * qemu 2.1.2
* GuestOS (VM)
 * Ubuntu 14.04 (amd64)
 * lagopus (github, master:6cd6660e2d5e7ce79e713aa4de9ab32efa011905)
 * dpdk 2.1.0
 * Ryu 3.25 (pipでインストール, python 2.7.6)


#前準備
ここではUbuntu14をインストールしたQemuのVMにLagopusをインストールするまでを説明する。
まず始めにUbuntu14をインストールしたQemuを用意する。

ただし、qemuの起動時に以下の設定が必要となるので注意

* ```-cpu host```をつける
* ```-smp cores={コア数},threads=1```の指定
 * (coresとthreadsを指定しないとlagopusが起動できない可能性がある為)
* 各ネットワークポートのMAC指定(全て異なるMACにすること)
* 確認する方法によって異なる```-netdev```の設定
    * netnsの場合にはtapを使用する
    * 他のvmの場合にはsocketを使用する

参考に著者の起動時のoptionをここに示す。

```lang:shell
#(netnsを使用して確認する場合)
cpu=3
mem=4000
qemu=qemu-system-x86_64
image=lagopus_sw.qcow2
boot=c

${qemu} -cpu host -smp ${cpu},cores=${cpu},threads=1 -numa node \
        -m ${mem} ${snapshot} \
        -rtc base=utc -usbdevice tablet -monitor stdio \
        -drive file=${image},if=virtio -boot ${boot} -vnc ${vnc} \
        -enable-kvm -L /usr/share/qemu  \
        -device virtio-net-pci,netdev=net0,mac=52:54:00:12:34:56 \
        -device virtio-net-pci,netdev=net1,mac=52:54:01:12:34:56 \
        -device virtio-net-pci,netdev=net2,mac=52:54:02:12:34:56 \
        -netdev bridge,id=net0,br=br0 \
        -netdev socket,id=net1,listen=:5001 \
        -netdev socket,id=net2,listen=:5002
```

```lang:shell
#(他のQemuのVMを使用して確認する場合)
cpu=3
mem=4000
qemu=qemu-system-x86_64
image=lagopus_sw.qcow2
boot=c

${qemu} -cpu host -smp ${cpu},cores=${cpu},threads=1 -numa node \
        -m ${mem} ${snapshot} \
        -rtc base=utc -usbdevice tablet -monitor stdio \
        -drive file=${image},if=virtio -boot ${boot} -vnc ${vnc} \
        -enable-kvm -L /usr/share/qemu  \
        -device virtio-net-pci,netdev=net0,mac=52:54:00:12:34:56 \
        -device virtio-net-pci,netdev=net1,mac=52:54:01:12:34:56 \
        -device virtio-net-pci,netdev=net2,mac=52:54:02:12:34:56 \
        -netdev bridge,id=net0,br=br0 \
        -netdev tap,id=net1,ifname=tap11,script=no \
        -netdev tap,id=net2,ifname=tap12,script=no 
```

#Ubuntuのアップデート・設定変更

まずLagopusの導入に必要なパッケージをインストールする。

```lang:shell
sudo apt-get -y update
sudo apt-get -y install unzip build-essential libexpat1-dev libgmp-dev libncurses5-dev libssl-dev libpcap-dev byacc flex libreadline-dev python-dev python-pastedeploy python-paste python-twisted git python-setuptools python-pip libxml2-dev libxslt1-dev ethtool
```

Lagopus/dpdkで必要となるhugepageの設定をgrubに対して行ない、反映させるために再起動する。

```lang:/etc/default/grub
GRUB_CMDLINE_LINUX="hugepages=256"
```

```lang:shell
sudo update-grub
sudo reboot
```

#dpdkのインストール
ここではdpdkの1.7.1をインストールする。

```lang:shell
wget -O - http://dpdk.org/browse/dpdk/snapshot/dpdk-2.1.0.tar.xz | tar xvfpJ -
cd dpdk-2.1.0/
make install T=x86_64-native-linuxapp-gcc
```

#lagopusのインストール
ここではlagopusをインストールする。release-0.2ブランチで確認した。

現在のlagopusではvirtio-netでの動作をサポートしていない[^1][^2]ので、公式にpatchを当てるか著者の対応済みのgithubをクローンする必要がある。
以下のどちらかの手段でもいいのでlagopusをダウンロードする。

```lang:shell
# 公式gitにpatchを当てる方法
git clone https://github.com/lagopus/lagopus.git
cd lagopus
git checkout release-0.2.0
wget -O - http://s1061123.net/lagopus_fix_virtio_tx_mtu_02.diff | patch -p1
```

その後，先程コンパイルしたdpdkを指定してlagopusをコンパイルする。

```lang:shell
./configure --with-dpdk-dir=${HOME}/dpdk-2.1.0
make 
sudo make install
```

[^1]: Pull Requestは提出済み (https://github.com/lagopus/lagopus/pull/46)
[^2]: Pull Requestは提出済み (https://github.com/lagopus/lagopus/pull/47)

##lagopus.confの作成
以下のconfigを作成して，```/usr/local/etc/lagopus.conf```として保存する。

###注意
ここのeth0,eth1はlagopusのために用意するポートの数だけ用意する。ここでのeth0,eth1はlagopusで管理されるポートであるのでLinuxから見えるeth0,eth1とは違うポートを指していることに注意。ここの例ではlagopusのVMは3ポートを持ち，内2 ポートをlagopusのスイッチ用ポートとして使うことにしている。

* Linuxのeth0
* Linuxのeth1 -> Lagopusのeth0になる (この時点でLinuxから見えなくなる)
* Linuxのeth2 -> Lagopusのeth1になる

```/usr/local/etc/lagopus.conf
channel channel01 create -dst-addr 127.0.0.1 -protocol tcp

controller controller01 create -channel channel01 -role equal -connection-type main

interface interface01 create -type ethernet-dpdk-phy -device eth0 -port-number 0

interface interface02 create -type ethernet-dpdk-phy -device eth1 -port-number 1

port port01 create -interface interface01

port port02 create -interface interface02

bridge bridge01 create -controller controller01 -port port01 1 -port port02 2 -dpid 0x1
bridge bridge01 enable
```

#ryuのインストール
lagopusを動かす際にOpenFlow ControllerとしてRyuを使用するのでそれもインストールする。
ryuのinstall後にsixのパッケージをupgradeすることが今のところ必要らしい。

```
pip install ryu
pip install six --upgrade
```

#lagopusを起動する
lagopusを動かす際(再起動後も含む)，lagopusだけでなくdpdkのドライバのインストールやOpenFlowコントロー ラの起動が必要になる。
ここではそれらを説明する。

##dpdk(lagopus)対象のPCIポートの確認
まずlagopusとして動かしたいポートが属するPCIのポートを知る必要がある。```dpdk-1.7.1/tools/dpdk_nic_bind.py --status```を実行してどのポートか確認する。

```lang:shell
$ ./tools/dpdk_nic_bind.py --status

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

Network devices using kernel driver
===================================
0000:00:03.0 'Virtio network device' if= drv=virtio-pci unused=igb_uio
0000:00:04.0 'Virtio network device' if= drv=virtio-pci unused=igb_uio
0000:00:05.0 'Virtio network device' if= drv=virtio-pci unused=igb_uio

Other network devices
=====================
<none>
```
この場合，一番上のport(linuxのeth0)は外部接続向けのポートなのでそれ以外の0000:00:04.0, 0000:00:05.0の2つが対象となる。

##dpdkドライバのインストール
dpdkドライバのインストール用のスクリプトを下からコピーしておく。その際に先程確認したPCIのポートを```DPDK_NIC_PCIS```に記入すること。

```install-dpdk.sh
#!/bin/sh
# This script is derived from handson training resources.
# http://lagopus.github.io/handson/handson.tar.xz
export RTE_SDK=${HOME}/dpdk-2.1.0
export RTE_TARGET=x86_64-native-linuxapp-gcc

DPDK_NIC_PCIS="0000:00:04.0 0000:00:05.0"
HUGEPAGE_NOPAGES="256"

set_numa_pages()
{
        for d in /sys/devices/system/node/node? ; do
                sudo sh -c "echo ${HUGEPAGE_NOPAGES} > $d/hugepages/hugepages-2048kB/nr_hugepages"
        done
}

set_no_numa_pages()
{
        sudo sh -c "echo ${HUGEPAGE_NOPAGES} > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages"
}

# install module
sudo modprobe uio
sudo insmod ${RTE_SDK}/${RTE_TARGET}/kmod/igb_uio.ko
sudo insmod ${RTE_SDK}/${RTE_TARGET}/kmod/rte_kni.ko

# unbind e1000 NICs from igb and bind igb_uio for DPDK
sudo ${RTE_SDK}/tools/dpdk_nic_bind.py --bind=igb_uio  ${DPDK_NIC_PCIS}
sudo ${RTE_SDK}/tools/dpdk_nic_bind.py --status

# mount fugepagefs
echo "Set hugepagesize=${HUGEPAGE_NOPAGES} of 2MB page"
NCPUS=$(find /sys/devices/system/node/node?  -maxdepth 0 -type d | wc -l)

if [ ${NCPUS} -gt 1 ] ; then
        set_numa_pages
else
        set_no_numa_pages
fi

echo "Creating /mnt/huge and mounting as hugetlbfs"
sudo mkdir -p /mnt/huge
grep -s '/mnt/huge' /proc/mounts > /dev/null
if [ $? -ne 0 ] ; then
        sudo mount -t hugetlbfs nodev /mnt/huge
fi

unset RTE_SDK
unset RTE_TARGET
```

install-dpdk.shを実行しdpdkドライバをインストールする。
"Network devices using DPDK-compatible driver"に対象のポートが追加されればよい。

```
user@packer-ubuntu-1404-server:~$ ./install-dpdk.sh 
[sudo] password for user: 

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= drv=virtio-pci unused=igb_uio 

Other network devices
=====================
<none>
Set hugepagesize=256 of 2MB page
Creating /mnt/huge and mounting as hugetlbfs
```

##lagopusの起動
以下のコマンドでlagopusを起動させる。Lagopusに認識させたくないポート(この場合はeth0)がある場合には'-b'オプションを使ってblacklist化しないとLagopusが起動しない(正確にはdpdkがエラー)するので注意。

```lang:shell
sudo lagopus -d -- -c3 -n1 -b 00:03.0 -- -p3
```

##ryuの起動
ここではryuのサンプルにあるsimple switchを起動する。

```lang:shell
ryu-manager --verbose
/usr/local/lib/python2.7/dist-packages/ryu/app/simple_switch_13.py
```
しばらくすると"move onto main mode"というメッセージがでる。
これでlagopusはRyuと連携して簡単なL2 Switchとなった。

#動作を確認する

##netnsを使用してHypervisorからpingを確認
確認する場合は2つのターミナルから以下のコマンドを打つことで疎通確認が可能である。
(両方のifconfigを全て実行した後にpingを打つこと)

このコマンドでは

* c1, c2というnetwork namespaceを作成し
* そこにtap11, tap12をc1,c2と別のnamespaceに所属させた後
* c1, c2の中で/bin/bashを起動する

ことを行なっている。

```lang:shell
#(A-side)
sudo ip netns add c1
sudo ip link set tap11 netns c1
sudo ip netns exec c1 /bin/bash
sudo ifconfig tap11 192.168.1.1/24
ping 192.168.1.2
```

```lang:shell
#(B-side)
sudo ip netns add c2
sudo ip link set tap12 netns c2
sudo ip netns exec c2 /bin/bash
sudo ifconfig tap12 192.168.1.2/24
ping 192.168.1.1
```

##他のQemuのvmからpingを確認
他のQemuのvmを使用して確認する場合は各OSで同じサブネットのIPを振ることで疎通確認が可能である。
その場合のQemuの起動オプションは以下の通り。

```lang:shell
#(A-side)
qemu-system-x86_64  ...必要なオプション...
        -device virtio-net-pci,netdev=net1,mac=52:54:11:12:34:56 \
        -netdev socket,id=net1,connect=127.0.0.1:5001
```
```lang:shell
#(B-side)
qemu-system-x86_64  ...必要なオプション...
        -device virtio-net-pci,netdev=net1,mac=52:54:21:12:34:56 \
        -netdev socket,id=net1,connect=127.0.0.1:5002
```

#まとめ
本記事ではlagopusをubuntu14を入れたqemu(virtio-net)で動作させることについて紹介した。

##謝辞
このようなソフトを作っているlagopus開発者チームの方、またlagopusのhandsonを企画した方に感謝致します。