概要
以前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はドキュメントがとても充実していて、サンプル実行ログ付きで解説されているので、何か試すときに何をすればいいか分からない、ということがあまりない。
- インストール関連
- CLIでの設定関連
有識者の方の書かれたslideやblogなども色々ある。
事前準備
SELinux、iptablesの無効化
SELinux、iptablesを無効にしておくとトラブルが少なくて済む。
yumリポジトリの設定
cassandraのインストールを行うためのリポジトリを設定する。
$ 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は書かない。
$ 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で動かすので、インストールしておく。
$ sudo yum -y install java-1.7.0-openjdk
zookeeper
nsdb用にzookeeperをインストールする。
$ 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をインストールする。
$ 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のインストールと設定
$ sudo yum -y install midonet-api
インストール後、web.xmlを編集する。
…
<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に設定する。
<context-param>
<param-name>auth-auth_provider</param-name>
<param-value>
org.midonet.api.auth.MockAuthService
</param-value>
</context-param>
tomcatをインストールする。
$ yum -y install tomcat
インストール後、コンテキスト設定ファイルを編集する。
<Context
path="/midonet-api"
docBase="/usr/share/midonet-api"
antiResourceLocking="false"
privileged="true"
/>
編集完了後、midonet-apiを起動する。
$ systemctl enable tomcat.service
$ systemctl start tomcat.service
midonet-cliのインストールと設定
epelをいれないとpython-httplib2とpython-eventletがみつからないため、epelを入れた上でpython-midonetclientをインストールする。
$ sudo yum -y install epel-release
$ sudo yum -y install python-midonetclient
CLIが読み込む設定ファイルを編集する。
[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
…
[zookeeper]
zookeeper_hosts = 192.168.122.111:2181
…
[cassandra]
servers = 192.168.122.111
replication_factor = 1
cluster = midonet
…
$ systemctl start midolman.service
この時点でmidonet 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をインストールする。
$ yum -y install docker
コンテナ起動
コンテナを起動し、netns内外に伸びるvethを生やした状態にする。
# !/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
# !/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を実行し、コンテナを作成。
# ./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
# ./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によって通信するが、トンネルの終端がある人であれば誰でも通信できてしまうため、勝手に知らない人と接続しないようにする機構。
$ 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を登録する。
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を作成する。
midonet> bridge create name testbridge
bridge0
midonet> list bridge
bridge bridge0 name testbridge state up
仮想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が仮想スイッチにつながっていることになり、通信可能になる。
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に追加されているのを確認
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に接続されたので、試しにコンテナ内から隣のコンテナに接続してみる。
$ 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プロダクトに比べて弱い可能性があり、今後の動向が気になるところ。