docker networkでマルチホストネットワークを試してみた(docker-1.9.0)

##概要
docker 1.9.0がリリースされ、docker networkコマンドがproductionでも使えるようになりました。特にマルチホストでのコンテナネットワークがdockerコマンドから作成可能になったので試してみました。

##`docker network`コマンドとは
`docker network`はコンテナに接続するネットワークを操作・管理するコマンド。
Docker Swarmと連携すればマルチホスト環境でも、コンテナ間でoverlayネットワークを構築してコンテナ間が通信できる。
マルチホストのコンテナネットワークを実現するツールや方法は他にもいろいろある。`weave`、`etcd+flannel`、`runcher`、`bridge接続+GRE`などなど。


環境
--
今回は2つのDockerホストVMをSwarmでクラスタ化し、docker networkコマンドでoverlayネットワークを構築して、マルチホストでのコンテナ通信ができるかを試しました。

あとdocker networkコマンドによるoverlayネットワークはLinux Kernel 3.16以上が必要になります。CentOS7は3.10なので、3.18をビルドしてインストールしています。（kernelビルド部分は省略）Ubuntu 14.10のカーネルは3.16なので、Ubuntuで試せばよかった。

* CentOS7(VM) x2台
 * docker01 :192.168.11.48 (consul server)
 * docker02 :192.168.11.49 (consul client)
* Linux Kernel 3.18
* Docker 1.9.0
* Docker Swarm 1.0
* consul 0.5.2(Docker SwarmのバックエンドKVS)

やったこと目次
--
1. consulのインストール・設定
1. Docker1.9.0のインストール・設定
1. Docker Swarmでクラスタ化
1. overlay networkの作成
1. コンテナにネットワークを接続して起動
1. コンテナ間の疎通確認


consulインストール・設定
--
**インストール**
バイナリファイルをダウンロードして配置するだけ。合わせて必要な設定ファイル、ディレクトリを作成します。

```docker01_&_docker02
$ wget https://releases.hashicorp.com/consul/0.5.2/consul_0.5.2_linux_amd64.zip
$ unzip consul_0.5.2_linux_amd64.zip
$ sudo cp consul /usr/local/bin/
$ sudo mkdir -p {/etc/consul,/var/lib/consul,/var/log/consul}
```

**consul設定**
consulの設定ファイルをserverとclientの2種類作成します。

docker01(consul server)

```json:/etc/consul/consul.json
{
  "datacenter": "docker",
  "addresses" : {
    "http": "0.0.0.0"
  },
  "bind_addr": "192.168.11.48",
  "node_name": "docker01",
  "domain": "consul",
  "server": true,
  "bootstrap_expect": 1,
  "data_dir": "/var/lib/consul"
}
```

docker02(consul client)

```json:/etc/consul/consul.json
{
  "datacenter": "docker",
  "addresses" : {
    "http": "0.0.0.0"
  },
  "bind_addr": "192.168.11.49",
  "node_name": "docker02",
  "domain": "consul",
  "retry_join": [ "192.168.11.48"],
  "server": false,
  "data_dir": "/var/lib/consul"
}
```

**consul起動**

```bash:docker01&docker02
$ consul -config-file=/etc/consul/consul.json >> /var/log/consul/consul.log &
$ consul members
Node      Address             Status  Type    Build  Protocol  DC
docker02  192.168.11.49:8301  alive   client  0.5.2  2         docker
docker01  192.168.11.48:8301  alive   server  0.5.2  2         docker
```
ちゃんとconsulクラスタにdocker01,02がいますね。


Docker 1.9.0インストール・設定
--
**インストール**
CentOS7はまだyumにあるdockerが1.8.2なので(2015/11/9現在)、最新のバイナリをダウンロードして挿し替えています。

```bash:docker01&docker02
$ sudo yum install -y docker
$ curl -o docker-1.9.0 https://get.docker.com/builds/Linux/x86_64/docker-1.9.0
$ sudo cp docker-1.9.0 /usr/bin/docker
$ sudo chmod +x /usr/bin/docker
```

**Docker Engine設定**
docker daemonの`--cluster-store` `--cluster-advertise`オプションを設定して起動します。それぞれKVSのアドレス、ホスト自身の`ipaddress:port`を設定します。

```ini:/etc/sysconfig/docker(docker01&docker02)
OPTIONS='--cluster-store=consul://localhost:8500 --cluster-advertise=<ホストIP>:2376 -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock'
```

**Docker起動**

```bash:docker01&docker02
$ sudo systemctl start docker
```

Docker Swarmクラスタ起動
--
docker01のホストでSwarmのクラスタマネージャコンテナを起動します。

```bash:docker01
$ docker run -d -p 2377:2375 --name swarm-manage-consul swarm manage consul://192.168.11.48:8500/docker
```

各ホストでSwarmクラスタへjoinするためのswarmコンテナを起動します。`docker`コマンドがswarmマネージャコンテナに向けて実行できればOKです。

```bash:docker01&docker02
# docker01
$ docker run -d --name swarm-join swarm join --advertise=192.168.11.48:2375 consul://192.168.11.48:8500/docker
# docker02
$ docker run -d --name swarm-join swarm join --advertise=192.168.11.49:2375 consul://192.168.11.48:8500/docker
$ docker -H tcp://192.168.11.48:2377 info
Containers: 3
Images: 4
Role: primary
Strategy: spread
Filters: health, port, dependency, affinity, constraint
Nodes: 2
 centos7-01: 192.168.11.48:2375
  └ Containers: 2
  └ Reserved CPUs: 0 / 2
  └ Reserved Memory: 0 B / 2.052 GiB
  └ Labels: executiondriver=native-0.2, kernelversion=3.18.22, operatingsystem=CentOS Linux 7 (Core), storagedriver=overlay
 centos7-02: 192.168.11.49:2375
  └ Containers: 1
  └ Reserved CPUs: 0 / 2
  └ Reserved Memory: 0 B / 2.052 GiB
  └ Labels: executiondriver=native-0.2, kernelversion=3.18.22, operatingsystem=CentOS Linux 7 (Core), storagedriver=overlay
CPUs: 4
Total Memory: 4.103 GiB
Name: 755cb58b105c
```

