Docker : マルチホスト間での仮想ネットワーク


概要

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台のホスト上で、それぞれ以下を実行します。

Host1&Host2
# 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 をダウンロード。

Host1&Host2
# ダウンロード
$ 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 をサーバーとして起動。

Host1
consul agent -server -bootstrap -data-dir=/tmp/consul -bind=100.74.14.78

Host2 側の consul をクラスターに参加させる。

Host2
consul agent -data-dir=/tmp/consul -bind=100.74.120.62 -join=100.74.14.78

別のターミナルから両ホストにSSH接続して、以下のコマンドを実行。クラスタが形成されたか確認しておきます。

Host1&Host2
$ 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 を指定します。
Host1
$ sudo docker daemon --cluster-store=consul://localhost:8500 --cluster-advertise=100.74.14.78:2376
Host2
$ sudo docker daemon --cluster-store=consul://localhost:8500 --cluster-advertise=100.74.120.62:2376

ネットワークの作成

また新しいターミナルを起動して、まずはデフォルトで作成されているネットワークを確認します。どちらも、bridge, null, host の3つのネットワークがあることが分かります。

Host1
$ docker network ls
NETWORK ID          NAME                DRIVER
3db3635076e3        bridge              bridge
1f616c6c3c34        none                null
e459697f7cb8        host                host
Host2
$ docker network ls
NETWORK ID          NAME                DRIVER
a629ccb5fbba        bridge              bridge
2802d9ae3067        none                null
42b1f2f3d2bd        host                host

ここで、Host1 側だけで 新しいネットワークを作成します。

Host1
$ docker network create -d overlay TestNetwork
60231ea6adf2c36cafe68ddc5ee0548fbae034a812b988c3e018b41b1cc66278

すると、Host1 と Host2 の両方で、今作成した TestNetwork というネットワークが見えます。

Host1
$ docker network ls
NETWORK ID          NAME                DRIVER
60231ea6adf2        TestNetwork         overlay
3db3635076e3        bridge              bridge
1f616c6c3c34        none                null
e459697f7cb8        host                host
Host2
$ docker network ls
NETWORK ID          NAME                DRIVER
60231ea6adf2        TestNetwork         overlay
a629ccb5fbba        bridge              bridge
2802d9ae3067        none                null
42b1f2f3d2bd        host                host

コンテナの作成とコンテナ間の通信

仮想ネットワークが作成できたので、いよいよネットワーク上にコンテナを作成します。

Host1
# コンテナを起動
$ 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
          (中略)
Host2
# コンテナを起動
$ 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 が送れることが分かります。

Container1
# 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 サーバーを入れます。

Container1
# 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 サーバーにアクセスすると、ちゃんと応答が返ってきます。

Container2
# コンテナ名で HTTP リクエストを送る
root@6b4baee6e4d0:/# curl http://Container1/test.html
Hello, I'm Container1.

マルチホストネットワーク機能を使わない場合は、docker run コマンドの -p, -P オプションでポートの公開をする必要がありましたが、マルチホストネットワークではそれも不要のようです。

参考サイト