LoginSignup
9
11

More than 5 years have passed since last update.

MidoNetとDockerをOpenStackなしで組み合わせてみるデモ

Last updated at Posted at 2015-08-01

概要

以前OpenVNetで行った、マルチホスト上のDockerコンテナを仮想ネットワークで繋ぐデモを、MidoNetで行う。

その前に、MidoNetとは?

ミドクラさんが開発しているネットワーク仮想化ソフトウェア。
http://www.midokura.com/midonet/
2014年にオープンソースになり、OpenStack Neutronととても親和性が高く(というより、Neutronバックエンド標準となるべく開発が進められている)、プラグインが提供されている。
サポート、GUI、便利機能などが入った商用のMidokura Enterprise MidoNetが元。

ありがたいことに、こちらにとても詳しい記事がある。

前提

MidoStackを使う方法は割と見かけるが、OpenStack(というかneutron)が出てくると、場合によっては一気にややこしく感じることがあるようなので、今回は純粋にMidoNetそのものとdockerだけを使う形で構成する。
keystoneがないので、midonet-apiの認証についてはMockAuthServiceを利用する。

なお、midonet apiはそのうち無くなる可能性があり、neutron api経由だけになる(つまり、MidoNetだけを使うとしてもkeystoneとneutronが必要になる)かもしれないので、neutron慣れしていない場合は、apiが移行する前に慣れておくと良い事がありそう。
また、現在MidoNetはlibnetworkに手を加えて(libnetworkのdriverとして?)dockerに対応する方向で動いているらしいので、こんなことをしなくてもよくなるはず。

環境

CentOS 7 minimalのホストを2台(midonet1、midonet2)
冗長化が目的ではないので、midonet1にNSDB、midonet-api、midonet-cli、midolmanを、midonet2にはmidolmanだけをインストールして使う。
実際に利用する際はNSDBのクラスタリングなどをしっかり考慮すること。

midonet1

  • CentOS 7
  • メモリ4G
  • eth0:192.168.122.111
  • zookeeper
  • cassandra
  • midolman
  • midonet-api
  • python-midonet-client

midonet2

  • CentOS 7
  • メモリ4G
  • eth0:192.168.122.112
  • midolman

参考ドキュメント

MidoNetはドキュメントがとても充実していて、サンプル実行ログ付きで解説されているので、何か試すときに何をすればいいか分からない、ということがあまりない。

有識者の方の書かれたslideやblogなども色々ある。

事前準備

SELinux、iptablesの無効化

SELinux、iptablesを無効にしておくとトラブルが少なくて済む。

yumリポジトリの設定

cassandraのインストールを行うためのリポジトリを設定する。

datastax.repo(midonet1)
$ cat > /etc/yum.repos.d/datastax.repo <<EOF
[datastax]
name= DataStax Repo for Apache Cassandra
baseurl=http://rpm.datastax.com/community
enabled=1
gpgcheck=0
EOF

MidoNetのインストールを行うためのリポジトリを設定する。
OpenStackなしで使ってみるため、openstack-integrationは書かない。

midonet.repo(midonet1、midonet2)
$ cat > /etc/yum.repos.d/midonet.repo <<EOF
[midonet]
name=MidoNet
baseurl=http://repo.midonet.org/midonet/v2015.01/RHEL/7/stable/
enabled=1
gpgcheck=1
gpgkey=http://repo.midonet.org/RPM-GPG-KEY-midokura

[midonet-misc]
name=MidoNet 3rd Party Tools and Libraries
baseurl=http://repo.midonet.org/misc/RHEL/7/misc/
enabled=1
gpgcheck=1
gpgkey=http://repo.midonet.org/RPM-GPG-KEY-midokura
EOF

各種インストール

openjdk-1.7.0のインストール

midolmanはjava7で動かすので、インストールしておく。

java-1.7.0-openjdkインストール(midonet1、midonet2)
$ sudo yum -y install java-1.7.0-openjdk