ここまでで`docker network`コマンドでoverlayネットワークを作成する準備ができました。いよいよ次はoverlayネットワーク作成します。

overlay networkの作成
--
`docker network create`コマンドでoverlayネットワークを作成します。
1つのホストで作成したvn01のネットワーク設定はクラスタの全ホストで共有されます。`docker inspect`コマンドで作成したコンテナネットワーク情報が見れます。

```bash:docker01&docker02
# docker01
$ docker network ls
NETWORK ID          NAME                DRIVER
6f9ee8d629d6        none                null                
0f0d0f12a59f        host                host                
12613b8ef4b7        docker_gwbridge     bridge              
0ac1dafaa764        bridge              bridge 

$ docker network create -d overlay vn01 
b07f8c4a5cb954e7ff8d387bfc65e3239f67852ee65f68cea8d77e9032b577c5

$ docker network ls
NETWORK ID          NAME                DRIVER
b07f8c4a5cb9        vn01                overlay             
0ac1dafaa764        bridge              bridge              
6f9ee8d629d6        none                null                
0f0d0f12a59f        host                host                
12613b8ef4b7        docker_gwbridge     bridge 

$ docker network inspect vn01
[
    {
        "Name": "vn01",
        "Id": "b07f8c4a5cb954e7ff8d387bfc65e3239f67852ee65f68cea8d77e9032b577c5",
        "Scope": "global",
        "Driver": "overlay",
        "IPAM": {
            "Driver": "default",
            "Config": [
                {}
            ]
        },
        "Containers": {},
        "Options": {}
    }
]
# docker02
$ docker network ls
NETWORK ID          NAME                DRIVER
b07f8c4a5cb9        vn01                overlay             
65a8ead71e7c        none                null                
ebb12613053e        host                host                
02ca2b4c8d98        docker_gwbridge     bridge              
071afc5c93e3        bridge              bridge  
```

コンテナへのネットワーク割り当て
--
test01コンテナを作って、vn01ネットワークを付与させてみます。

```bash:docker01
$ docker run -idt --name test01 --hostname test01 centos:7 /bin/bash
$ docker network connect vn01 test01
$ docker exec test01 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
40: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:acff:fe11:2/64 scope link 
       valid_lft forever preferred_lft forever
43: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP 
    link/ether 02:42:0a:00:00:02 brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.2/24 scope global eth1
       valid_lft forever preferred_lft forever
    inet6 fe80::42:aff:fe00:2/64 scope link 
       valid_lft forever preferred_lft forever
```

test01のコンテナの`eth1`に`10.0.0.2/24`のネットワークが付いてますね。IPアドレスは自動で割り振られるのかな？


コンテナ起動時に`--net`オプションを使うことでも付与できるみたいです。

```bash:dcoker01
$ docker run -dit --name test01 --hostname test01 --net vn01 centos:7 /bin/bash
```


同様に別のホストでもvn01を付与したコンテナを起動してネットワークを付与します。

```bash:docker02
$ docker run -dit --name test02 --hostname test02 centos:7 /bin/bash
$ docker network connect vn01 test02 
$ docker exec test02 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
39: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:acff:fe11:2/64 scope link 
       valid_lft forever preferred_lft forever
42: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP 
    link/ether 02:42:0a:00:00:03 brd ff:ff:ff:ff:ff:ff
    inet 10.0.0.3/24 scope global eth1
       valid_lft forever preferred_lft forever
    inet6 fe80::42:aff:fe00:3/64 scope link 
       valid_lft forever preferred_lft forever
```
こっちは`10.0.0.3/24`のネットワークがついてますね。

コンテナ間通信
--
やっとコンテナの疎通確認！test02(10.0.0.3)コンテナからtest01コンテナ(10.0.02)へpingを打ってみます。

```
$ docker exec test02 ping -c 4 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.991 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.639 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.650 ms
64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=0.664 ms

--- 10.0.0.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 0.639/0.736/0.991/0.147 ms
```
つながりました！

どうやらコンテナ名で名前解決もできるみたい。便利！

```bash:docker02
$ docker exec test02 ping -c 4 test01
PING test01 (10.0.0.2) 56(84) bytes of data.
64 bytes from test01 (10.0.0.2): icmp_seq=1 ttl=64 time=0.516 ms
64 bytes from test01 (10.0.0.2): icmp_seq=2 ttl=64 time=0.665 ms
64 bytes from test01 (10.0.0.2): icmp_seq=3 ttl=64 time=0.745 ms
64 bytes from test01 (10.0.0.2): icmp_seq=4 ttl=64 time=0.714 ms

--- test01 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 0.516/0.660/0.745/0.087 ms
```

静的IPは振れない(?)みたいですが、名前解決できるので問題なさそう。
いろいろオプションがあるみたいですがまだ試せていません…。
コンテナネットワークを外部に公開できる機能があれば最高なのになぁ。`weave expose`みたいな。まだないのかな？それとも外部接続用コンテナ作ってそこはブリッジ接続かな。

あとは性能がどんなもんかですね。技術的にはvxlanでoverlayネットワークを作成しているみたいですが、そのオーバーヘッドで性能どうなるか私気になります！


参考
--
* [Get started with multi-host networking](http://docs.docker.com/engine/userguide/networking/get-started-overlay/)