Docker Swarmクラスタでリンクとボリュームを使ってみる

Docker Swarmでクラスタを構築すると、複数のホストを1つのホストのように扱うことができます。
複数のコンテナを連携する機能であるリンクやボリュームがクラスタでも使えるのか調べて見ます。

# 準備

Docker Swarmのクラスタを構築します。構築方法はREADMEや以前書いた記事を参照してください。

* https://github.com/docker/swarm
* [Docker Swarmでクラスタを構築する](http://qiita.com/atsaki/items/16c50dfd579a9339c333)

```swarm manage``` を実行している端末でエラーがでていないことと、```swarm list``` でノードのIPアドレスが表示されることを確認します。

```bash
$ swarm list --token e7c4acc2096712987b041645b7b6290a
10.3.0.184:2375
10.3.0.64:2375
```

```node01``` で```DOCKER_HOST``` の設定も忘れずに行っておきます。

```bash
$ export DOCKER_HOST=tcp://<node01のプライベートIP>:2375
```

# Docker Swarmの制限

Docker Swarm経由では一部のコマンドやオプションに制限があります。

## コンテナをdaemonモードで起動する必要がある

```-d``` オプションを付けないと以下のように怒られてしまいます。
figもこれに引っかかって動かせませんでした。

```bash
$ docker run -it --rm --link redis:redis relateiq/redis-cli
FATA[0000] Error response from daemon: Attach is not supported in clustering mode, use -d.
```

## 一部のコマンドが実行できない

```docker images``` など一部のコマンドはDocker Swarmでは使えないようです。

```bash
$ docker images
FATA[0000] Error response from daemon: Not supported in clustering mode. 
```

# リンク

1つめのコンテナを起動します。

```bash
$ docker run -d --name redis crosbymichael/redis
3416288ab10b5d18496b3905f18598c6f2d081ba04c83db13ce1f2675050d13f
$ docker ps -a
CONTAINER ID        IMAGE                        COMMAND                CREATED             STATUS                  PORTS               NAMES
3416288ab10b        crosbymichael/redis:latest   "redis-server --bind   6 seconds ago       Up Less than a second   6379/tcp            VM-68ec7bff-77e2-4b95-bd34-50735f4a4ea3.csecidcfcloud.internal/redis  
```

redisコンテナをリンクして新しいコンテナを起動し環境変数が設定されていることを確認します。

```bash
$ docker run -d --link redis:redis dockerfile/redis 'env'
f3b9225ce639ad224df16a9310e3ad231eef1064b4599287c99b69284ca5a6d6
$ docker logs f3b
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=f3b9225ce639
REDIS_PORT=tcp://172.17.0.21:6379
REDIS_PORT_6379_TCP=tcp://172.17.0.21:6379
REDIS_PORT_6379_TCP_ADDR=172.17.0.21
REDIS_PORT_6379_TCP_PORT=6379
REDIS_PORT_6379_TCP_PROTO=tcp
REDIS_NAME=/pensive_fermi/redis
HOME=/root
```

期待通り設定されているので```redis-cli``` コマンドを実行してみます。
応答が返ってきて連携がとれているのがわかります。

```bash
$ docker run -d --link redis:redis dockerfile/redis bash -c 'redis-cli -h redis ping'
88a191b504d1eccf1d29fec57f818308821419cc6005d12c5a41bead2d73405a
$ docker logs 88a
PONG
```

redisコンテナが乗っているノードを落としてみます。

```bash
# node02 or node03
# Ctrl-Cでswap joinをとめる
$ service docker stop
```

redisコンテナとリンクしてコンテナを起動させようとすると以下のようにエラーとなります。

```bash
$ docker run -d --link redis:redis dockerfile/redis bash -c 'redis-cli -h redis ping'
FATA[0000] Error response from daemon: 500 Internal Server Error: Could not find entity for redis
```

リンクさせなければ起動しているノードで実行されます。

```bash
$ docker run -d dockerfile/redis bash -c 'redis-cli -h redis ping'
bf7b396167536be3cfe2708e572f612802a7a4a0436dc35f816f7526016211e3
$ docker logs bf7b
Could not connect to Redis at redis:6379: Name or service not known
```

落としたノードをクラスタに復帰させます。

```bash
$ service docker start
docker start/running, process 6727
$ swarm join --token 71cb3861bd4d2d2e2a16c9c37807e955 --addr 10.3.0.184:2375
```

```swarm manage```を実行している端末に次のようなメッセージが表示され復帰したことがわかります。

```bash
INFO[1260] [D7R4:KWCD:QGET:MZTH:QLLF:GW2X:RBVS:BCE3:CD6T:KNAA:JI72:E7BF/VM-68ec7bff-77e2-4b95-bd34-50735f4a4ea3.csecidcfcloud.internal] Node came back to life. Hooray! 
```

redisコンテナを再度起動し、```redis-cli ping```で応答が返ってくることを確認します。


```bash
$ docker start 3416288ab10b
$ docker run -d --link redis:redis dockerfile/redis bash -c 'redis-cli -h redis ping'
a9cfba6683b48c42091062ebce63a25e87140766c37749c4e5928c20c018c7f5
$ docker logs a9c
PONG
```

# ボリューム

Swarmクラスタでも通常と同様にコンテナにボリュームを追加できます。

```bash
# node01で実行
$ docker run -d -P --name web -v /webapp training/webapp python app.py
85e0df543e2db9e3b3ea77e74c350548b52d08c972d42872b90b9b7ca72075f3
$ docker ps
CONTAINER ID        IMAGE                    COMMAND             CREATED             STATUS              PORTS                        NAMES
85e0df543e2d        training/webapp:latest   "python app.py"     17 minutes ago      Up 17 minutes       10.3.0.184:49153->5000/tcp   VM-68ec7bff-77e2-4b95-bd34-50735f4a4ea3.csecidcfcloud.internal/web    
$ curl http://10.3.0.184:49153
Hello world!
```

## ホストマシンのディレクトリ参照

ホストマシンのディレクトリを参照することもできます。ただし、参照されるディレクトリはノードのディレクトリとなります。webappコンテナを正しく動かすためnode02、node03でディレクトリの用意をしておきます。

```bash
# node02, node03で実行
$ git clone https://github.com/docker-training/webapp.git
$ mkdir -p /src/webapp
$ cp webapp/webapp/* /src/webapp/
```

```bash
# node01で実行
$ docker run -d -P --name web2 -v /src/webapp:/opt/webapp training/webapp python app.py
eab2a9d2c34e8815b0268c258c37d73e0c44c99869c3f1256084cb36f79d8ef4
$ docker ps
CONTAINER ID        IMAGE                    COMMAND             CREATED             STATUS              PORTS                        NAMES
1ca0c9f171ef        training/webapp:latest   "python app.py"     3 minutes ago       Up 3 minutes        10.3.0.184:49156->5000/tcp   VM-68ec7bff-77e2-4b95-bd34-50735f4a4ea3.csecidcfcloud.internal/web2   
85e0df543e2d        training/webapp:latest   "python app.py"     17 minutes ago      Up 17 minutes       10.3.0.184:49153->5000/tcp   VM-68ec7bff-77e2-4b95-bd34-50735f4a4ea3.csecidcfcloud.internal/web    
$ ls /src
ls: cannot access /src: No such file or directory
$ curl http://10.3.0.184:49156
Hello world!
```

## 他のコンテナのボリュームをマウント

他のコンテナのボリュームをマウントすることもできます。

```bash
# node01で実行
# ボリューム用のコンテナを作成
$ docker run -d -v /dbdata --name dbdata training/postgres echo Data-only container for postgres
# dbdataのボリュームをbackup.tarに書き出す
# daemonモードシカ使えないので-dモードを付けておく
$ docker run -d --volumes-from dbdata -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata
4f33ec202c9de26ca0d3e3ec484887aa264d948063b638e4b5af3a81001d1e86
```

node02, node03のいずれかにtarファイルが作成されているのが確認できます。

```bash
# node02 or node03
$ tar tvf backup.tar
drwxr-xr-x root/root         0 2014-12-14 12:32 dbdata/
```

dbdataを持っているコンテナが乗っているノードを落としてみます。

```bash
# node02 or node03
# Ctrl-Cでswap joinをとめる
$ service docker stop
```

dbdataのマウントができなくなります。

```bash
# node01
$ docker run -d --volumes-from dbdata -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata
FATA[0000] Error response from daemon: 406 Not Acceptable: Container dbdata not found. Impossible to mount its volumes
```

ノードをクラスタに復帰させます。```swarm manager``` に復帰したことを示すメッセージが出ることを確認します。

```bash
$ service docker start
$ swarm join --token 09a5b19db4814aecaa09bf5434dfb720 --addr 10.3.0.64:2375
```

再びマウントdbdataをマウントしてみると再び成功するようになっています。

```bash
$ docker run -d --volumes-from dbdata -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata
cf434576e679c9a3e99a2065964c68614d054744fe2bf57658a50d9ea2c63112
```

# まとめ

Docker Swarmでもリンクやボリュームの機能を使うことはできることがわかりました。リンクしたり、ボリュームをマウントしているものについては同じノードで実行されるようです。

一部使えないコマンドもあり、Dockerと連携しているツールがそのまま使えるまでにはもうしばらくかかるのかもしれません。

引き続きチェックしていきたいと思います。

# 参考

* [Dockerコンテナ接続パターン (2014年冬)](http://deeeet.com/writing/2014/12/01/docker-link-pattern/)
* [Data Volume と Data Volume Container](http://qiita.com/sokutou-metsu/items/b83b275198fc9594f5a4)
* [Managing Data in Containers](https://docs.docker.com/userguide/dockervolumes/)