Docker Swarmでクラスタを構築する

前回は[Docker MachineでDockerノードを構築してみた](http://qiita.com/atsaki/items/472ac40fa53fd98626a0)ので、今回は同時にアナウンスされた[Docker Swarm](https://github.com/docker/swarm)でクラスタを構築してみます。

# Docker Swarmとは

Docker Swarmは複数のDockerホストをクラスタ化するためのツールです。Docker SwarmをAPIを受け取るフロントエンドとして、コンテナをクラスタのメンバー上で動かすことができます。

Docker Swarmのマネージャをホストとして指定すれば各種Dockerのツールもそのまま使えるようです。

# ノードの構築

今回はIDCFクラウド上にnode1〜node3の3つの仮想マシンを作成しクラスタを後置鵜してみます。

3台の役割とlistenしているIP・ポートは以下の通りとします。

* node1
    * マネージャ(tcp://0.0.0.0:12375)　　
    * ノード(tcp://0.0.0.0:2375)
* node2
    * ノード(tcp://0.0.0.0:2375) 
* node3
    * ノード(tcp://0.0.0.0:2375)

仮想マシン作成用のVagrantfileは下記リンク先にあります。

* https://github.com/atsaki/vagrant-examples/tree/master/swarm

```vagrant up``` で最小構成のUbuntu14.04の仮想マシン3台が起動します。

```bash
$ vagrant up
```

VagrantfileではDockerのインストールとswarmのイメージのpull、TCPを利用するための設定を行っています。 

```rb
      node.vm.provision "docker" do |d|
        d.pull_images "swarm"
      end
                                                                          
      node.vm.provision "shell" do |s|
        s.inline = <<-EOS
          echo 'DOCKER_OPTS="-H tcp://0.0.0.0:2375 ${DOCKER_OPTS}"' >> /etc/default/docker
          service docker restart

          echo 'export DOCKER_HOST=tcp://localhost:2375' >> ~/.bashrc
        EOS
      end
```

実行が完了したら```vagrant status``` コマンドで3台とも起動している事を確認します。起動やプロビジョニングに失敗したノードについては再度```vagrant up```や```vagrant provision```をやり直してください。

```bash
$ vagrant status
Current machine states:

node1                     running (cloudstack)
node2                     running (cloudstack)
node3                     running (cloudstack)
```

# クラスタの構築

いずれかのノード（例ではnode1）で```swarm create```コマンドを実行しトークンを発行します。

```bash
$ vagrant ssh node1 -c 'docker -H tcp://localhost:2375 run --rm swarm create'
6120a2e88cd767c8a1ee1f4c40ea013f
```

```swarm join```コマンドで各ノードをクラスタにジョインします。

```bash
# ノードにログイン
$ vagrant ssh <ノード名>
# eth0のIPを確認
$ ip addr
# クラスタにジョイン
$ docker run -d swarm join --addr=<eth0のIP>:2375 token://<取得したトークン> 
# ログアウト
$ exit
```

node1で```swarm manage```コマンドを実行しマネージャーを起動します。

```bash
$ vagrant ssh node1
$ docker run -d -p 12375:2375 swarm manage token://<取得したトークン> 
```

# クラスタのステータス確認

node1にログインして、環境変数```DOCKER_HOST``` をswarmのマネージャに設定します。

```bash
$ export DOCKER_HOST=tcp://localhost:12375
```

```swarm list```コマンドを実行すると各ノードのIPが表示されます。

```bash
$ docker run --rm swarm list token://<取得したトークン> 
10.3.0.52:2375
10.3.0.67:2375
10.3.0.79:2375
```

```docker info``` コマンドでノード・コンテナの情報を表示することができます。

```bash
$  docker info
Containers: 4
Nodes: 3
 VM-48692e09-2749-47eb-9b97-92d1b985d2ab.csecidcfcloud.internal: 10.3.0.52:2375
  └ Containers: 1
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 986.8 MiB
 VM-35e5645b-75c2-43a7-b319-a08bec3415b2.csecidcfcloud.internal: 10.3.0.67:2375
  └ Containers: 1
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 986.8 MiB
 VM-2c5d1a87-15f8-4963-8cb9-754ff21d23da.csecidcfcloud.internal: 10.3.0.79:2375
  └ Containers: 2
  └ Reserved CPUs: 0 / 1
  └ Reserved Memory: 0 B / 986.8 MiB
```

```docker ps``` コマンドでswarm関連のコンテナが各ホストで起動していることを確認する事ができます。

```bash
$ docker ps 
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
root@VM-2c5d1a87-15f8-4963-8cb9-754ff21d23da:~# docker ps -a
CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS                       NAMES
91eef6434dec        swarm:latest        "/swarm manage token   8 minutes ago       Up 8 minutes        10.3.0.79:12375->2375/tcp   VM-2c5d1a87-15f8-4963-8cb9-754ff21d23da.csecidcfcloud.internal/loving_curie        
9f99e4992916        swarm:latest        "/swarm join --addr=   9 minutes ago       Up 9 minutes        2375/tcp                    VM-48692e09-2749-47eb-9b97-92d1b985d2ab.csecidcfcloud.internal/gloomy_poitras      
1b9ade9d59d8        swarm:latest        "/swarm join --addr=   10 minutes ago      Up 9 minutes        2375/tcp                    VM-35e5645b-75c2-43a7-b319-a08bec3415b2.csecidcfcloud.internal/gloomy_archimedes   
fd6764187dbc        swarm:latest        "/swarm join --addr=   10 minutes ago      Up 10 minutes       2375/tcp                    VM-2c5d1a87-15f8-4963-8cb9-754ff21d23da.csecidcfcloud.internal/determined_kirch 
```

# コンテナの起動

```docker run```コマンドでコンテナを起動します。初回のダウンロードが実行されている場合などは少し時間がかかります。
```swarm manage``` を実行している端末に実行中の処理のメッセージが表示されます。

```bash
$ docker run -d -p 80:80 dockerfile/nginx
```

完了すると```docker ps```でコンテナの状態を確認できます。curlで80番ポートに接続するとnginxが起動していることが確認できます。

```bash
$ docker ps 
CONTAINER ID        IMAGE                     COMMAND             CREATED              STATUS              PORTS                           NAMES
d21f305376aa        dockerfile/nginx:latest   "nginx"             About a minute ago   Up About a minute   443/tcp, 10.3.0.67:80->80/tcp   VM-35e5645b-75c2-43a7-b319-a08bec3415b2.csecidcfcloud.internal/trusting_tesla
$ curl 10.3.0.67 
```

もう1つコンテナを起動すると別のホストで起動しました。

```bash
$ docker ps
CONTAINER ID        IMAGE                     COMMAND             CREATED             STATUS              PORTS                           NAMES
63abf37db4cf        dockerfile/nginx:latest   "nginx"             11 seconds ago      Up 6 seconds        443/tcp, 10.3.0.52:80->80/tcp   VM-48692e09-2749-47eb-9b97-92d1b985d2ab.csecidcfcloud.internal/agitated_carson   
d21f305376aa        dockerfile/nginx:latest   "nginx"             4 minutes ago       Up 4 minutes        443/tcp, 10.3.0.67:80->80/tcp   VM-35e5645b-75c2-43a7-b319-a08bec3415b2.csecidcfcloud.internal/trusting_tesla        
```

# ノードの停止

node3（例ではeth0のIPが10.3.0.52のノード）を停止してみます。

```bash
$ vagrant halt node3
```

しばらくするとマネージャのログにnode3が死んだことを示すメッセージが表示されました。

```bash
$ docker logs 91eef6434dec 2>&1 | grep error | tail -3
time="2015-03-01T12:11:22Z" level=error msg="Flagging node as dead. Updated state failed: Get http://10.3.0.52:2375/v1.15/containers/json?all=1&size=0: dial tcp 10.3.0.52:2375: no route to host. Are you trying to connect to a TLS-enabled daemon without TLS?" id="VG4H:VHCS:IBXK:5NHM:OAGR:HDK3:DKEH:OD2W:RA3Y:EYGN:4ZJE:KCFM" name="VM-48692e09-2749-47eb-9b97-92d1b985d2ab.csecidcfcloud.internal" 
time="2015-03-01T12:11:55Z" level=error msg="Flagging node as dead. Updated state failed: Get http://10.3.0.52:2375/v1.15/containers/json?all=1&size=0: dial tcp 10.3.0.52:2375: no route to host. Are you trying to connect to a TLS-enabled daemon without TLS?" id="VG4H:VHCS:IBXK:5NHM:OAGR:HDK3:DKEH:OD2W:RA3Y:EYGN:4ZJE:KCFM" name="VM-48692e09-2749-47eb-9b97-92d1b985d2ab.csecidcfcloud.internal" 
time="2015-03-01T12:12:28Z" level=error msg="Flagging node as dead. Updated state failed: Get http://10.3.0.52:2375/v1.15/containers/json?all=1&size=0: dial tcp 10.3.0.52:2375: no route to host. Are you trying to connect to a TLS-enabled daemon without TLS?" id="VG4H:VHCS:IBXK:5NHM:OAGR:HDK3:DKEH:OD2W:RA3Y:EYGN:4ZJE:KCFM" name="VM-48692e09-2749-47eb-9b97-92d1b985d2ab.csecidcfcloud.internal" 
```

```docker ps``` で見るとnode3で起動していたコンテナのSTATUSがPendingとなっています。
このコンテナについてはdaemonへ接続できないため操作ができなくなっています。

```bash
$ docker ps -a | grep VM-48692
63abf37db4cf        dockerfile/nginx:latest   "nginx"                About an hour ago   Pending             443/tcp, 10.3.0.52:80->80/tcp   VM-48692e09-2749-47eb-9b97-92d1b985d2ab.csecidcfcloud.internal/agitated_carson     
9f99e4992916        swarm:latest              "/swarm join --addr=   About an hour ago   Pending             2375/tcp                        VM-48692e09-2749-47eb-9b97-92d1b985d2ab.csecidcfcloud.internal/gloomy_poitras       
```

# ノードの再ジョインとコンテナの再起動


node3を起動し同じトークンで再度joinさせてみます。

```bash
$ vagrant up node3
$ vagrant ssh node3
$ docker run -d swarm join --addr=<eth0のIP>:2375 token://<取得したトークン> 
```

node1からswarmマネージャに接続し確認してみると、node3のコンテナのSTATUSがExitedになっています。

```bash
$ docker ps -a 
CONTAINER ID        IMAGE                     COMMAND                CREATED             STATUS                     PORTS                           NAMES
0819e0d4b157        swarm:latest              "/swarm join --addr=   12 seconds ago      Up Less than a second      2375/tcp                        VM-48692e09-2749-47eb-9b97-92d1b985d2ab.csecidcfcloud.internal/jovial_rosalind     
63abf37db4cf        dockerfile/nginx:latest   "nginx"                About an hour ago   Exited (0) 6 minutes ago                                   VM-48692e09-2749-47eb-9b97-92d1b985d2ab.csecidcfcloud.internal/agitated_carson     
d21f305376aa        dockerfile/nginx:latest   "nginx"                About an hour ago   Up About an hour           443/tcp, 10.3.0.67:80->80/tcp   VM-35e5645b-75c2-43a7-b319-a08bec3415b2.csecidcfcloud.internal/trusting_tesla      
91eef6434dec        swarm:latest              "/swarm manage token   About an hour ago   Up About an hour           10.3.0.79:12375->2375/tcp       VM-2c5d1a87-15f8-4963-8cb9-754ff21d23da.csecidcfcloud.internal/loving_curie        
9f99e4992916        swarm:latest              "/swarm join --addr=   About an hour ago   Exited (2) 6 minutes ago                                   VM-48692e09-2749-47eb-9b97-92d1b985d2ab.csecidcfcloud.internal/gloomy_poitras      
1b9ade9d59d8        swarm:latest              "/swarm join --addr=   About an hour ago   Up About an hour           2375/tcp                        VM-35e5645b-75c2-43a7-b319-a08bec3415b2.csecidcfcloud.internal/gloomy_archimedes   
fd6764187dbc        swarm:latest              "/swarm join --addr=   About an hour ago   Up About an hour           2375/tcp                        VM-2c5d1a87-15f8-4963-8cb9-754ff21d23da.csecidcfcloud.internal/determined_kirch   
```

停止しているコンテナを起動すると再度nginxに接続できるようになります。

```bash
$ docker start 63abf37db4cf
```

# トラブルシューティング

ノードが認識されないなどの問題が発生した場合には、swarmのマネージャ、各ノードのエージェントのログを確認する。

swarm関連のコンテナを表示させる場合には、```-a``` オプションを付ける。

```bash
$ docker ps -a | grep manage
91eef6434dec        swarm:latest              "/swarm manage token   About an hour ago   Up About an hour    10.3.0.79:12375->2375/tcp       VM-2c5d1a87-15f8-4963-8cb9-754ff21d23da.csecidcfcloud.internal/loving_curie 
```

```bash 
$ docker logs 91eef6434dec 2>&1 | head -5
time="2015-03-01T10:51:06Z" level=info msg="Listening for HTTP" addr=":2375" proto=tcp 
time="2015-03-01T10:55:48Z" level=info msg="HTTP request received" method=GET uri="/v1.17/info" 
time="2015-03-01T10:56:08Z" level=info msg="HTTP request received" method=GET uri="/v1.17/containers/json" 
time="2015-03-01T10:57:30Z" level=info msg="HTTP request received" method=GET uri="/v1.17/containers/json" 
time="2015-03-01T10:57:32Z" level=info msg="HTTP request received" method=GET uri="/v1.17/containers/json?all=1" 
```

# まとめ

Dockerのクラスタを構築するツールが色々とでてきましたが、Docker SwarmはDockerのコンテナだけでクラスタが構築できるので比較的簡単に使えそうです。

以前は上手くいかなかったDocker MachineやDocker Composeとの連携もできるようになっているので試してみたいです。