zookeeper

nsdb用にzookeeperをインストールする。

zookeeperインストールと動作確認(midonet1)
$ sudo yum -y install zookeeper
$ mkdir -p /usr/java/default/bin/
$ ln -s /usr/lib/jvm/jre-1.7.0-openjdk/bin/java /usr/java/default/bin/java
$ echo "server.1=192.168.122.111:2888:3888" >> /etc/zookeeper/zoo.cfg
$ echo 1 > /var/lib/zookeeper/data/myid
$ sudo systemctl start zookeeper.service
$ ss -napt | grep 2181
$ sudo yum -y install nc
$ echo ruok | nc 127.0.0.1 2181

cassandra

nsdb用にcassandraをインストールする。

cassandraインストールと動作確認(midonet1)
$ sudo yum -y install dsc20
$ sed -ri "s/Test Cluster/midonet/g" /etc/cassandra/conf/cassandra.yaml
$ sed -ri "s/localhost/192.168.122.111/g" /etc/cassandra/conf/cassandra.yaml
$ sed -ri "s/127\.0\.0\.1/192.168.122.111/g" /etc/cassandra/conf/cassandra.yaml
$ rm -rf /var/lib/cassandra/data/system/
$ sudo systemctl restart cassandra.service
$ ss -napt | grep 9160
$ cassandra-cli -h 192.168.122.111 -p 9160

midonet-apiのインストールと設定

midonet-apiのインストール(midonet1)
$ sudo yum -y install midonet-api

インストール後、web.xmlを編集する。

/usr/share/midonet-api/WEB-INF/web.xmlの編集(midonet1)
<context-param>
    <param-name>rest_api-base_uri</param-name>
    <param-value>http://192.168.122.111:8080/midonet-api</param-value>
</context-param><context-param>
    <param-name>zookeeper-zookeeper_hosts</param-name>
    <param-value>192.168.122.111:2181</param-value>
</context-param><context-param>
    <param-name>midobrain-properties_file</param-name>
    <param-value>/var/lib/tomcat/webapps/host_uuid.properties</param-value>
</context-param>

ここを参考に、今回は認証をMockAuthServiceに設定する。

/usr/share/midonet-api/WEB-INF/web.xmlの編集(midonet1)
  <context-param>
    <param-name>auth-auth_provider</param-name>
    <param-value>
      org.midonet.api.auth.MockAuthService
    </param-value>
  </context-param>

tomcatをインストールする。

tomcatのインストール(midonet1)
$ yum -y install tomcat

インストール後、コンテキスト設定ファイルを編集する。

/etc/tomcat/Catalina/localhost/midonet-api.xmlの編集(midonet1)
<Context
    path="/midonet-api"
    docBase="/usr/share/midonet-api"
    antiResourceLocking="false"
    privileged="true"
/>

編集完了後、midonet-apiを起動する。

midonet-apiの起動(midonet1)
$ systemctl enable tomcat.service
$ systemctl start tomcat.service

midonet-cliのインストールと設定

epelをいれないとpython-httplib2とpython-eventletがみつからないため、epelを入れた上でpython-midonetclientをインストールする。

epel-release、python-midonetclientのインストール(midonet1)
$ sudo yum -y install epel-release
$ sudo yum -y install python-midonetclient

CLIが読み込む設定ファイルを編集する。

~/.midonetrcの編集(midonet1)
[cli]
api_url = http://192.168.122.111:8080/midonet-api
username = admin
password = ADMIN_PASS
project_id = admin

midolmanのインストールと設定

各ホストにmidolmanをインストールする。

$ sudo yum install -y midolman
/etc/midolman/midolman.conf(midonet1、midonet2)

[zookeeper]
zookeeper_hosts = 192.168.122.111:2181

[cassandra]
servers = 192.168.122.111
replication_factor = 1
cluster = midonet

