Docker Swarm(docker swarm mode) 入門

# 背景
過去に大好評だったDocker入門の内容を2016年版にupdateした再入門投稿3本が終わったので、
まだ紹介していない機能を投稿していきます。

- [Docker Compose 入門](http://qiita.com/ohhara_shiojiri/items/0ac80924a1a8292ce511)
- [Docker Machine 入門(Hyper-Vの場合)](http://qiita.com/ohhara_shiojiri/items/801a24a2c94ff93be82d)
- [Docker Machine 入門(AWSの場合)](http://qiita.com/ohhara_shiojiri/items/9fbb32ecdb999e2df523)
- [Docker 再入門3 2016](http://qiita.com/ohhara_shiojiri/items/cabc644f8a4d1aaf2e36)
- [Docker 再入門2 2016](http://qiita.com/ohhara_shiojiri/items/31a76913cbfdd9d2fe13)
- [Docker 再入門1 2016](http://qiita.com/ohhara_shiojiri/items/98573b074185381d46c8)
- [Docker 入門](http://www.slideshare.net/ShinichiroOhhara/docker-39456836) (SlideShare)

# 動作環境
- ThinkPad X200s OS:Linux MINT 18
- ThinkPad X1 Carbon OS:Windows 10 Pro (Hyper-V)

個人的に開発環境OSはLinuxが主なので、MacOSやWindowsについては優先度が低いのでご了承ください。

# Docker 1.12(docker swarm mode)
複数のホストでDockerコンテナのクラスタを構築する場合、専用のDocker Swarmイメージを使用してクラスタ構築を実現していました。
2016年にリリースされたDocker 1.12からはDocker Engineに統合されてswarm modeとして構築する事が可能になりました。

- [Swarm mode overview](https://docs.docker.com/engine/swarm/)(docker.com)

# Dockerクラスタの構築
通常のwebサイトではクラスタ構築は馴染みが無いかもしれませんが、冗長化や負荷分散といった効果がありますので、積極的に使って行きたいですね。
では、公式ドキュメントを参考にDockerクラスタを構築してみましょう。

## クラスタのノードを３ホスト用意する
まず最初に３つのホストをdocker-machineで作成します。
ここでは例として、Windows 10 Pro(Hyper-V)上にBoot2Dockerのホストを３つ(node1,node2,node3)作成してみました。

```
docker-machine create -d hyperv --hyperv-virtual-switch "Primary Virtual Switch" node1
docker-machine create -d hyperv --hyperv-virtual-switch "Primary Virtual Switch" node2
docker-machine create -d hyperv --hyperv-virtual-switch "Primary Virtual Switch" node3
```

動作状況を確認しておきます。

```
PS C:\WINDOWS\system32> docker-machine ls
NAME    ACTIVE   DRIVER   STATE     URL                        SWARM   DOCKER    ERRORS
node1   -        hyperv   Running   tcp://192.168.99.11:2376           v1.12.3
node2   -        hyperv   Running   tcp://192.168.99.12:2376           v1.12.3
node3   -        hyperv   Running   tcp://192.168.99.13:2376           v1.12.3
```

パラメーターのSWARMにステータスが無い事が確認出来ます。これがDockerの通常モードです。

## 親ノード(manager)の設定
全てのノードの元になる親ノード(manager)を設定します。ここではnode1のホストを親ノード(manager)とします。
docker-machineコマンドでnode1ホストにSSHログインします。
そしてdockerコマンドでswarm modeに初期化します。IPアドレスはnode1ホストの番号を指定する事でサービスが開始されます。

```
PS C:\WINDOWS\system32> docker-machine ssh node1

docker@node1:~$ docker swarm init --advertise-addr 192.168.99.11
Swarm initialized: current node (28d8rbrnzxz4ktgmhhattzc52) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join \
    --token SWMTKN-1-0z7y5jvysq0kuwao3itq783ncslyxw3ugn1l5w9l4k64z83lzi-9v684gv2g9tp28blu3hvhplrd \
    192.168.99.11:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
```

表示文中のコマンド例は、別の追加する子ノード(worker)上で実行するモノで、これをメモしておきましょう。
また最終行にあるコマンド'docker swarm join-token manager'で再表示も可能です。

swarm modeへの変更を確認してみましょう。

```
docker@node1:~$ docker info
・・・中略・・・
Swarm: active
 NodeID: 28d8rbrnzxz4ktgmhhattzc52
 Is Manager: true
 ClusterID: 5t03nqvt7btkh1y88dfsb2f2t
 Managers: 1
 Nodes: 1
```

swarm modeが有効になって、ID表示がされています。

全ノードの状態表示をして確認してみましょう。

```
docker@node1:~$ docker node ls
ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
28d8rbrnzxz4ktgmhhattzc52 *  node1     Ready   Active        Leader
```

親ノード(manager)として動作していることが確認できます。

## 子ノード(worker)の追加設定
それでは、他のホストを子ノードとして設定しましょう。
docker-machineコマンドでnode2ホストにSSHログインします。
そしてdockerコマンドで先程メモしたコマンドを入力して実行します。

```
PS C:\WINDOWS\system32> docker-machine ssh node2

docker@node2:~$ docker swarm join \
> --token SWMTKN-1-0z7y5jvysq0kuwao3itq783ncslyxw3ugn1l5w9l4k64z83lzi-9v684gv2g9tp28blu3hvhplrd \
> 192.168.99.11:2377
This node joined a swarm as a manager.
```

無事に子ノード(worker)が追加されたら同じようにnode3ホストも設定します。

## 全クラスタの動作確認
親ノード(manager)にSSHログインしてノード状態を表示してみます。

```
PS C:\WINDOWS\system32> docker-machine ssh node1

docker@node1:~$ docker node ls
ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
28d8rbrnzxz4ktgmhhattzc52 *  node1     Ready   Active        Leader
7yt8lf2zmcxt16dkcw5b4ors1    node2     Ready   Active        Reachable
877vnv6x40xzd6uf8c2i0rvdp    node3     Ready   Active        Reachable
```

## サービスをデプロイ
それでは親ノード(manager)にDockerコンテナのサービスをデプロイしてみます。
例として、AlpineLinuxでdocker.comへpingする単純なコンテナです。
Dockerコンテナ名にhelloworldを付けています。

```
docker@node1:~$ docker service create --replicas 1 --name helloworld alpine ping docker.com
0vu955zjrfh294mw1ivdzdr67
```

作成したDockerコンテナサービス(helloworld)の状態表示をしてみます。

```
docker@node1:~$ docker service ls
ID            NAME        REPLICAS  IMAGE   COMMAND
0vu955zjrfh2  helloworld  1/1       alpine  ping docker.com

docker@node1:~$ docker service inspect --pretty helloworld
ID:             0vu955zjrfh294mw1ivdzdr67
Name:           helloworld
Mode:           Replicated
 Replicas:      1
Placement:
UpdateConfig:
 Parallelism:   1
 On failure:    pause
ContainerSpec:
 Image:         alpine
 Args:          ping docker.com
Resources:

docker@node1:~$ docker service ps helloworld
ID                         NAME          IMAGE   NODE   DESIRED STATE  CURRENT STATE          ERROR
f0i0te74m5in3qehhtgkg1yfv  helloworld.1  alpine  node1  Running        Running 2 minutes ago

docker@node1:~$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS
NAMES
a1e9f32ec207        alpine:latest       "ping docker.com"   3 minutes ago       Up 3 minutes        helloworld.1.f0i0te74m5in3qehhtgkg1yfv
```

親ノード(manager)でDockerコンテナ(helloworld)のサービスが稼動している状況が確認できました。

## 子ノード(worker)へスケール
親ノード(manager)で動作しているDockerコンテナのサービス(helloworld)を子ノード(worker)へスケールさせてみましょう。

```
docker@node1:~$ docker service scale helloworld=5
helloworld scaled to 5
```

このコマンドでは、親ノード(manager)で動作しているhelloworldのコンテナを５個スケールさせています。
ノードの数は全部で３個ですが、どうなるでしょう。

```
docker@node1:~$ docker service ps helloworld
ID                         NAME          IMAGE   NODE   DESIRED STATE  CURRENT STATE           ERROR
f0i0te74m5in3qehhtgkg1yfv  helloworld.1  alpine  node1  Running        Running 6 minutes ago
1p3u9x2po7vlgfph2vw2stqt0  helloworld.2  alpine  node2  Running        Running 23 seconds ago
2rxzqe66wdyd4tao016ktcmh8  helloworld.3  alpine  node1  Running        Running 25 seconds ago
5xrj8yxz54faehzr591xjqb9k  helloworld.4  alpine  node3  Running        Running 23 seconds ago
5vpxaiqy8slos9j81bmuydy7m  helloworld.5  alpine  node2  Running        Running 23 seconds ago
```

１個のノードに複数のDockerコンテナサービスが稼働しています。
node1のホストでも2個のDockerコンテナサービスが稼働しているので確認してみましょう。

```
docker@node1:~$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS              PORTS
 NAMES
166d6e7a8dbe        alpine:latest       "ping docker.com"   About a minute ago   Up About a minute   helloworld.3.2rxzqe66wdyd4tao016ktcmh8
a1e9f32ec207        alpine:latest       "ping docker.com"   7 minutes ago        Up 7 minutes        helloworld.1.f0i0te74m5in3qehhtgkg1yfv
```

最初に実行したオリジナルのDockerコンテナの他にもう1個のDockerコンテナが実行されているのが確認できます。
この様にして１つのDockerコンテナを複数のホストにスケールする事でクラスタが構築出来ます。

## 後片付け方法
複数のホストで複数のDockerコンテナで構成されたクラスタですが、コマンド１つで簡単に消す事が出来ます。

```
docker@node1:~$ docker service rm helloworld
helloworld

docker@node1:~$ docker service inspect helloworld
[]
Error: no such service: helloworld
```