Docker swarmモードを試してみた

# 最初に
ホントはRaft Distributed Consensus Alrogithm/Protocolを使っているようなのでマネージャーが3台より多くあると障害にも強いクラスタになるようですが今回はそんなこと考えないです。

気になった方は[etcd総選挙を眺めてみる](http://qiita.com/ksato9700/items/9b44a95ce27ac23a94e1)を読むと良いかもしれないです。(すごい参考になりました)

この辺めっちゃ参考にしてます。
https://docs.docker.com/engine/swarm/

# 実行環境

|node|役割|
|:--|:--|
|node-01|worker兼マネージャー|
|node-02|worker|

ターミナル1はnode-01のコマンド実行用にする

```sh:ターミナル1
$ docker-machine create -d virtualbox node-01
$ eval $(docker-machine env node-01)
```

ターミナル2はnode-02のコマンド実行用にする

```sh:ターミナル2
$ docker-machine create -d virtualbox node-02
$ eval $(docker-machine env node-02)
```

# 初期化

まずはマネージャーの初期化

```sh:ターミナル1
$ docker swarm init --advertise-addr $(docker-machine ip node-01)
Swarm initialized: current node (61p7kfvmw4m4nbfz3vodgc2y6) is now a manager.

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

    docker swarm join \
    --token SWMTKN-1-0ue65cw4x1k6j2hys2n6hgzpqy0ckv8du7v88e6drkoylh1snm-d7uhyzmofmli2mxa9n93jwepd \
    192.168.99.100:2377

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

上記を実行するとworkerを参加させるためのtoken付きのコマンドが発行される
このコマンドをworkerにしようと思っているnode-02で実行するとクラスタに参加できる

参加する前に現状のクラスタの状態を確認しておく

```sh:ターミナル1
$ docker node ls
ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
61p7kfvmw4m4nbfz3vodgc2y6 *  node-01   Ready   Active        Leader
```

tokenの確認は下記のコマンドでもできる

```sh:ターミナル1
$ docker swarm join-token worker
To add a worker to this swarm, run the following command:

    docker swarm join \
    --token SWMTKN-1-0ue65cw4x1k6j2hys2n6hgzpqy0ckv8du7v88e6drkoylh1snm-d7uhyzmofmli2mxa9n93jwepd \
    192.168.99.100:2377

```

マネージャーとしてクラスタに参加させたい場合は `docker swarm join-token manager` を実行するとマネージャーとして参加させるためのtokenが見れる

node-02をworkerとして参加させて見る

```sh:ターミナル2
$ docker swarm join \
--token SWMTKN-1-0ue65cw4x1k6j2hys2n6hgzpqy0ckv8du7v88e6drkoylh1snm-d7uhyzmofmli2mxa9n93jwepd \
192.168.99.100:2377

This node joined a swarm as a worker.
```

クラスタの状態を確認する

```sh:ターミナル1
$ docker node ls
ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
61p7kfvmw4m4nbfz3vodgc2y6 *  node-01   Ready   Active        Leader
cu9p6rego36d4hhtn2653bacj    node-02   Ready   Active
```

ちなみにworker側で状態の確認などはできない

```sh:ターミナル2
$ docker node ls
Error response from daemon: This node is not a swarm manager. Worker nodes can't be used to view or modify cluster state. Please run this command on a manager node or promote the current node to a manager.
```

# サービスの作成

redisサービスを生成する
(TODO: redisのクラスタが生成できるかもしれないが調査できていないためreplicasは1にしている)

```sh:ターミナル1
$docker service create --replicas 1 --name redis -p 6379:6379 redis:3.2.0-alpine
7vurzloshshm2wn5vezxuexku
```

サービスの確認を連打していたらREPLICASが1/1になるのが確認できる

```sh:ターミナル1
$ docker service ls
ID            NAME   REPLICAS  IMAGE               COMMAND
ac3iz5deljd1  redis  0/1       redis:3.2.0-alpine
$ docker service ls
ID            NAME   REPLICAS  IMAGE               COMMAND
ac3iz5deljd1  redis  1/1       redis:3.2.0-alpine
```

```sh:ターミナル1
$ docker service ps redis
ID                         NAME     IMAGE               NODE     DESIRED STATE  CURRENT STATE            ERROR
25g7igyki966vlljtv9lktywx  redis.1  redis:3.2.0-alpine  node-01  Running        Preparing 7 seconds ago
```

実際にどちらのnodeからでも接続できるか確認する
ついでにnode-01でfooにbarをセットしてnode-02で確認できるかも見ておく

```sh:ターミナル1
$ redis-cli -h $(docker-machine ip node-01)
192.168.99.100:6379> set foo bar
OK
192.168.99.100:6379> get foo
"bar"
192.168.99.100:6379> exit

$ redis-cli -h $(docker-machine ip node-02)
192.168.99.101:6379> get foo
"bar"
192.168.99.101:6379> exit
```

# サービスのスケールアップ・スケールダウン、そして自動復旧について

redisのコンテナ数を2つに増やすには下記のscaleコマンドを実行します

```sh:ターミナル1
$ docker service scale redis=2
redis scaled to 2

$ docker service ps redis
ID                         NAME     IMAGE               NODE     DESIRED STATE  CURRENT STATE                   ERROR
25g7igyki966vlljtv9lktywx  redis.1  redis:3.2.0-alpine  node-01  Running        Running 27 minutes ago
73f8jbhknsqu2xo4czq3ng7r0  redis.2  redis:3.2.0-alpine  node-02  Running        Running less than a second ago

$ docker service ls
ID            NAME   REPLICAS  IMAGE               COMMAND
ac3iz5deljd1  redis  2/2       redis:3.2.0-alpine
```

上記のようにかんたんにスケールアップできますが、自動的に負荷分散されるのでredis等の場合にはコンテナ側でクラスタ化しないと値のセットして参照したら見れないことになる可能性があるので1つに戻しておきます。

```sh:ターミナル1
$ docker service scale redis=1
redis scaled to 1

$ docker service ps redis
ID                         NAME     IMAGE               NODE     DESIRED STATE  CURRENT STATE           ERROR
25g7igyki966vlljtv9lktywx  redis.1  redis:3.2.0-alpine  node-01  Running        Running 32 minutes ago
73f8jbhknsqu2xo4czq3ng7r0  redis.2  redis:3.2.0-alpine  node-02  Shutdown       Shutdown 3 seconds ago

$ docker service ls
ID            NAME   REPLICAS  IMAGE               COMMAND
ac3iz5deljd1  redis  1/1       redis:3.2.0-alpine
```

上のreplicasの数を減らした場合コンテナは削除されずただストップする形になるので何度も試してるとゴミが残ることになります。

```sh:ターミナル2
$ docker ps -a
CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS                          PORTS               NAMES
a18235283c81        redis:3.2.0-alpine   "docker-entrypoint.sh"   6 minutes ago       Exited (0) About a minute ago                       redis.2.73f8jbhknsqu2xo4czq3ng7r0
```

~~そして消されるコンテナは古い順ではなくNAMEに書かれてる数値が大きいものから削除されるようです(要検証)~~

20ぐらいまで増やして１つに絞ってみたらredis.13が生き残ったったのでランダムっぽいです

```
dcjg438wipqsw6lsi0vw67f5a  redis.11     redis:3.2.0-alpine  node-01  Shutdown       Shutdown about a minute ago
09a4j1fwxb91tkqioz4bti3sz  redis.12     redis:3.2.0-alpine  node-01  Shutdown       Shutdown 3 seconds ago
064hpn2v6jwpy0p8kbpryzwn7  redis.13     redis:3.2.0-alpine  node-01  Running        Running 3 minutes ago
8lu1d4qwtyb1wz050fjccnbtm  redis.14     redis:3.2.0-alpine  node-01  Shutdown       Shutdown about a minute ago
c9740i08ndi7dte16wsri9ev0  redis.15     redis:3.2.0-alpine  node-01  Shutdown       Shutdown about a minute ago
099mt2ha487pafhufc37hiutv  redis.16     redis:3.2.0-alpine  node-02  Shutdown       Shutdown 3 seconds ago
```

`docker stop`等によってコンテナが眠ってしまった場合自動的にreplicasの数になるように実行されます。
↑この機能を利用していい感じにコンテナの差し替えができるのでは無いかと思ったりもしている

# ローリング・アップデート

下記の設定をするだけでローリングアップデートできます。

```
  --update-delay
  --update-parallelism
```

5秒待ち1コンテナずつ更新かける場合は下記のコマンドになる。

```sh:ターミナル1
$ docker service update --update-delay 5s --update-parallelism 1 --image redis:3.2.5-alpine redis
redis
```

scaleをとりあえず20にして試した結果

```
840x553ujrfghnbqhqi9s0zx5  redis.7       redis:3.2.5-alpine  node-02  Running        Running 55 seconds ago
3qoj8jpmcfj1ji20aa2hgx1by   \_ redis.7   redis:3.2.0-alpine  node-01  Shutdown       Shutdown 58 seconds ago
844vrpbzjs29q9kua2b3n9r3p   \_ redis.7   redis:3.2.0-alpine  node-01  Shutdown       Shutdown 17 minutes ago
db6k0he84jv9xp0az3r8sqd4z  redis.8       redis:3.2.0-alpine  node-01  Running        Running 3 minutes ago
31ict01zi3l0spo05a4gjixwi   \_ redis.8   redis:3.2.0-alpine  node-01  Shutdown       Shutdown 16 minutes ago
bssq36i1fq20e5a2a629n83bz  redis.9       redis:3.2.0-alpine  node-01  Running        Running 3 minutes ago
e7sea97hqpd5d1pjaugxb33pv   \_ redis.9   redis:3.2.0-alpine  node-02  Shutdown       Shutdown 17 minutes ago
52e7l8s5ossbq6nndbwfrbxxq  redis.10      redis:3.2.5-alpine  node-02  Running        Running 6 seconds ago
8jozd1yew7vou0g8x458t0t9e   \_ redis.10  redis:3.2.0-alpine  node-01  Shutdown       Shutdown 9 seconds ago
21hlsx81oruek4p8de0ujztxb   \_ redis.10  redis:3.2.0-alpine  node-01  Shutdown       Shutdown 16 minutes ago
03mtra8s2zw2tomwgqsqz4sxa  redis.11      redis:3.2.5-alpine  node-02  Running        Running 25 seconds ago
```

# サービス削除

すごいサービス消したら紐付いてたコンテナ全部消してくれる！

```sh:ターミナル1
$ docker service rm nginx
nginx
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
```

```sh:ターミナル2
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
```

# まとめ

すげえ

# まとめパート2

バックエンドにetcd等を用意しなくて良くなったswarmって感想。
ただファイルでnodeの管理ができないのでつらいかもしれないですが、一度構築したら必要ないので問題無い気がします。

Nginxとかscaleし易いやつで試せって話でした。試そうと思ったときに適当にdockerhubから選んだ僕があれでした。

#　おまけ

REPLICASの話をつらつらと書きましたが実はgrobalモードがあります。(やる気が出たら記事に追記なりします)
気になった方は調べてみてください。