IDCFクラウドのDebian JessieでMulti-Host Docker Networking - Part 1: Overlayネットワークの作成

　Docker 1.9でプロダクションになったMulti-Host Docker Networkingを使ってDockerホストのクラスタをセットアップしていきます。[Ambari on Docker](https://github.com/sequenceiq/docker-ambari)やDocker SwarmとDocker Composeを使ったデプロイ環境の構築が目的です。

　IDCFクラウドでようやく利用できるようになったDebian Jessieを利用してみます。今回は手動で設定をしていますが、[Manage the cloud with Terraform, Salt, Consul and DigitalOcean](http://ahus1.github.io/saltconsul-examples/tutorial.html)のように[Salt](https://docs.saltstack.com/en/latest/)で構成管理をする予定です。

## このシリーズ

* [IDCFクラウドのDebian JessieでMulti-Host Docker Networking - Part 1: Overlayネットワークの作成](http://qiita.com/masato/items/6a1fa797b6c25d4c5745)
* [IDCFクラウドのDebian JessieでMulti-Host Docker Networking - Part 2: Docker Swarmクラスタの作成](http://qiita.com/masato/items/a047798e48ccc1e56eb8)
* [IDCFクラウドのDebian JessieでMulti-Host Docker Networking - Part 3: OverlayネットワークとDocker SwarmとDocker ComposeでRethinkDBをスケールさせる](http://qiita.com/masato/items/bd3d50b31f16a00eac47)


## 参考

　以下のサイトを参考にしました。

* [Multi-Host Docker Networking is now ready for production](https://blog.docker.com/2015/11/docker-multi-host-networking-ga/)

* [マルチホストDockerネットワーキング（正式版 native overlay）](http://qiita.com/nmatsui/items/2811f2297343e1d50739)

* [Docker1.9のマルチホストネットワーク](https://thinkit.co.jp/article/8414)

* [Docker Swarm in Debian Jessie with multi-networking](http://spartatek.se/meteor_blog/docker/2015/11/18/docker-swarm-in-debian-jessy-with-multi-network.html)

* [Manage the cloud with Terraform, Salt, Consul and DigitalOcean](http://ahus1.github.io/saltconsul-examples/tutorial.html)

## 仮想マシンの用意

　IDCFクラウドの[クラウドコンソール](http://www.idcf.jp/cloud/spec/config.html)から仮想マシンを3台作成します。プライベートIPアドレスはわかりやすいように連番で作成します。

|Dockerホストのhostname|Dockerホストのeth0|
|:--|:--|
|minion-1|10.3.0.101|
|minion-2|10.3.0.102|
|minion-3|10.3.0.103|


　Debianのバージョンを確認します。

```bash
$ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 8.2 (jessie)
Release:        8.2
Codename:       jessie
```

　[libnetwork](https://github.com/docker/libnetwork)のoverlayドライバはLinux Kernel 3.16以上が必要です。Debian 8.2のカーネルは3.16なのでこのまま利用することができます。

```bash
$ uname -r
3.16.0-4-amd64
```

　`minion[1-3]`の仮想マシンにConsulとDockerをインストールしていきます。

## Consul

　overlayドライバのメタデータを保存する分散KVSに[Consul](https://www.consul.io/)を使います。以下の作業はroot権限で実行します。

### インストール

　執筆時点で最新の`0.6.0`をインストールします。

```bash
$ curl -LO https://releases.hashicorp.com/consul/0.6.0/consul_0.6.0_linux_amd64.zip
$ unzip consul_0.6.0_linux_amd64.zip
$ mv consul /usr/local/bin
$ consul version
Consul v0.6.0
Consul Protocol: 3 (Understands back to: 1)
```

　Consulを実行するの`consul`ユーザーを作成します。

```bash
$ useradd consul
```

### 設定ファイル

　Consulの設定ファイルを配置するディレクトリを作成します。データを保存する`/var/consul`ディレクトリのオーナーを`consul`に変更します。

```bash
$ mkdir /var/consul /etc/consul.d /etc/sysconfig
$ chown consul:consul /var/consul
```

　Debian用の環境設定ファイルを作成します。`minion-1`の場合`-advertise`をeth0の`10.3.0.101`に設定します。`minion-2`と`minon-3`はそれぞれのIPアドレスを設定します。

```basg:/etc/default/consul
GOMAXPROCS=2
CMD_OPTS="agent -advertise 10.3.0.101 -config-dir=/etc/consul.d -data-dir=/var/consul"
```

　Consulの設定ファイルはJSON形式で以下のように記述します。`bootstrap_expect`、`retry_join`にConsulクラスタを構成するノードのIPアドレスを設定して3台が参加するまで起動を待機します。

```json:/etc/consul.d/config.json
{
  "bootstrap_expect": 3,
  "server": true,
  "data_dir": "/var/consul",
  "log_level": "INFO",
  "enable_syslog": false,
  "retry_join": ["10.3.0.101", "10.3.0.102", "10.3.0.103"],
  "client_addr": "0.0.0.0"
}
```


### systemd

　Jessieではデフォルトのinitとして[systemd](http://freedesktop.org/wiki/Software/systemd/)が採用されています。Consulの設定ファイルは[consul.service](https://github.com/ahus1/saltconsul-examples/blob/master/master/srv/salt/consul/conf/consul.service)を参考にしました。

```bash:/lib/systemd/system/consul.service
[Unit]
Description=Consul agent
Requires=network-online.target
After=network-online.target

[Service]
User=consul
Group=consul
EnvironmentFile=-/etc/default/consul
ExecStart=/usr/local/bin/consul $CMD_OPTS
ExecReload=/bin/kill -HUP $MAINPID
KillSignal=SIGINT

[Install]
WantedBy=multi-user.target
```

　`編集したsystemdの設定ファイルをリロードして反映させます。

```bash
$ systemctl daemon-reload
```

　Consulサービスの自動起動を有効にしてサービスを起動します。

```bash
$ systemctl enable consul
Created symlink from /etc/systemd/system/multi-user.target.wants/consul.service to /lib/systemd/system/consul.service.
$ systemctl start consul
```


## Docker

　[Dockerのインストール](https://docs.docker.com/engine/installation/debian/)はワンライナーで簡単にします。

```bash
$ curl -sSL https://get.docker.com/ | sh
```

### systemd

　[Docker init script on Debian Jessie not honoring DOCKER_OPTS](https://groups.google.com/forum/#!msg/docker-user/WCeEVsgyD4c/aPFdQSJMnbMJ)にあるように、Jessieの`/lib/systemd/system/docker.service`では`DOCKER_OPTS`の起動オプションを読み込みません。オフィシャルの[Control and configure Docker with systemd](https://docs.docker.com/engine/articles/systemd/)を参考にsystemdの設定ファイルを編集します。

　dockerサービスを停止します。

```bash
$ systemctl stop docker
```

　```/etc/default/docker```にDockerの起動オプションを`DOCKER_OPTS`環境変数に設定します。

```bash:/etc/default/docker
DOCKER_OPTS="--cluster-store=consul://localhost:8500 --cluster-advertise=eth0:2376"
```

　systemdの`docker.service`ファイルを編集して環境設定ファイルから`DOCKER_OPTS`環境変数を読み込み、`docker daemon`の起動オプションに追加します。

```bash:/lib/systemd/system/docker.service
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network.target docker.socket
Requires=docker.socket

[Service]
Type=notify
EnvironmentFile=-/etc/default/docker 
ExecStart=/usr/bin/docker daemon -H fd:// $DOCKER_OPTS
MountFlags=slave
LimitNOFILE=1048576
LimitNPROC=1048576
LimitCORE=infinity

[Install]
WantedBy=multi-user.target
```

　設定ファイルのリロードとサービスを開始します。

```bash
$ systemctl daemon-reload
$ systemctl start docker
```

　`docker info`コマンドを実行すると`Cluster store`と`Cluster advertise`の情報が表示されます。Consulを分散KVSに使ったMulti-Host Docker Networkingの準備ができました。

```bash
$ docker info
Containers: 1
Images: 8
Server Version: 1.9.1
Storage Driver: aufs
 Root Dir: /var/lib/docker/aufs
 Backing Filesystem: extfs
 Dirs: 10
 Dirperm1 Supported: true
Execution Driver: native-0.2
Logging Driver: json-file
Kernel Version: 3.16.0-4-amd64
Operating System: Debian GNU/Linux 8 (jessie)
CPUs: 2
Total Memory: 7.812 GiB
Name: minion-1
ID: NQQD:HJHQ:HQE5:23ZH:H5XH:A6KR:YY3B:36QY:557I:CTB7:TS5Z:BCML
WARNING: No memory limit support
WARNING: No swap limit support
Cluster store: consul://localhost:8500
Cluster advertise: 10.3.0.101:2376
```


### Overlayネットワークの作成

　[Understand Docker container networks](https://docs.docker.com/engine/userguide/networking/dockernetworks/)を読みながら、Docker 1.9からプロダクションになった`docker network`コマンドを使いOverlayネットワークを作成します。`dn`は任意につけたネットワーク名です。`--subnet`フラグでDockerのネットワークアドレスを設定します。`docker network`は`minion-[1-3]`のどこのノードで実行しても構いません。


```bash
$ docker network create \
  --subnet=192.168.0.0/24 \
  --driver=overlay \
  dn
```

　`minion-1`にログインしてDockerのネットワークを確認します。

```bash
$ docker network ls
NETWORK ID          NAME                DRIVER
f10c0f7978af        dn                  overlay
339726e9a8c5        bridge              bridge
4b052cb2cf21        none                null
662ed6607bf0        host                host
```

　`minion-2`と`minion-3`も同様に確認します。どのノードでも同じ`dn`ネットワークは同じIDになっています。

```bash
$ docker network ls
NETWORK ID          NAME                DRIVER
4af071c309a6        bridge              bridge
4524099d60f2        none                null
c49d7d97bf1a        host                host
f10c0f7978af        dn                  overlay
```

## コンテナの作成

### --netフラグ

 `docker run`の`--net`フラグは指定されたネットワークに参加した状態でコンテナを起動します。`minion-1`のDockerホストで`amb1`コンテナを作成します。`eth0`が`docker network create`で作成したネットワークに割り当てられます。

```bash
$ docker run -d -it \
  --name amb1 \
  -h amb1 \
  --net dn \
  debian:jessie
$ docker exec amb1 ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
    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
9: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
    link/ether 02:42:c0:a8:00:02 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.2/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:c0ff:fea8:2/64 scope link
       valid_lft forever preferred_lft forever
12: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.2/16 scope global eth1
       valid_lft forever preferred_lft forever
    inet6 fe80::42:acff:fe12:2/64 scope link
       valid_lft forever preferred_lft forever
```


　同様に`minion-2`Dockerホストから`amb2`コンテナを作成します。

```bash
$ docker run -d -it \
  --name amb2 \
  -h amb2 \
  --net dn \
  debian:jessie
```

　`minion-3`では`amb3`コンテナを作成します。

```bash
$ docker run -d -it \
  --name amb3 \
  -h amb3 \
  --net dn \
  debian:jessie
```

### /etc/hosts

 作成したコンテナの`/etc/hosts`に`--net`フラグで指定したネットッワークに参加しているコンテナのホスト名とIPアドレスが追加されます。

```bash
$ docker exec amb1 cat /etc/hosts
192.168.0.2     amb1
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
192.168.0.3     amb2
192.168.0.3     amb2.dn
192.168.0.4     amb3
192.168.0.4     amb3.dn
```

　作成したコンテナは`dn`ネットワークで以下のIPアドレスが割り当てられました。

|コンテナのhostname|Dockerホスト|コンテナのeth0|
|:--|:--|:--|
|amb1|minion-1|192.168.0.2|
|amb2|minion-2|192.168.0.3|
|amb3|minion-3|192.168.0.4|


## pingを使ったテスト

　pingを実行して異なるDockerホストのコンテナからネットワークの疎通確認をします。

　`amb1`コンテナから`amb2`コンテナへping。

```bash
$ docker exec amb1 ping -c 1 amb2
PING amb2 (192.168.0.3): 56 data bytes
64 bytes from 192.168.0.3: icmp_seq=0 ttl=64 time=0.426 ms
--- amb2 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.426/0.426/0.426/0.000 ms
```

　コンテナから別のDockerホストへも通信できます。`amb1`コンテナから`minion-2(10.3.0.102)`へping。

```bash
$ docker exec amb1 ping -c 1 10.3.0.102
docker exec amb1 ping -c 1 10.3.0.102
PING 10.3.0.102 (10.3.0.102): 56 data bytes
64 bytes from 10.3.0.102: icmp_seq=0 ttl=63 time=0.307 ms
--- 10.3.0.102 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.307/0.307/0.307/0.000 ms
$ docker exec amb1 ping -c 1 minion-2
PING minion-2.cs3d0idcfcloud.internal (10.3.0.102): 56 data bytes
64 bytes from 10.3.0.102: icmp_seq=0 ttl=63 time=0.254 ms
--- minion-2.cs3d0idcfcloud.internal ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.254/0.254/0.254/0.000 ms
```

　コンテナから名前解決をしてインターネットへ通信もできます。

```bash
$ docker exec amb1 ping -c 1 www.yahoo.co.jp
PING www.g.yahoo.co.jp (182.22.40.240): 56 data bytes
64 bytes from 182.22.40.240: icmp_seq=0 ttl=57 time=1.139 ms
--- www.g.yahoo.co.jp ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max/stddev = 1.139/1.139/1.139/0.000 ms
```