概要
Docker 1.9 で正式版になったマルチホストネットワーク機能を使ってみたのでまとめます。
この機能では、複数のホストにまたがる仮想ネットワークを構築でき、コンテナはどこのホスト上で動いているかに関わらず、仮想ネットワークを経由して他のコンテナと通信することができます。
今回は下図のように、2台のホスト上のコンテナが仮想ネットワークを通じて通信できるようにしました。
マルチホストネットワークの要件
- マルチホストネットワークを使うには、複数のホストにまたがる Key/Value ストアが必要になりますが(図の Consul がそれに相当)、これについては後述します
- ホストの Linux Kernel は 3.16 かそれ以降が必要になります
今回の検証環境
- Host: Ubuntu 15.10 (Kernel 4.2.0)
- Docker Version: 1.9.0
手順
Docker 1.9 のインストール
2台のホスト上で、それぞれ以下を実行します。
# Docker Engine の最新版をインストール
$ curl -sSL https://test.docker.com/ | sh
# Docker デーモンが動いていたら停止しておく。後で、デフォルトとは異なるパラメータを付けて開始するため。
$ sudo service docker stop
# docker コマンドを実行するたびに sudo を打つのが面倒なので、以下のコマンドを実行して再ログインする。
$ sudo usermod -aG docker <username>
Consul のインストールと起動
複数のホストにまたがる仮想ネットワークを作成する場合は、ホスト間でネットワークの情報を共有するために 分散Key/Value ストアのクラスターが必要になります。
Docker 1.9 では Consul, etcd, ZooKeeper が利用できるようですが、今回は Consul を使いました。Consul は、Vagrant や Packer を開発している HashiCorp が提供している Key/Value ストアです。
まずは、両方のホストで consul をダウンロード。
# ダウンロード
$ wget https://releases.hashicorp.com/consul/0.5.2/consul_0.5.2_linux_amd64.zip
# もし unzip コマンドが入ってなければインストールしておく
$ sudo apt-get install unzip
# consul を解凍してコピー
$ unzip consul_0.5.2_linux_amd64.zip
$ sudo cp consul /usr/local/bin/
Host1 側で consul をサーバーとして起動。
consul agent -server -bootstrap -data-dir=/tmp/consul -bind=100.74.14.78
Host2 側の consul をクラスターに参加させる。
consul agent -data-dir=/tmp/consul -bind=100.74.120.62 -join=100.74.14.78
別のターミナルから両ホストにSSH接続して、以下のコマンドを実行。クラスタが形成されたか確認しておきます。
$ consul members
Node Address Status Type Build Protocol DC
Ubuntu1 100.74.14.78:8301 alive server 0.5.2 2 dc1
Ubuntu2 100.74.120.62:8301 alive client 0.5.2 2 dc1
Docker デーモンの起動
Key/Value ストアのクラスターが構成できたので、それを指定して Docker デーモンを起動します。引数は以下になります。
- --cluster-store : Key/Value のクラスタ
- --cluster-advertise : 自分自身の IP/Port を指定します。
$ sudo docker daemon --cluster-store=consul://localhost:8500 --cluster-advertise=100.74.14.78:2376
$ sudo docker daemon --cluster-store=consul://localhost:8500 --cluster-advertise=100.74.120.62:2376
ネットワークの作成
また新しいターミナルを起動して、まずはデフォルトで作成されているネットワークを確認します。どちらも、bridge, null, host の3つのネットワークがあることが分かります。
$ docker network ls
NETWORK ID NAME DRIVER
3db3635076e3 bridge bridge
1f616c6c3c34 none null
e459697f7cb8 host host
$ docker network ls
NETWORK ID NAME DRIVER
a629ccb5fbba bridge bridge
2802d9ae3067 none null
42b1f2f3d2bd host host
ここで、Host1 側だけで 新しいネットワークを作成します。
$ docker network create -d overlay TestNetwork
60231ea6adf2c36cafe68ddc5ee0548fbae034a812b988c3e018b41b1cc66278
すると、Host1 と Host2 の両方で、今作成した TestNetwork というネットワークが見えます。
$ docker network ls
NETWORK ID NAME DRIVER
60231ea6adf2 TestNetwork overlay
3db3635076e3 bridge bridge
1f616c6c3c34 none null
e459697f7cb8 host host
$ docker network ls
NETWORK ID NAME DRIVER
60231ea6adf2 TestNetwork overlay
a629ccb5fbba bridge bridge
2802d9ae3067 none null
42b1f2f3d2bd host host
コンテナの作成とコンテナ間の通信
仮想ネットワークが作成できたので、いよいよネットワーク上にコンテナを作成します。
# コンテナを起動
$ docker run -it --net=TestNetwork --name=Container1 ubuntu /bin/bash
# コンテナの中で ifconfig を実行してIPアドレスを確認
root@27f699b0d97c:/# ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:0a:00:00:02
inet addr:10.0.0.2 Bcast:0.0.0.0 Mask:255.255.255.0
inet6 addr: fe80::42:aff:fe00:2/64 Scope:Link
(中略)
eth1 Link encap:Ethernet HWaddr 02:42:ac:12:00:02
inet addr:172.18.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe12:2/64 Scope:Link
(中略)
# コンテナを起動
$ docker run -it --net=TestNetwork --name=Container2 ubuntu /bin/bash
# コンテナの中で ifconfig を実行してIPアドレスを確認
root@6b4baee6e4d0:/# ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:0a:00:00:03
inet addr:10.0.0.3 Bcast:0.0.0.0 Mask:255.255.255.0
inet6 addr: fe80::42:aff:fe00:3/64 Scope:Link
(中略)
eth1 Link encap:Ethernet HWaddr 02:42:ac:12:00:02
inet addr:172.18.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe12:2/64 Scope:Link
(中略)
どちらのコンテナにも 172.18.0.2 という IP のほかに、10.0.0.x という IP が割り当たっています。この 10.0.0.x が、仮想ネットワークに接続されている IP アドレスになります。
ping での通信
では、Container1(10.0.0.2) から Container2(10.0.0.3) に通信してみます。IP でもコンテナ名でも Ping が送れることが分かります。
# IP で ping を送る
root@27f699b0d97c:/# ping 10.0.0.3
PING 10.0.0.3 (10.0.0.3) 56(84) bytes of data.
64 bytes from 10.0.0.3: icmp_seq=1 ttl=64 time=1.11 ms
64 bytes from 10.0.0.3: icmp_seq=2 ttl=64 time=0.806 ms
・・・
# コンテナ名で ping を送る
root@27f699b0d97c:/# ping Container2
PING Container2 (10.0.0.3) 56(84) bytes of data.
64 bytes from Container2 (10.0.0.3): icmp_seq=1 ttl=64 time=0.661 ms
64 bytes from Container2 (10.0.0.3): icmp_seq=2 ttl=64 time=1.61 ms
・・・
# コンテナ名.ネットワーク名 で ping を送る
root@27f699b0d97c:/# ping Container2.TestNetwork
PING Container2.TestNetwork (10.0.0.3) 56(84) bytes of data.
64 bytes from Container2 (10.0.0.3): icmp_seq=1 ttl=64 time=1.00 ms
64 bytes from Container2 (10.0.0.3): icmp_seq=2 ttl=64 time=0.767 ms
・・・
HTTP での通信
次に HTTP で通信してみます。
まず、Container1 内に HTTP サーバーを入れます。
# apache のインストールと開始
root@27f699b0d97c:/# apt-get install apache2
root@27f699b0d97c:/# service apache2 start
# HTML ファイルの作成しておく
root@27f699b0d97c:/# echo "Hello, I'm Container1." > /var/www/html/test.html
Container2 から HTTP サーバーにアクセスすると、ちゃんと応答が返ってきます。
# コンテナ名で HTTP リクエストを送る
root@6b4baee6e4d0:/# curl http://Container1/test.html
Hello, I'm Container1.
マルチホストネットワーク機能を使わない場合は、docker run コマンドの -p, -P オプションでポートの公開をする必要がありましたが、マルチホストネットワークではそれも不要のようです。