Docker 1.9 でマルチホストネットワーク機能を試した


何周遅れだよって話ではあるけど Docker 使い始めました。

さて、[【参考訳】 プロダクションに対応するマルチホスト Docker ネットワーク機能](http://pocketstudio.jp/log3/2015/11/04/docker-multi-host-networking-ga/)を参考にして試した。みんな「dockerのリンクはとても便利」と絶賛するけどどうも馴染まなかった自分には素晴らしい展開。

まずシングルホストでMySQLノードへと他のノードから接続させるテストをしたのち、マルチホストで同様の動作を試すことに。

動作環境は以下のとおりです。

- MacBook(2015) Mem 8GB
- OS X El Capitan
- virtualbox 5.0.8

メモリが足りない。

<追記>なお、この記事の上位互換の内容が @zembutsu さんによって[書かれている](http://qiita.com/zembutsu/items/4cd756ff8be4be76e5a3)のでより詳細な内容を知りたい場合はそちらを。</追記>

# Docker Toolboxのインストール

初心者なので公式インストーラを使います。

- https://www.docker.com/docker-toolbox

# シングルホストで試す
## docker networkを作る

```bash
$ docker network create backend
461b41ec193090776948e42bee70b64cdc46dfe59e8206e5bbf0cb49372d9589
```

## 必要なイメージを pull する

```
$ docker pull mysql
$ docker pull centos
```

## DBを作る

`db`というホスト名で、backend ネットワークに所属するコンテナを作成する。

```bash
$ docker run -d -e MYSQL_ROOT_PASSWORD=mysql --net=backend --name db mysql:5.7
3637731853a41c7c318c0f88d356005e48cf76806f360f0d5df8c217beaa544d
```

## もいっこCentOSを立てて、さっきのDBにアクセスする

同じネットワークに属したホスト同士はホスト名でお互いを解決できる。

ネットワーク名(backend)とホスト名(db)の組み合わせで名前解決できることがわかる。このあたりの動きは自然。なお、`/etc/hosts` を見ればわかるが同じネットワークに属しているのでホスト名だけでも解決できる。

```bash
$ docker run -it --net=backend centos
[root@1ea2906c98f0 /]# ping db.backend
PING db.backend (172.19.0.2) 56(84) bytes of data.
64 bytes from db (172.19.0.2): icmp_seq=1 ttl=64 time=0.153 ms
64 bytes from db (172.19.0.2): icmp_seq=2 ttl=64 time=0.080 ms
64 bytes from db (172.19.0.2): icmp_seq=3 ttl=64 time=0.085 ms
[root@1ea2906c98f0 /]# yum install -y mysql

.... 

[root@1ea2906c98f0 /]# mysql --version
mysql  Ver 15.1 Distrib 5.5.44-MariaDB, for Linux (x86_64) using readline 5.1

[root@1ea2906c98f0 /]# mysql -h db.backend -uroot -pmysql
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.9 MySQL Community Server (GPL)

Copyright (c) 2000, 2015, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MySQL [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.00 sec)

```	

ちゃんと`db.backend` という名前で MySQL 5.7.9 に接続できた!

```
[root@1ea2906c98f0 /]# cat /etc/hosts
172.19.0.3	1ea2906c98f0
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
172.19.0.2	db
172.19.0.2	db.backend
```
 
これら名前は `/etc/hosts` に書かれている。まあこのあたりは理解できる話。**しかし、この状態で mysql のコンテナを落とすと /etc/hosts に書かれた mysql のエントリも消える。** しかも、**新しく同じネットワークにコンテナを起動すると /etc/hosts にエントリが増える。**なんてことだ。やや感動した。

### <余談> MySQLサーバの方の /etc/hosts

こうなってた。

```
# cat /etc/hosts
172.19.0.2	3637731853a4
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
ff00::0	ip6-mcastprefix
ff02::1	ip6-allnodes
ff02::2	ip6-allrouters
#
```

centosがいないのはホスト名つけてないからかな。

### <余談>作成済みのコンテナを後からネットワークに所属させたり

コンテナ作成時に `--net=hogehoge` で指定するだけでなく、connect サブコマンドで後から所属させることができる。

```
$ docker network connect <コンテナ名> <ネットワーク名>
```

手元で試した際、`-p 80:80` で公開済みの Nginx コンテナを frontend ネットワークに所属させようとしたら、以下のように怒られたので一度ポートのバインド外す必要があったりとかしそう。このあたりは細かく見てない。ごめんなさい。

```
$ docker network connect frontend nginx_web
Error response from daemon: failed to create endpoint nginx_front on network frontend: Bind for 0.0.0.0:80 failed: port is already allocated
```


# マルチホストで試す
## swarm を作る

```
$ docker run swarm create

=> <SWARM_TOKEN>
```

このトークンを後から使うのでとっておく。

## ところでマルチホストネットワークにはクラスタ設定が必要だ

`--cluster-advertise`と`--cluster-store`の指定をした Docker Engine が必要である。

> To use multi-host networking you need to start your docker engines with --cluster-store and --cluster-advertise as indicated in the docker engine docs
https://docs.docker.com/swarm/networking/

[公式のサンプルコード](https://github.com/dave-tucker/docker-network-demos/blob/master/multihost-local.sh)には Consul を起動する手順が書かれている。

ここではとりあえずなので、このまんま Consul用の環境を一個作ってしまう。Consulも使ったことがないが、いいんだ。どう動いているかどうかを細かく理解するのは後回しである。後で見よう。

```bash
docker-machine create \
    -d virtualbox \
    mhl-consul-1

docker $(docker-machine config mhl-consul-1) run -d \
    -p "8500:8500" \
    -h "consul" \
    progrium/consul -server -bootstrap
```

https://docs.docker.com/swarm/discovery/ このへん見ると クラスタ情報を外部で保持する discovery には consul だけでなく `etcd` とか `zookeeper` も選択できるみたい。

## swarm master を作る

```bash
docker-machine create \
        -d virtualbox \
        --swarm \
        --swarm-master \
        --swarm-discovery token://${SWARM_TOKEN} \
        --engine-opt="cluster-store=consul://$(docker-machine ip mhl-consul-1):8500" \
        --engine-opt="cluster-advertise=eth1:0" \
        swarm-master
```

できたらこっちに環境を切り替える。`eval $(docker-machine env --swarm swarm-master)`する。


## swarm node を作る

```bash
$ docker-machine create \
    -d virtualbox \
    --swarm \
    --swarm-discovery token://${SWARM_TOKEN} \
    --engine-opt="cluster-store=consul://$(docker-machine ip mhl-consul-1):8500" \
    --engine-opt="cluster-advertise=eth1:0" \
    swarm-agent-00


$ docker-machine create \
    -d virtualbox \
    --swarm \
    --swarm-discovery token://${SWARM_TOKEN} \
    --engine-opt="cluster-store=consul://$(docker-machine ip mhl-consul-1):8500" \
    --engine-opt="cluster-advertise=eth1:0" \
    swarm-agent-01
```

確認。

```bash
$ docker-machine ls
NAME             ACTIVE   DRIVER       STATE     URL                         SWARM
default          -        virtualbox   Running   tcp://192.168.99.100:2376
mhl-consul-1     -        virtualbox   Running   tcp://192.168.99.101:2376
swarm-agent-00   -        virtualbox   Running   tcp://192.168.99.103:2376   swarm-master
swarm-agent-01   -        virtualbox   Running   tcp://192.168.99.104:2376   swarm-master
swarm-master     -        virtualbox   Running   tcp://192.168.99.102:2376   swarm-master (master)
```

子供達が増えてた。

## swarm-master に接続して状態を見る

swarm-master を普通のDockerエンジンのように扱って接続できる。`--swarm` を指定して接続すると、単独ノードではなくクラスタ全体を対象として操作することになる。

```
$ eval $(docker-machine env --swarm swarm-master)
```

あとは普通に docker コマンドが使える。

```
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

$ docker info
Containers: 4
Images: 3
Role: primary
Strategy: spread
Filters: health, port, dependency, affinity, constraint
Nodes: 3
 swarm-agent-00: 192.168.99.103:2376
  └ Containers: 1
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 1.021 GiB
  └ Labels: executiondriver=native-0.2, kernelversion=4.1.12-boot2docker, operatingsystem=Boot2Docker 1.9.0 (TCL 6.4); master : 16e4a2a - Tue Nov  3 19:49:22 UTC 2015, provider=virtualbox, storagedriver=aufs
 swarm-agent-01: 192.168.99.104:2376
  └ Containers: 1
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 1.021 GiB
  └ Labels: executiondriver=native-0.2, kernelversion=4.1.12-boot2docker, operatingsystem=Boot2Docker 1.9.0 (TCL 6.4); master : 16e4a2a - Tue Nov  3 19:49:22 UTC 2015, provider=virtualbox, storagedriver=aufs
 swarm-master: 192.168.99.102:2376
  └ Containers: 2
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 1.021 GiB
  └ Labels: executiondriver=native-0.2, kernelversion=4.1.12-boot2docker, operatingsystem=Boot2Docker 1.9.0 (TCL 6.4); master : 16e4a2a - Tue Nov  3 19:49:22 UTC 2015, provider=virtualbox, storagedriver=aufs
CPUs: 3
Total Memory: 3.064 GiB
Name: a1eae869d74b
```

3ノードが統括されている。
どうでもいいけど 3CPU、Total Memory 3GBはMacBookにはそれなりに深刻…。この他にConsulマシンがいるからね。

## ネットワークを作成する

ネットワークを表示してみる。

```
$ docker network create backend
cf756ea9e77cbabf1e3ba019b4e7afc8b8b4575bd565415e9c304397e23c0664

$ docker network ls
NETWORK ID          NAME                    DRIVER
ded21015a5b2        swarm-master/none       null
41558558d9be        swarm-master/host       host
cf756ea9e77c        backend                 overlay
4cbc614ed481        swarm-agent-01/none     null
e9aefb33dfd7        swarm-agent-01/host     host
d1435420f650        swarm-master/bridge     bridge
01cbc675564a        swarm-agent-00/bridge   bridge
9ea065803ecc        swarm-agent-00/none     null
8fbe7735ed4c        swarm-agent-00/host     host
d69c33adb66c        swarm-agent-01/bridge   bridge
```

各ホストごとのネットワークの他に、今作った backend という名のオーバーレイネットワークができてる。

### discovery を使わずに各エンジンを作っていると、オーバーレイネットワークの作成ができない。

```
$ docker network create backend
Error response from daemon: 500 Internal Server Error: failed to parse pool request for address space "GlobalDefault" pool "" subpool "": cannot find address space GlobalDefault (most likely the backing datastore is not configured)
```

このエラーが出た時は Docker エンジンが `cluster-advertise` と `cluster-store` の指定なしで作られちゃってる疑惑。`docker-machine inspect`で確認するんだ。

上の手順で Consul を立ててなかったりするとこれでこける。

## docker pull すると各ノードでダウンロードされる

各ノードでコンテナイメージは持たないとなわけだから、まあそうなるのか。

```bash
$ docker pull mysql
Using default tag: latest
swarm-agent-01: Pulling mysql:latest...
swarm-master: Pulling mysql:latest...
swarm-agent-00: Pulling mysql:latest...
$ docker pull centos
Using default tag: latest
swarm-agent-01: Pulling centos:latest... : downloaded
swarm-master: Pulling centos:latest... : downloaded
swarm-agent-00: Pulling centos:latest... : downloaded
```

全部 virtualbox　だと複数並列で pull するので、自宅回線的につらみがある。

```
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
swarm               latest              6b40fe7724bd        3 days ago          15.6 MB
mysql               latest              fb86ef4dd8b7        8 days ago          359.8 MB
centos              latest              e9fa5d3a0d0e        3 weeks ago         172.3 MB
```

swarm だと docker images コマンド叩けないと書かれた記事もあるが、現時点ではそんなこともない模様。

## シングルホストの時と同じようにコンテナを作って試す

ネットワーク「backend」に所属させる。

```bash
docker run -d -e MYSQL_ROOT_PASSWORD=mysql --net=backend --name db mysql
docker run -it --net=backend --name centos centos
```

swarm上だと `-d` でデーモン起動しないとまずいという記事も見たが、現時点ではフォアグラウンド実行も可能みたい。

別窓でログインして `docker ps` 打つと、ちゃんと異なるノードで起動しているのがわかる。mysqlは swarm-agent-00、 centosは swarm-agent-01。

```bash
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
6a2a862a4fac        centos              "/bin/bash"              39 seconds ago      Up 38 seconds                           swarm-agent-01/centos
33358a65ed35        mysql               "/entrypoint.sh mysql"   39 minutes ago      Up 39 minutes       3306/tcp            swarm-agent-00/db
```

CentOS側でmysql(maria)をインストールして、MySQLノードに接続する。

```
[root@6a2a862a4fac /]# yum install -y mysql
[root@6a2a862a4fac /]# mysql -uroot -hdb.backend -pmysql
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.9 MySQL Community Server (GPL)

Copyright (c) 2000, 2015, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]>
```

でけたー。ちゃんと MySQL5.7.9 につながる。
いい感じに「普通」につながる。

いったんはここまで。