OpenVNetとDockerを組み合わせてみるデモ（分散vna）


概要
----

* OpenVNetをdockerとあわせて動かしてみる。
* 今回もまだ既存ネットワークとは通信なしでVNET間もルーティングはしないが、前回のものを拡張し、複数ホストのDockerコンテナ間で仮想ネットワークを構成。

前提
----

[ほぼ前回の記事](http://qiita.com/qb0c80aE/items/8bdd22d104b06657fd89)と同じだが、

* redisをすべてのホストから見えるようにしていたり
* vnaをそれぞれのホストで構成したり
* public networkを作ったり
* VMからbroadcastしてホストから出る時にunicastに変換される際のMACを定義したり

といった点が異なる。

Dockerイメージは引き続きcentos_sshdを利用する。

### 環境

CentOS6.5のVM2台をホスト（Host1、Host2とする）として、同一セグメント内に配置する。
※このCentOSを動かしているHypervisorはKVM

また、それぞれのホストにNICは2枚あり、

* Host1：
  * eth0：ssh、OpenVNetの管理で利用（192.168.122.50）
  * eth1：OpenVNetでPublic Network用に利用
* Host2：
  * eth0：ssh、OpenVNetの管理で利用（192.168.122.51）
  * eth1：OpenVNetでPublic Network用に利用

となっている。

作業の流れ
---------

今回も[InstallGuite](https://github.com/axsh/openvnet/blob/master/docs/InstallGuide.md) に則って大体インストールしていくが、

* Host1はAll-in-One構成（vnmgr、webapi、vnaすべてが同じホストにある）
* Host2はvnaだけインストール
* Host1の上にVNET1に属するContainer1と、VNET2に属するContainer2を起動
* Host2の上にVNET1に属するContainer3と、VNET2に属するContainer4を起動

という形態で動作させて、それぞれのVNET内のコンテナ同士が疎通できるか確かめる。
※Host1、2を動かしているHypervisorはKVM

で動かす。

ネットワークに関しては、前回同様docker0は放っておいてbr0をOVSでつくり、それを利用する形にする。コンテナのNIC追加は、pipeworkは使わず、netnsにvethを放り込む形にする。

2つのホストは同一セグメントに存在し、MAC2MACで隣のホストのコンテナと通信ができれば良いので、Routerの設定は不要。

### OpenVNetのインストール（Host1）

Host1のインストール自体は前回と大体同様だが、インストール後にいくつか設定を直すところがある。

```/etc/redis.conf
（省略）
bind 0.0.0.0
（省略）
```
redisは他のvnaからも参照されるため、外から接続可能にしておく必要がある。
※mysqlとredisはvnmgrと同じホストに存在する必要はなく、外部に置いた場合はcommon.confで接続先を変更すれば良いが、mysqlはlocalhost以外から接続可能なようにユーザを設定する必要がある。

```/etc/openvnet/vna.conf
node {
  id "vna1"
  addr {
    protocol "tcp"
    host "192.168.122.51"
    port 9103
  }
}
```

vna.confのポイントはidで、名前の通り一意にしておく必要がある。

### OpenVNetのインストール（Host2）

Host2はvnaだけインストールすればいい。全部インストールした上でvnaしか使わない、としても良い。
こちらも設定を一部修正する。

```/etc/openvnet/vna.conf
node {
  id "vna2"
  addr {
    protocol "tcp"
    host "192.168.122.51"
    port 9103
  }
}
```
同様に、idをバッティングしない値に変更しておく。

### br0の作成

ここも前回と同じように、eth1をbr0にOVSPortとして追加する形で設定する。
Host1のbr0とHost2のbr0のdatapath-id、hwaddrはバッティングしない値にしておく。

```bash:/etc/sysconfig/network-scripts/ifcfg-eth1（Host1、2共通）
DEVICE=eth1
DEVICETYPE=ovs
TYPE=OVSPort
OVS_BRIDGE=br0
BOOTPROTO=none
ONBOOT=yes
HOTPLUG=no
```

```bash:/etc/sysconfig/network-scripts/ifcfg-br0（Host1）
DEVICE=br0
DEVICETYPE=ovs
TYPE=OVSBridge
ONBOOT=yes
BOOTPROTO=static
IPADDR=192.168.121.50
NETMASK=255.255.255.0
HOTPLUG=no
OVS_EXTRA="
 set bridge     ${DEVICE} protocols=OpenFlow10,OpenFlow12,OpenFlow13 --
 set bridge     ${DEVICE} other_config:disable-in-band=true --
 set bridge     ${DEVICE} other-config:datapath-id=0000000000000001 --
 set bridge     ${DEVICE} other-config:hwaddr=02:01:00:00:00:01 --
 set-fail-mode  ${DEVICE} standalone --
 set-controller ${DEVICE} tcp:127.0.0.1:6633
"
```

```bash:/etc/sysconfig/network-scripts/ifcfg-br0（Host2）
DEVICE=br0
DEVICETYPE=ovs
TYPE=OVSBridge
ONBOOT=yes
BOOTPROTO=static
IPADDR=192.168.121.51
NETMASK=255.255.255.0
HOTPLUG=no
OVS_EXTRA="
 set bridge     ${DEVICE} protocols=OpenFlow10,OpenFlow12,OpenFlow13 --
 set bridge     ${DEVICE} other_config:disable-in-band=true --
 set bridge     ${DEVICE} other-config:datapath-id=0000000000000002 --
 set bridge     ${DEVICE} other-config:hwaddr=02:01:00:00:00:02 --
 set-fail-mode  ${DEVICE} standalone --
 set-controller ${DEVICE} tcp:127.0.0.1:6633
"
```

設定が完了したらそれぞれのホストのbr0を起動して、Host1ではvnmgr・webapi・vnaを、Host2ではvnaを起動する。

```bash:Host1
$ service mysqld start
$ service redis start
$ ifup eth1
$ initctl start vnet-vnmgr
$ initctl start vnet-webapi
$ initctl start vnet-vna
```

```bash:Host2
$ ifup eth1
$ initctl start vnet-vna
```
### コンテナの起動

Host1ではContainer1と2、Host2ではContainer3と4を起動する。

```bash:openvnet-demo-init.sh（Host1）
#!/bin/sh

BRIDGE=br0
DOCKER_PID=`ps -ef | grep docker | grep -v grep |  awk '{print $2}'`
DOCKER_IMAGE="centos_sshd"
IP_ADDRESSES=("10.102.0.10/24" "10.102.0.10/24")

rm -f ./netconfig.env
mkdir -p /var/run/netns/

for i in `seq 1 ${#IP_ADDRESSES[@]}`; do
  let address_index=$i-1
  CONTAINER_ID=`docker run --hostname="container${i}" --net="none" -i -t -d ${DOCKER_IMAGE} /bin/bash`
  BASH_PID=`docker inspect --format {{.State.Pid}} ${CONTAINER_ID}`

  ln -s /proc/${BASH_PID}/ns/net /var/run/netns/${BASH_PID}

  ip link add veth${i}-1 type veth peer name veth${i}-2
  ip link set veth${i}-1 up
  ip link set veth${i}-2 netns ${BASH_PID}
  ip netns exec ${BASH_PID} ip link set veth${i}-2 up
  ip netns exec ${BASH_PID} ip addr add ${IP_ADDRESSES[${address_index}]} dev veth${i}-2

  ovs-vsctl add-port ${BRIDGE} veth${i}-1

  hwaddr=`ip netns exec ${BASH_PID} ip link show veth${i}-2 | awk 'NR==2' | awk '{print $2}'`
  ipaddr=`echo "${IP_ADDRESSES[${address_index}]}" | awk -F/ '{print $1}'`
  netmask=`echo "${IP_ADDRESSES[${address_index}]}" | awk -F/ '{print $2}'`
  echo "veth${i}-2 ${IP_ADDRESSES[${address_index}]} ${hwaddr}"
  echo "export mac_veth${i}2=${hwaddr}" >> ./netconfig.env
  echo "export ip_veth${i}2=${ipaddr}" >> ./netconfig.env
  echo "export netmask_veth${i}2=${netmask}" >> ./netconfig.env
done
```

```bash:openvnet-demo-init.sh（Host2）
#!/bin/sh

BRIDGE=br0
DOCKER_PID=`ps -ef | grep docker | grep -v grep |  awk '{print $2}'`
DOCKER_IMAGE="centos_sshd"
IP_ADDRESSES=("10.102.0.11/24" "10.102.0.11/24")

rm -f ./netconfig.env
mkdir -p /var/run/netns/

for i in `seq 1 ${#IP_ADDRESSES[@]}`; do
  let address_index=$i-1
  let j=$i+2
  CONTAINER_ID=`docker run --hostname="container${j}" --net="none" -i -t -d ${DOCKER_IMAGE} /bin/bash`
  BASH_PID=`docker inspect --format {{.State.Pid}} ${CONTAINER_ID}`

  ln -s /proc/${BASH_PID}/ns/net /var/run/netns/${BASH_PID}

  ip link add veth${j}-1 type veth peer name veth${j}-2
  ip link set veth${j}-1 up
  ip link set veth${j}-2 netns ${BASH_PID}
  ip netns exec ${BASH_PID} ip link set veth${j}-2 up
  ip netns exec ${BASH_PID} ip addr add ${IP_ADDRESSES[${address_index}]} dev veth${j}-2

  ovs-vsctl add-port ${BRIDGE} veth${j}-1

  hwaddr=`ip netns exec ${BASH_PID} ip link show veth${j}-2 | awk 'NR==2' | awk '{print $2}'`
  ipaddr=`echo "${IP_ADDRESSES[${address_index}]}" | awk -F/ '{print $1}'`
  netmask=`echo "${IP_ADDRESSES[${address_index}]}" | awk -F/ '{print $2}'`
  echo "veth${j}-2 ${IP_ADDRESSES[${address_index}]} ${hwaddr}"
  echo "export mac_veth${j}2=${hwaddr}" >> ./netconfig.env
  echo "export ip_veth${j}2=${ipaddr}" >> ./netconfig.env
  echo "export netmask_veth${j}2=${netmask}" >> ./netconfig.env
done
```

シェルスクリプトをそれぞれのホストで実行すると、以下のような結果が出力される。

```bash:Host1
$ ./openvnet-demo-init.sh
veth1-2 10.102.0.10/24 32:92:dd:d4:b0:c4
veth2-2 10.102.0.10/24 a6:06:59:b2:99:da
```

```bash:Host2
$ ./openvnet-demo-init.sh
veth3-2 10.102.0.11/24 ca:9c:32:da:37:65
veth4-2 10.102.0.11/24 96:3e:bf:1d:ee:0a
```

netconfig.envの中身は、以下のようになる。

```bash:netconfig.env（Host1）
export mac_veth12=32:92:dd:d4:b0:c4
export ip_veth12=10.102.0.10
export netmask_veth12=24
export mac_veth22=a6:06:59:b2:99:da
export ip_veth22=10.102.0.10
export netmask_veth22=24
```

```bash:netconfig.env（Host2）
export mac_veth32=ca:9c:32:da:37:65
export ip_veth32=10.102.0.11
export netmask_veth32=24
export mac_veth42=96:3e:bf:1d:ee:0a
export ip_veth42=10.102.0.11
export netmask_veth42=24
```

### OpenVNetのAPIで仮想ネットワーク設定

webapiを利用して仮想ネットワーク設定を行う。
今回のdb.shは以下のようになる。

```bash:db.sh（Host1）
#!/bin/sh

set -e
set -x

mysqladmin -f -uroot drop vnet
mysqladmin -uroot create vnet
(cd /opt/axsh/openvnet/vnet; bundle exec rake db:init)

export vnmgr_host=192.168.122.50
export vnmgr_port=9090

# 上記のコンテナを作成するシェルスクリプトで出力される、
# コンテナのNIC情報を出力したファイル
. ./netconfig.env

# datapaths
curl -X POST \
--data-urlencode uuid=dp-node1 \
--data-urlencode display_name="vna1" \
--data-urlencode dpid="0x0000000000000001" \
--data-urlencode node_id="vna1" \
http://${vnmgr_host}:${vnmgr_port}/api/datapaths

# networks
curl -s -X POST \
--data-urlencode uuid=nw-pub \
--data-urlencode display_name="nw-pub" \
--data-urlencode ipv4_network="192.168.121.0" \
--data-urlencode ipv4_prefix="24" \
--data-urlencode network_mode="physical" \
http://${vnmgr_host}:${vnmgr_port}/api/networks

curl -X POST \
--data-urlencode uuid=nw-vnet1 \
--data-urlencode display_name="nw-vnet1" \
--data-urlencode ipv4_network="10.100.0.0" \
--data-urlencode ipv4_prefix="24" \
--data-urlencode network_mode=