midolman起動
$ systemctl start midolman.service

この時点でmidonet datapathができている。

datapathの確認
$ ip a
…
3: midonet: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN 
    link/ether 5e:e6:d8:3f:59:52 brd ff:ff:ff:ff:ff:ff

Dockerコンテナの起動

dockerインストール

dockerをインストールする。

dockerインストール
$ yum -y install docker

コンテナ起動

コンテナを起動し、netns内外に伸びるvethを生やした状態にする。

コンテナ作成shell(init.sh:midonet1)
#!/bin/sh

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

for i in `seq 1 ${#IP_ADDRESSES[@]}`; do
  let address_index=$i-1
  CONTAINER_ID=`docker run --hostname="container1${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
done
コンテナ作成shell(init.sh:midonet2)
#!/bin/sh

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

for i in `seq 1 ${#IP_ADDRESSES[@]}`; do
  let address_index=$i-1
  CONTAINER_ID=`docker run --hostname="container2${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
done

上記shellを実行し、コンテナを作成。

コンテナ作成とコンテナ内外のvethの確認(midonet1)
# ./init.sh
$ ip a
…
3: midonet: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN 
    link/ether 02:1a:d8:e1:e3:75 brd ff:ff:ff:ff:ff:ff
…
8: veth1-1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65000 qdisc pfifo_fast master midonet state UP qlen 1000
    link/ether 8e:22:8d:91:0e:af brd ff:ff:ff:ff:ff:ff
    inet6 fe80::8c22:8dff:fe91:eaf/64 scope link 
       valid_lft forever preferred_lft forever
$ docker ps
CONTAINER ID        IMAGE                COMMAND             CREATED             STATUS              PORTS               NAMES
5ecaa2bc140c        centos_sshd:latest   "/bin/bash"         5 hours ago         Up 5 hours                              compassionate_darwin   
$ docker exec -it 5ec ip a
…
7: veth1-2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 72:e7:96:78:cf:09 brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.10/24 scope global veth1-2
       valid_lft forever preferred_lft forever
    inet6 fe80::70e7:96ff:fe78:cf09/64 scope link 
       valid_lft forever preferred_lft forever
コンテナ作成とコンテナ内外のvethの確認(midonet2)
# ./init.sh
$ ip a
…
3: midonet: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN 
    link/ether c2:65:9e:ca:30:9e brd ff:ff:ff:ff:ff:ff
…
14: veth1-1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65000 qdisc pfifo_fast master midonet state UP qlen 1000
    link/ether a6:0d:94:11:98:de brd ff:ff:ff:ff:ff:ff
    inet6 fe80::a40d:94ff:fe11:98de/64 scope link 
       valid_lft forever preferred_lft forever
$ docker ps
CONTAINER ID        IMAGE                COMMAND             CREATED             STATUS              PORTS               NAMES
efb4579d636a        centos_sshd:latest   "/bin/bash"         5 hours ago         Up 5 hours                              ecstatic_hopper     
$ docker exec -it efb ip a
…
13: veth1-2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 02:29:49:b5:34:30 brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.11/24 scope global veth1-2
       valid_lft forever preferred_lft forever
    inet6 fe80::29:49ff:feb5:3430/64 scope link 
       valid_lft forever preferred_lft forever

仮想ネットワーク設定

コンテナが繋ぐ仮想ネットワークを作成する。

tunnel zoneの作成

まず最初にtunnel zoneを作る。tunnel zoneとは、MidoNetは離れたcompute node上のインタフェースとはflow-based tunnelingによって通信するが、トンネルの終端がある人であれば誰でも通信できてしまうため、勝手に知らない人と接続しないようにする機構。

tunnel-zone作成
$ midonet-cli 
midonet> create tunnel-zone name testtz type gre
tzone0
midonet> list tunnel-zone
tzone tzone0 name testtz type gre

tunnel zoneにホスト(midolman)を登録

tunnel zoneに参加できるmidolmanを登録する。

tunnel-zoneにホストを登録
midonet> list host
host host0 name midonet1 alive true
host host1 name midonet2 alive true
midonet> tunnel-zone tzone0 add member host host0 address 192.168.122.111
zone tzone0 host host0 address 192.168.122.111
midonet> tunnel-zone tzone0 add member host host1 address 192.168.122.112
zone tzone0 host host1 address 192.168.122.112

仮想bridgeの作成

仮想的なbridgeを作成する。

仮想bridgeの作成
midonet> bridge create name testbridge
bridge0
midonet> list bridge
bridge bridge0 name testbridge state up

仮想bridgeにポートを作成

作った仮想bridgeにポートを作成する。

仮想bridgeにポートを作成
midonet> bridge bridge0 add port
bridge0:port0
midonet> bridge bridge0 add port
bridge0:port1
midonet> bridge bridge0 list port
port port1 device bridge0 state up
port port0 device bridge0 state up

作成したportに実際のインタフェースをbind

仮想的なportがどのVMのportに該当するのかを定義する。
2つマッピングすれば、その時点で2つのVMが仮想スイッチにつながっていることになり、通信可能になる。

作成したportに実際のインタフェースをbind
midonet> host host0 list interface
…
iface midonet host_id host0 status 0 addresses [] mac 02:1a:d8:e1:e3:75 mtu 1500 type Virtual endpoint DATAPATH
…
iface veth1-1 host_id host0 status 3 addresses [u'fe80:0:0:0:8c22:8dff:fe91:eaf'] mac 8e:22:8d:91:0e:af mtu 65000 type Virtual endpoint DATAPATH
…
midonet> host host1 list interface
…
iface midonet host_id host1 status 0 addresses [] mac c2:65:9e:ca:30:9e mtu 1500 type Virtual endpoint DATAPATH
…
iface veth1-1 host_id host1 status 3 addresses [u'fe80:0:0:0:a40d:94ff:fe11:98de'] mac a6:0d:94:11:98:de mtu 65000 type Virtual endpoint DATAPATH
…
midonet> 
midonet> host host0 add binding port bridge0:port0 interface veth1-1
host host0 interface veth1-1 port bridge0:port0
midonet> host host1 add binding port bridge0:port1 interface veth1-1
host host1 interface veth1-1 port bridge0:port1
midonet> host host0 list binding
host host0 interface veth1-1 port bridge0:port0
midonet> host host1 list binding
host host1 interface veth1-1 port bridge0:port1

veth1-1がmidonet datapathに追加されているのを確認

veth1-1がmidonet datapathに追加されているのを確認
mm-dpctl --show-dp midonet
Datapath name   : midonet
Datapath index : 3
Datapath Stats: 
  Flows :0
  Hits  :225
  Lost  :3
  Misses:39
Port #0 "midonet"  Internal  Stats{rxPackets=0, txPackets=0, rxBytes=0, txBytes=0, rxErrors=0, txErrors=0, rxDropped=0, txDropped=0}
Port #1 "tngre-overlay"  Gre  Stats{rxPackets=23, txPackets=30, rxBytes=4459, txBytes=4273, rxErrors=0, txErrors=0, rxDropped=0, txDropped=0}
Port #2 "tnvxlan-overlay"  VXLan  Stats{rxPackets=0, txPackets=0, rxBytes=0, txBytes=0, rxErrors=0, txErrors=0, rxDropped=0, txDropped=0}
Port #3 "tnvxlan-vtep"  VXLan  Stats{rxPackets=0, txPackets=0, rxBytes=0, txBytes=0, rxErrors=0, txErrors=0, rxDropped=0, txDropped=0}
Port #4 "veth1-1"  NetDev  Stats{rxPackets=31, txPackets=23, rxBytes=4099, txBytes=4459, rxErrors=0, txErrors=0, rxDropped=0, txDropped=0}

疎通確認

ここまでで各ホストのコンテナのvethが仮想ブリッジbridge0に接続されたので、試しにコンテナ内から隣のコンテナに接続してみる。

container1からcontainer2にssh
$ docker ps
CONTAINER ID        IMAGE                COMMAND             CREATED             STATUS              PORTS               NAMES
5ecaa2bc140c        centos_sshd:latest   "/bin/bash"         4 hours ago         Up 4 hours                              compassionate_darwin   
$ docker exec -it 5ec bash
[root@container11 /]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
7: veth1-2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 72:e7:96:78:cf:09 brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.10/24 scope global veth1-2
       valid_lft forever preferred_lft forever
    inet6 fe80::70e7:96ff:fe78:cf09/64 scope link 
       valid_lft forever preferred_lft forever
[root@container11 /]# ssh 10.0.0.11
The authenticity of host '10.0.0.11 (10.0.0.11)' can't be established.
RSA key fingerprint is 62:07:3d:c7:f4:3f:bb:45:48:e8:45:85:42:33:39:28.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '10.0.0.11' (RSA) to the list of known hosts.
[root@container21 ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
13: veth1-2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 02:29:49:b5:34:30 brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.11/24 scope global veth1-2
       valid_lft forever preferred_lft forever
    inet6 fe80::29:49ff:feb5:3430/64 scope link 
       valid_lft forever preferred_lft forever
[root@container21 ~]# 

問題なく接続できた。

tips(多少上でも書いてるところがある)

  • midonet-cliで、正しい構文で実行しているのに「Syntax Error」と言われることがある?
    • これはmidonet-cliが自分から状態を取得しにいっているわけではないためらしく、一回list系で取得してから再度設定系コマンドを実行すると直ったりする。
  • tunnel zone?
    • MidoNetは離れたcompute node上のインタフェースとはflow-based tunnelingによって通信するが、トンネルの終端がある人であれば誰でも通信できてしまうため、勝手に知らない人と接続しないようにする機構がtunnel zone。
  • GREがdropされる環境ではどうすれば?
    • tunnel zoneにはvxlanも利用できるので、vxlanを使ってみると良い。
  • python-midonet-clientのインストールでエラーになる
    • epelが有効になっていないとエラーになるので、その可能性をあたってみる。
  • zookeeperはDataStaxのものを入れる?
    • midonet-miscから入るのが正解。
  • java8でも動く?
    • midolmanは起動時にjava7を要求しているので、7を入れること。
    • cassandraやzookeeperと共存環境にするとjava7と8が同時に入ったりするが、midolmanは7である必要がある。
  • midolmanが起動しない?
    • メモリ不足でcassandraが動いていない可能性がある。MidoNetは全体で結構なメモリを消費するので、この点は注意。
    • lsmodでopenvswitchが存在しない場合はmodprobeする。
    • それぞれのmidolmanのバージョンが違うと起動に失敗するので、ホスト間でバージョンは合わせる。
  • midolmanがNAT下の場合、Private IPをhostとして登録すると、Public IPである相手のtxDropが増加して返答返ってこないし、NATされたあとのPublic IPをhostとして登録すると送出時にtxErrorが出るんだけど?
    • そもそもそういう使い方をするケースがあんまりない気がするが、Private IPで登録しておいて、相手の方でPrivate IPに送出(返送)する場合はPublic IPにDNAT、とすれば疎通はできるようになる(SoftLayerのPublic IPとAWS VPCのPrivate IPの間で確認済み)。

その他

Docker自身もリポジトリを見ている限りではおそらく1.9?でsocketplaneをベースにした仮想ネットワーク機能が利用可能になりそうだが、socketplaneなどコンテナ付属の仮想ネットワークは仮想トポロジ構築が中心で、ネットワーク機能はMidoNetなどのSDNプロダクトに比べて弱い可能性があり、今後の動向が気になるところ。

9
11
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
11