過去最大のupdateと聞いてDocker1.12を試してみた

ということで、タイトル通り、過去最大のupdateと言われているDocker1.12の気になる機能を試しました。
meetup等でDocker社の人の話を聞いた内容や公式ドキュメントベースで気になった箇所のみ試します。
ちなみに後方互換性は保たれているので、以下で出てくるswarm init等のコマンドを使わない限り今まで通り使えるそうです。

自分が気になったところを試しただけなので、網羅的な更新一覧および、内部の仕組みは公式ページなり、いつも訳して下さっている前佛さんの日本語訳を参考にしてください。

* [GitHub上のリリース情報](https://github.com/docker/docker/releases/tag/v1.12.0)
  * [日本語訳](http://qiita.com/zembutsu/items/561062299d810257d38b)
* [Docker Built-in Orchestration Ready for Production: Docker 1.12 Goes GA](https://blog.docker.com/2016/07/docker-built-in-orchestration-ready-for-production-docker-1-12-goes-ga/)
  * [日本語訳](https://pocketstudio.net/2016/07/30/docker-1-12-goes-ga-translate/)
* [Docker 1.12: Now with Built-in Orchestration!](https://blog.docker.com/2016/06/docker-1-12-built-in-orchestration/)
  * [日本語訳](https://pocketstudio.net/2016/06/21/docker-1-12-build-in-orchestration-translate/)

あとは[DockerConの動画](https://www.youtube.com/watch?v=vE1iDPx6-Ok&list=PLkA60AVN3hh9gnrYwNO6zTb9U3i1Y9FMY)とかも沢山あるけど多すぎるので、先日行われた[オンラインmeetupの動画](https://www.youtube.com/watch?v=joFYGbqjLBc)がまとまっててオススメです。
というか、このポストはほぼオンラインmeetup及び、ほぼ同じ内容のSanta Claraのmeetupで紹介されてた機能の紹介ですｗ

# 環境

以下の環境はLinuxで行いました。dockerはインストール済みと仮定します。dockerrepoを追加していれば、aptでもdnfでも最新版が入るはずです。以下のデモではDigitalOceanを使いますがVirtualBoxでもAWSでも何でも大丈夫かと思います。DigitalOceanの場合は[こちら](https://m.do.co/c/7be3336a0400)から登録すると今回動かす分くらいのクレジットは貰えます。(私にもクレジットが入るのでそーゆーのがイヤな方は普通にトップから登録してください)

また、以下のポートが開いている必要があります。(DigitalOceanでは設定不要ですが、ipv6有効になってたりするとトラブルかもしれません)

* TCP: 2377, 7946, 4789
* UDP: 7946, 4789

# Swarm modeのチュートリアルを一通りやってみる

まずは[チュートリアル](https://docs.docker.com/engine/swarm/swarm-tutorial/)にあるswarm modeの使い方を一通り試してみます。
全く同じではないですが、既にやった方はここは読まなくて大丈夫です。

チュートリアルでは各ノードでdockerコマンド叩いてますが、sshしたりするのが面倒なのでdocker-machineを使いたいと思います。

```bash:docker-machineのインストール
curl -L https://github.com/docker/machine/releases/download/v0.8.0/docker-machine-`uname -s`-`uname -m` >~/path/to/docker-machine && chmod +x ~/path/to/docker-machine
```

まずはmanagerとなるノードを作成します。DIGITALOCEAN_API_TOKENは適宜設定してください。(以後同様)

```
docker-machine create -d digitalocean --digitalocean-image "ubuntu-16-04-x64" --digitalocean-region "sfo1" --digitalocean-size "512mb" --digitalocean-access-token ${DIGITALOCEAN_API_TOKEN} manager
```

swarmモードで起動します。--advertise-addrではmanagerのIPを指定しますので、docker-machine ls等で調べておいてください。

```
eval $(docker-machine env manager)
docker swarm init --advertise-addr 192.241.219.207
```

以下のようなworker、managerそれぞれの追加方法が出力として得られます。

```bash:実行結果
Swarm initialized: current node (djvtufr82caej8ebysdbsj0ov) is now a manager.

To add a worker to this swarm, run the following command:
    docker swarm join \
    --token SWMTKN-1-46kek56vwx6f7y9d0tqci7zrly11b2h8u0k31xhx0kaxx4f70o-6k4xdghyave5qci4o94s8b433 \
    192.241.219.207:2377

To add a manager to this swarm, run the following command:
    docker swarm join \
    --token SWMTKN-1-46kek56vwx6f7y9d0tqci7zrly11b2h8u0k31xhx0kaxx4f70o-dxsejlrazkr0jaznx4u9o8yl7 \
    192.241.219.207:2377
```

docker infoでSwarmモードがactive、IsManagerがtrueになってることが確認できます。

```
docker info
Swarm: active
IsManager: true
```

docker node lsコマンドでもノードの状態が確認できます。

```
$ docker node ls
ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
djvtufr82caej8ebysdbsj0ov *  manager   Ready   Active        Leader
```

続いてworker用のマシンを2台作ります。

```
docker-machine create -d digitalocean --digitalocean-image "ubuntu-16-04-x64" --digitalocean-region "sfo1" --digitalocean-size "512mb" --digitalocean-access-token ${DIGITALOCEAN_API_TOKEN} worker1
docker-machine create -d digitalocean --digitalocean-image "ubuntu-16-04-x64" --digitalocean-region "sfo1" --digitalocean-size "512mb" --digitalocean-access-token ${DIGITALOCEAN_API_TOKEN} worker2
```

先ほどmanagerを作ったときに得られた結果を参考に、workerを追加してみます。


```
eval $(docker-machine env worker1)
docker swarm join --token SWMTKN-1-46kek56vwx6f7y9d0tqci7zrly11b2h8u0k31xhx0kaxx4f70o-6k4xdghyave5qci4o94s8b433 192.241.219.207:2377

eval $(docker-machine env worker2)
docker swarm join --token SWMTKN-1-46kek56vwx6f7y9d0tqci7zrly11b2h8u0k31xhx0kaxx4f70o-6k4xdghyave5qci4o94s8b433 192.241.219.207:2377
```

再びmanagerでdocker node lsを実行して結果を確認しておきます。

```
eval $(docker-machine env manager)

$ docker node ls
ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
as3dl417gpz9795aedxa970th    worker2   Ready   Active
asqs3rls6pc32izn74zkkiysd    worker1   Ready   Active
djvtufr82caej8ebysdbsj0ov *  manager   Ready   Active        Leader
```

次にサービスを作ります。

```
docker service create --replicas 1 --name helloworld alpine ping docker.com
```

1コンテナのみのhelloworldという名前のサービスで、alpineのイメージを使い、ping docker.comという内容を実行する、というサービスです。

色々なコマンドで結果を確認します。

```
$ docker service ls
ID            NAME        REPLICAS  IMAGE   COMMAND
16hidqk9zown  helloworld  1/1       alpine  ping docker.com

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

$  docker service ps helloworld
ID                         NAME          IMAGE   NODE     DESIRED STATE  CURRENT STATE          ERROR
bdbz6k194232czgn0rrvkklkw  helloworld.1  alpine  manager  Running        Running 2 minutes ago
```

スケールさせてみます。

```
$ docker service scale helloworld=5
helloworld scaled to 5

$ docker service ps helloworld
ID                         NAME          IMAGE   NODE     DESIRED STATE  CURRENT STATE           ERROR
bdbz6k194232czgn0rrvkklkw  helloworld.1  alpine  manager  Running        Running 7 minutes ago
6qhabtifgay4zzzduqabiiwhe  helloworld.2  alpine  worker1  Running        Running 12 seconds ago
33zyifha6z47dvnve8g48g9dh  helloworld.3  alpine  manager  Running        Running 13 seconds ago
7cobt9a7umy33h1flgzor0j1s  helloworld.4  alpine  worker2  Running        Running 11 seconds ago
d8jnt9u5yn85t8bjcfp58cmau  helloworld.5  alpine  worker1  Running        Running 12 seconds ago
```

サービスを削除します。

```
$ docker service rm helloworld
helloworld
```

サービスの状態を確認しようとすると削除済みのためエラーになります。

```
$ docker service ps helloworld
Error: No such service: helloworld
```

続いてローリングアップデートを試してみます。
まずはredisを3コンテナ動かし、update-delayを10秒に設定します。

```
$ docker service create \
  --replicas 3 \
  --name redis \
  --update-delay 10s \
  redis:3.0.6
```

状態確認します。

```
$ docker service ps redis
ID                         NAME     IMAGE        NODE     DESIRED STATE  CURRENT STATE             ERROR
bq9xwhwbryx7i180fx1zw6pb2  redis.1  redis:3.0.6  worker2  Running        Preparing 16 seconds ago
86mgm09e7vb8hzp7aki87fxcd  redis.2  redis:3.0.6  worker1  Running        Running 4 seconds ago
0aphe5lz4w3mrn9wx2rmdnykh  redis.3  redis:3.0.6  manager  Running        Running 3 seconds ago

$ docker service inspect redis --pretty
ID:             6ue7ncgimxwsnarvvdgw7431d
Name:           redis
Mode:           Replicated
 Replicas:      3
Placement:
UpdateConfig:
 Parallelism:   1
 Delay:         10s
 On failure:    pause
ContainerSpec:
 Image:         redis:3.0.6
Resources:
```

redis3.0.7にローリングアップデートしてみます。

```
$ docker service update --image redis:3.0.7 redis
```

まずは以下でUpdateが進行中なのが分かります。

```
$ docker service inspect redis --pretty
ID:             6ue7ncgimxwsnarvvdgw7431d
Name:           redis
Mode:           Replicated
 Replicas:      3
Update status:
 State:         updating
 Started:       9 seconds ago
 Message:       update in progress
Placement:
UpdateConfig:
 Parallelism:   1
 Delay:         10s
 On failure:    pause
ContainerSpec:
 Image:         redis:3.0.7
Resources:
```

10秒間隔のローリングアップデートなので、適宜時間を空けて結果を確認すると以下のようにローリングアップデートされているのが分かります。

```
$ docker service ps redis
ID                         NAME         IMAGE        NODE     DESIRED STATE  CURRENT STATE            ERROR
bq9xwhwbryx7i180fx1zw6pb2  redis.1      redis:3.0.6  worker2  Running        Running 2 minutes ago
86mgm09e7vb8hzp7aki87fxcd  redis.2      redis:3.0.6  worker1  Running        Running 2 minutes ago
1kdq4xygokkeeycdhgw1f9zpc  redis.3      redis:3.0.7  manager  Running        Running 4 seconds ago
0aphe5lz4w3mrn9wx2rmdnykh   \_ redis.3  redis:3.0.6  manager  Shutdown       Shutdown 16 seconds ago

$ docker service ps redis
ID                         NAME         IMAGE        NODE     DESIRED STATE  CURRENT STATE            ERROR
bq9xwhwbryx7i180fx1zw6pb2  redis.1      redis:3.0.6  worker2  Running        Running 2 minutes ago
5tn67yo2r8rdt9sx4tbmk6h3x  redis.2      redis:3.0.7  worker2  Running        Running 9 seconds ago
86mgm09e7vb8hzp7aki87fxcd   \_ redis.2  redis:3.0.6  worker1  Shutdown       Shutdown 25 seconds ago
1kdq4xygokkeeycdhgw1f9zpc  redis.3      redis:3.0.7  manager  Running        Running 36 seconds ago
0aphe5lz4w3mrn9wx2rmdnykh   \_ redis.3  redis:3.0.6  manager  Shutdown       Shutdown 47 seconds ago

$ docker service ps redis
ID                         NAME         IMAGE        NODE     DESIRED STATE  CURRENT STATE                ERROR
a02h3iatx9dfwnkcl5doqette  redis.1      redis:3.0.7  worker1  Running        Running about a minute ago
bq9xwhwbryx7i180fx1zw6pb2   \_ redis.1  redis:3.0.6  worker2  Shutdown       Shutdown about a minute ago
5tn67yo2r8rdt9sx4tbmk6h3x  redis.2      redis:3.0.7  worker2  Running        Running about a minute ago
86mgm09e7vb8hzp7aki87fxcd   \_ redis.2  redis:3.0.6  worker1  Shutdown       Shutdown 2 minutes ago
1kdq4xygokkeeycdhgw1f9zpc  redis.3      redis:3.0.7  manager  Running        Running 2 minutes ago
0aphe5lz4w3mrn9wx2rmdnykh   \_ redis.3  redis:3.0.6  manager  Shutdown       Shutdown 2 minutes ago
```

終了後に再び状態確認しておきます。Updateがcompletedになっていることが分かります。

```
$ docker service inspect redis --pretty
ID:             6ue7ncgimxwsnarvvdgw7431d
Name:           redis
Mode:           Replicated
 Replicas:      3
Update status:
 State:         completed
 Started:       2 minutes ago
 Completed:     about a minute ago
 Message:       update completed
Placement:
UpdateConfig:
 Parallelism:   1
 Delay:         10s
 On failure:    pause
ContainerSpec:
 Image:         redis:3.0.7
Resources:
```

サービスを削除しておきます。

```
$ docker service rm redis
redis
$ docker service ps redis
Error: No such service: redis
```

次にDrainを試します。ノードの状態をDrainにするとそのノードではコンテナが作られなくなります。
既に作られているコンテナは別のノードに移動します。

```
$ docker service create --replicas 3 --name redis --update-delay 10s redis:3.0.6
6w08p3j5ew7pg4nurc3ywdq2l

$ docker service ps redis
ID                         NAME     IMAGE        NODE     DESIRED STATE  CURRENT STATE          ERROR
d4z5lw1sn8uiz21m0s2btgvc5  redis.1  redis:3.0.6  manager  Running        Running 8 seconds ago
ad3nqvbu357xjcao1wwdow2kf  redis.2  redis:3.0.6  worker2  Running        Running 7 seconds ago
0yndsykpbfsu4iegpxfuu900q  redis.3  redis:3.0.6  worker1  Running        Running 8 seconds ago
```

worker1をDrainにします。

```
$ docker node update --availability drain worker1
worker1
```

ノードの状態を確認するとDrainになっているのが分かります。

```
$ docker node ls
ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
as3dl417gpz9795aedxa970th    worker2   Ready   Active
asqs3rls6pc32izn74zkkiysd    worker1   Ready   Drain
djvtufr82caej8ebysdbsj0ov *  manager   Ready   Active        Leader
```

serviceの状態を確認するとworker1で動いていたコンテナはmanagerに移動しているのが分かります。

```
$ docker service ps redis
ID                         NAME         IMAGE        NODE     DESIRED STATE  CURRENT STATE               ERROR
d4z5lw1sn8uiz21m0s2btgvc5  redis.1      redis:3.0.6  manager  Running        Running about a minute ago
ad3nqvbu357xjcao1wwdow2kf  redis.2      redis:3.0.6  worker2  Running        Running about a minute ago
d8aehr5dad3u61d77iiun8gyj  redis.3      redis:3.0.6  manager  Running        Running 50 seconds ago
0yndsykpbfsu4iegpxfuu900q   \_ redis.3  redis:3.0.6  worker1  Shutdown       Shutdown 50 seconds ago
```

worker1をactiveに戻しておきます。

```
$ docker node update --availability active worker1
worker1

$ docker node ls
ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
as3dl417gpz9795aedxa970th    worker2   Ready   Active
asqs3rls6pc32izn74zkkiysd    worker1   Ready   Active
djvtufr82caej8ebysdbsj0ov *  manager   Ready   Active        Leader
```

移動したコンテナは戻りませんが、次に作成する際等はworker1にも割り当てられます。

Drainは色々使い方があるようで、例えば、メンテナンスするときなどには当然活用できるでしょう。
また、managerにはコンテナを作りたくないといった場合は、managerをDrainにすれば実現できます(もちろんlabel等を使っても同じことはできますが)。

とりあえず一旦全ノード削除しておきます。

```
docker-machine rm manager
docker-machine rm worker1
docker-machine rm worker2
```

# managerのフェールオーバ

次にmanagerのフェールオーバを試してみます。

コンセンサスにはRaftが使われているので、奇数のmanagerが推奨されています。
ということで、managerとworkerを3台ずつ作ってみます。下記は出力結果を省略しています。

```bash
# docker-machineでmanagerとworkerを3台ずつ作成
docker-machine create -d digitalocean --digitalocean-image "ubuntu-16-04-x64" --digitalocean-region "sfo1" --digitalocean-size "512mb" --digitalocean-access-token ${DIGITALOCEAN_API_TOKEN} manager1
docker-machine create -d digitalocean --digitalocean-image "ubuntu-16-04-x64" --digitalocean-region "sfo1" --digitalocean-size "512mb" --digitalocean-access-token ${DIGITALOCEAN_API_TOKEN} manager2
docker-machine create -d digitalocean --digitalocean-image "ubuntu-16-04-x64" --digitalocean-region "sfo1" --digitalocean-size "512mb" --digitalocean-access-token ${DIGITALOCEAN_API_TOKEN} manager3
docker-machine create -d digitalocean --digitalocean-image "ubuntu-16-04-x64" --digitalocean-region "sfo1" --digitalocean-size "512mb" --digitalocean-access-token ${DIGITALOCEAN_API_TOKEN} worker1
docker-machine create -d digitalocean --digitalocean-image "ubuntu-16-04-x64" --digitalocean-region "sfo1" --digitalocean-size "512mb" --digitalocean-access-token ${DIGITALOCEAN_API_TOKEN} worker2
docker-machine create -d digitalocean --digitalocean-image "ubuntu-16-04-x64" --digitalocean-region "sfo1" --digitalocean-size "512mb" --digitalocean-access-token ${DIGITALOCEAN_API_TOKEN} worker3


# manager1でswarmモードを初期化
eval $(docker-machine env manager1)
docker swarm init --advertise-addr 104.236.176.63

# manager2,3を既存のswarmにmanagerとして追加
eval $(docker-machine env manager2)
    docker swarm join \
    --token SWMTKN-1-3pxkaylj5uubcwiu3a3d7e3zuwqabdbl2y7tf26c1wbbw4xnvy-9ed3sgq2agihmq7aewbvoim4e \
    104.236.176.63:2377
eval $(docker-machine env manager3)
    docker swarm join \
    --token SWMTKN-1-3pxkaylj5uubcwiu3a3d7e3zuwqabdbl2y7tf26c1wbbw4xnvy-9ed3sgq2agihmq7aewbvoim4e \
    104.236.176.63:2377

# worker1,2,3を既存のswarmにworkerとして追加
eval $(docker-machine env worker1)
docker swarm join \
    --token SWMTKN-1-3pxkaylj5uubcwiu3a3d7e3zuwqabdbl2y7tf26c1wbbw4xnvy-619h8ucg44larxtqszj9080gx \
    104.236.176.63:2377
eval $(docker-machine env worker2)
docker swarm join \
    --token SWMTKN-1-3pxkaylj5uubcwiu3a3d7e3zuwqabdbl2y7tf26c1wbbw4xnvy-619h8ucg44larxtqszj9080gx \
    104.236.176.63:2377
eval $(docker-machine env worker3)
docker swarm join \
    --token SWMTKN-1-3pxkaylj5uubcwiu3a3d7e3zuwqabdbl2y7tf26c1wbbw4xnvy-619h8ucg44larxtqszj9080gx \
    104.236.176.63:2377
```

結果を確認しておきます。

```bash
$ eval $(docker-machine env manager1)
$ docker node ls
ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
0eapj2cat7ih6973k9ysapwz2    worker2   Ready   Active
0inl859a5zuul7peoyjfjx9x8    worker3   Ready   Active
6hfv3zrwrd5doma7s6tc7hs61 *  manager1  Ready   Active        Leader
6snxc51snhhjzsin7qg0egobx    manager2  Ready   Active        Reachable
7xj8a2ufn63k38j9aynfzbyl8    manager3  Ready   Active        Reachable
918ny04ls768sl33rl5n02qal    worker1   Ready   Active
```

おもむろにLeaderのmanagerを落としてみます。

```bash
docker-machine rm manager1
```

リーダーが変わったことを確認します。

```bash
$ eval $(docker-machine env manager2)
$ docker node ls
ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
0eapj2cat7ih6973k9ysapwz2    worker2   Ready   Active
0inl859a5zuul7peoyjfjx9x8    worker3   Ready   Active
6hfv3zrwrd5doma7s6tc7hs61    manager1  Down    Active        Unreachable
6snxc51snhhjzsin7qg0egobx *  manager2  Ready   Active        Reachable
7xj8a2ufn63k38j9aynfzbyl8    manager3  Down    Active        Leader
918ny04ls768sl33rl5n02qal    worker1   Down    Active
```

managerは奇数推奨なのでこのような場合は一台追加するべきです。
managerの追加は先述のdocker swarm joinコマンドでもできますが、docker node promoteというコマンドを使うことでworkerをmanagerにpromoteすることができるようなので、こちらを試してみます。

```bash
$ docker node promote worker1
```

結果を確認しておきます。

```bash
$ docker node ls
ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
0eapj2cat7ih6973k9ysapwz2    worker2   Ready   Active
0inl859a5zuul7peoyjfjx9x8    worker3   Ready   Active
6hfv3zrwrd5doma7s6tc7hs61    manager1  Down    Active        Unreachable
6snxc51snhhjzsin7qg0egobx *  manager2  Ready   Active        Reachable
7xj8a2ufn63k38j9aynfzbyl8    manager3  Ready   Active        Leader
918ny04ls768sl33rl5n02qal    worker1   Ready   Active        Reachable
```

逆にdemoteコマンドを使うとmanagerをworkerにdemoteすることもできます。

```bash
$ docker node demote manager3
$ docker node ls
ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
0eapj2cat7ih6973k9ysapwz2    worker2   Ready   Active
0inl859a5zuul7peoyjfjx9x8    worker3   Ready   Active
6hfv3zrwrd5doma7s6tc7hs61    manager1  Down    Active        Unreachable
6snxc51snhhjzsin7qg0egobx *  manager2  Ready   Active        Leader
7xj8a2ufn63k38j9aynfzbyl8    manager3  Ready   Active
918ny04ls768sl33rl5n02qal    worker1   Ready   Active        Reachable
```

ちなみにSTATUSの変化は1分くらいかけて、Unknown、Downと変わってからReadyに変わりました。

次のデモのために再び全滅させておきます。

```bash
docker-machine rm manager1
docker-machine rm manager2
docker-machine rm manager3
docker-machine rm worker1
docker-machine rm worker2
docker-machine rm worker3
```

# Routing meshを試す

Routing meshはserviceを作った場合に、リクエストを受けたノード上で該当のサービスを実行するコンテナが動いていなくても、コンテナが動いているノードにrerouteしてくれるというものです。

今度は再びmanager1台、worker3台構成で試してみます。
実験用に--engine-labelでラベルを指定し、worker1と2がenv=production、3がenv-developmentとなるようにしています。

```bash
# docker-machineでノードを作成
docker-machine create -d digitalocean --digitalocean-image "ubuntu-16-04-x64" --digitalocean-region "sfo1" --digitalocean-size "512mb" --digitalocean-access-token ${DIGITALOCEAN_API_TOKEN} manager
docker-machine create --engine-label env=production -d digitalocean --digitalocean-image "ubuntu-16-04-x64" --digitalocean-region "sfo1" --digitalocean-size "512mb" --digitalocean-access-token ${DIGITALOCEAN_API_TOKEN} worker1
docker-machine create --engine-label env=production -d digitalocean --digitalocean-image "ubuntu-16-04-x64" --digitalocean-region "sfo1" --digitalocean-size "512mb" --digitalocean-access-token ${DIGITALOCEAN_API_TOKEN} worker2
docker-machine create --engine-label env=development -d digitalocean --digitalocean-image "ubuntu-16-04-x64" --digitalocean-region "sfo1" --digitalocean-size "512mb" --digitalocean-access-token ${DIGITALOCEAN_API_TOKEN} worker3

# swarmの初期化とworkderの追加
eval $(docker-machine env manager)
docker swarm init --advertise-addr 162.243.146.81
eval $(docker-machine env worker1)
docker swarm join \
    --token SWMTKN-1-3vh5e65rcvv5vbx10o456ikl6mmab7v5pgpebrilpejictw1kh-c5ewusty0safq5764gvgvknwl \
    162.243.146.81:2377
eval $(docker-machine env worker2)
docker swarm join \
    --token SWMTKN-1-3vh5e65rcvv5vbx10o456ikl6mmab7v5pgpebrilpejictw1kh-c5ewusty0safq5764gvgvknwl \
    162.243.146.81:2377
eval $(docker-machine env worker3)
docker swarm join \
    --token SWMTKN-1-3vh5e65rcvv5vbx10o456ikl6mmab7v5pgpebrilpejictw1kh-c5ewusty0safq5764gvgvknwl \
    162.243.146.81:2377
```

Redisのサービスをコンテナがmanagerで起動するように作成します。

```bash
docker service create \
  --replicas 1 \
  --name redis \
  --update-delay 10s \
  --publish 6379:6379 \
  --constraint node.hostname==manager \
  redis:latest
```

worker上でアプリを動かします。
使うアプリは[以前書いた記事](http://qiita.com/kiida/items/059e34ea53c16e6bc9ec)で使った[ウンコード](http://qiita.com/kiida/items/059e34ea53c16e6bc9ec#compose%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6swarm%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E4%B8%8A%E3%81%AB%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%82%92%E4%BD%9C%E6%88%90)と[ウンコテナ](https://hub.docker.com/r/daikikohara/app01/)を使います。

中身はREDISからアクセスするたびに変化するカウンターの値を取得するだけです。

```bash
docker service create \
  --replicas 2 \
  --name app \
  --update-delay 10s \
  --publish 8000:8000 \
  --env REDIS_HOST=162.243.146.81 \
  --constraint engine.labels.env==production \
  daikikohara/app01:v6 
```

しばらく時間がかかるので、docker service ps appで起動状態になるまでまちます。

```
# これだとPrepareだからまだ
$ docker service ps app
ID                         NAME   IMAGE                 NODE     DESIRED STATE  CURRENT STATE             ERROR
7t31od5tkcey8cri9mbhp7elt  app.1  daikikohara/app01:v6  worker2  Running        Preparing 53 seconds ago
2s74twgjjxlrxlgz3v5zxy498  app.2  daikikohara/app01:v6  worker1  Running        Preparing 53 seconds ago

# こうなればOK
$ docker service ps app
ID                         NAME   IMAGE                 NODE     DESIRED STATE  CURRENT STATE          ERROR
7t31od5tkcey8cri9mbhp7elt  app.1  daikikohara/app01:v6  worker2  Running        Running 3 minutes ago
2s74twgjjxlrxlgz3v5zxy498  app.2  daikikohara/app01:v6  worker1  Running        Running 3 minutes ago
```

なお、上記結果の通り、labelでenvをproductionにしたworkerでのみコンテナが動いていることが分かります。


各workerノードにcurlでアクセスしてカウンターの値が取れるか確認しておきます。

各ノードのIPは以下の通りです。

```
$ docker-machine ls
NAME      ACTIVE   DRIVER         STATE     URL                          SWARM   DOCKER    ERRORS
manager   *        digitalocean   Running   tcp://162.243.146.81:2376            v1.12.0
worker1   -        digitalocean   Running   tcp://107.170.250.177:2376           v1.12.0
worker2   -        digitalocean   Running   tcp://107.170.233.85:2376            v1.12.0
worker3   -        digitalocean   Running   tcp://104.131.132.105:2376           v1.12.0
```

コンテナが起動しているworker1,2にリクエストすれば当然値が取れます。

```
$ curl http://107.170.250.177:8000
You have visited this page 1 times!

$ curl http://107.170.233.85:8000
You have visited this page 2 times!
```

routing meshにより、コンテナが起動していないworker3でも値が取れます。

```
$ curl http://104.131.132.105:8000
You have visited this page 3 times!
```

ってか、worker3はdevelopmentというlabelを指定してたので取れないことを想定していたんですが、swarmクラスタ上のノードだと全てrouting meshが有効になっているようです。
これってlabelで指定したサービス以外だと取れないことを設定できた方が便利な気がするので、今後に期待したいと思います。(もしかして既に設定できるかな？)

ということで、ホントはproductionの1ノードのみにscaleさせて逆側のproductionのworkerでも取れることを確認したかったのですが、そもそも既に動作確認できてしまったので、これで終わりますｗ

# HEALTHCHECK

Dockerfileに[HEALTHCECK](https://docs.docker.com/engine/reference/builder/#/healthcheck)という命令が追加されました。HEALTHCHECKを使うと独自のロジックでコンテナのヘルスチェックをできるようになります。
上のapp01というコンテナを以下のような内容のDockerfileに置き換えてv7タグでpublishしています。

```
FROM golang:1.6.3
ADD app /app
HEALTHCHECK --interval=10s --timeout=5s --retries=3 CMD curl http://localhost:8000 | grep "You have visited"
CMD /app
```

上記の場合CMDに指定したコマンドを10秒間隔で実行し、5秒以内に正常に結果が得られなかったケースが3回連続したらコンテナはunhealthyと見なされます。

ということで、まずはredisのコンテナを起動します。

```
docker service create \
  --replicas 1 \
  --name redis \
  --update-delay 10s \
  --publish 6379:6379 \
  --constraint node.hostname==manager \
  redis:latest
```

続いてappを起動します。

```
docker service create \
  --replicas 2 \
  --name app \
  --update-delay 10s \
  --publish 8000:8000 \
  --env REDIS_HOST=162.243.146.81 \
  --constraint engine.labels.env==production \
  daikikohara/app01:v7
```

redisサービスを終了させます。

```
docker service rm redis
```

上記のHEALTHCHECKが失敗するので、しばらくしてから確認すると以下のように何度もコンテナを再起動しようとして失敗しているのが分かります。

```
$ docker service ps app
ID                         NAME       IMAGE                 NODE     DESIRED STATE  CURRENT STATE                    ERROR
7u1xdmznc4n1qmxn5m8noeu84  app.1      daikikohara/app01:v7  worker1  Ready          Ready 3 seconds ago
82g81zthfgm0o4b8ylbegsvfr   \_ app.1  daikikohara/app01:v7  worker1  Shutdown       Failed 4 seconds ago             "task: non-zero exit (2)"
9wlenwhsht77zcavdpw39ju7m   \_ app.1  daikikohara/app01:v7  worker1  Shutdown       Failed 10 seconds ago            "task: non-zero exit (2)"
51zxip69n196202i0cqelurf5   \_ app.1  daikikohara/app01:v7  worker1  Shutdown       Failed 16 seconds ago            "task: non-zero exit (2)"
as4vhy7zxh78wj30k3w2b0cdp   \_ app.1  daikikohara/app01:v7  worker1  Shutdown       Failed 22 seconds ago            "task: non-zero exit (2)"
2v1tmjx0qmh0x46pfvklns542  app.2      daikikohara/app01:v7  worker2  Running        Starting less than a second ago
eqqdrefs7w63v239tr1gjy85g   \_ app.2  daikikohara/app01:v7  worker2  Shutdown       Failed 5 seconds ago             "task: non-zero exit (2)"
2r17smeo78j1lppbu9n1z5z9h   \_ app.2  daikikohara/app01:v7  worker2  Shutdown       Failed 11 seconds ago            "task: non-zero exit (2)"
5zkes80bq2wtp8h4b3d2qpora   \_ app.2  daikikohara/app01:v7  worker2  Shutdown       Failed 16 seconds ago            "task: non-zero exit (2)"
e4s1lrwc1ainf5jfzfq1iqmg4   \_ app.2  daikikohara/app01:v7  worker2  Shutdown       Failed 22 seconds ago            "task: non-zero exit (2)"
```

redisを立ち上げ直してしばらくしてから確認すると2コンテナともRunning状態を維持できるようになります。

```
$ docker service ps app
ID                         NAME       IMAGE                 NODE     DESIRED STATE  CURRENT STATE          ERROR
d39j1zw2fs2d1bc4o5tzc5q7n  app.1      daikikohara/app01:v7  worker1  Running        Running 2 minutes ago
9u8nmhwwmkczxqg9pjpcug214   \_ app.1  daikikohara/app01:v7  worker1  Shutdown       Failed 2 minutes ago   "task: non-zero exit (2)"
4yau7s92c0917penlazl5uuq0   \_ app.1  daikikohara/app01:v7  worker1  Shutdown       Failed 2 minutes ago   "task: non-zero exit (2)"
581y6jezbpr6cxwyynnqqvw97   \_ app.1  daikikohara/app01:v7  worker1  Shutdown       Failed 2 minutes ago   "task: non-zero exit (2)"
7u1xdmznc4n1qmxn5m8noeu84   \_ app.1  daikikohara/app01:v7  worker1  Shutdown       Failed 3 minutes ago   "task: non-zero exit (2)"
at0qqu7xhjz90ujmnmgminn00  app.2      daikikohara/app01:v7  worker2  Running        Running 2 minutes ago
f0cq0x7ccq9d88dpspolx78db   \_ app.2  daikikohara/app01:v7  worker2  Shutdown       Failed 2 minutes ago   "task: non-zero exit (2)"
4qpmgm93g899xu9rgr8rrqq30   \_ app.2  daikikohara/app01:v7  worker2  Shutdown       Failed 2 minutes ago   "task: non-zero exit (2)"
1ce8fu69ua6hnqnomho52vb1t   \_ app.2  daikikohara/app01:v7  worker2  Shutdown       Failed 2 minutes ago   "task: non-zero exit (2)"
a5r6fkpme7elaj0qw3kln5zo3   \_ app.2  daikikohara/app01:v7  worker2  Shutdown       Failed 2 minutes ago   "task: non-zero exit (2)"
```


# globalモード

ここまではreplicated serviceという各ノード上に適宜分散配置されるコンテナを使ったサービスになりますが、globalモードを使うと必ず各ノードで1コンテナずつ動くサービスが作れます。
公式ドキュメント等ではノードのモニタリングやウイルススキャン等、必ず各ノードで1つ動いておくべきものを動かすことを想定しているようです。

```
docker service create \
  --name redis \
  --mode global \
  --publish 6379:6379 \
  redis:latest


$ docker service ps redis
ID                         NAME       IMAGE         NODE     DESIRED STATE  CURRENT STATE            ERROR
1fitx5g74ujssl9bjbhyz5nv3  redis      redis:latest  worker3  Running        Preparing 7 seconds ago
1quo2c3c9fz3wll6cl57dao4b   \_ redis  redis:latest  worker2  Running        Preparing 7 seconds ago
5paactsnolafblrz0y6j8rhfs   \_ redis  redis:latest  worker1  Running        Preparing 7 seconds ago
coh5zcahl5jedozkqzgcx06g4   \_ redis  redis:latest  manager  Running        Running 6 seconds ago
```

ということで、各ノードで1コンテナずつ動いていることが確認できました。

# その他気になった点

* ノード間の通信がデフォルトでTLSになったらしいです。([参考](https://docs.docker.com/swarm/secure-swarm-tls/))
* [plugin](https://docs.docker.com/engine/extend/plugins/)コマンドとかも気になるんですが、まだexperimentalな上にドキュメントがよく分からなかったので試してません。。。

以上、またなにかあれば追記・修正します。