Data Volume と Data Volume Container

Dockerを使う際にアプリケーションで使用するデータをどう扱えばいいのか？

まずは関係ありそうな **Data Volume** と **Data Volume Container** について公式マニュアルを読んで理解してみる。なお、英語が得意でないので間違って解釈している箇所があるかもしれない。 Docker のバージョンは 1.0.0 である。

マニュアル： [Managing data in containers](https://docs.docker.com/userguide/dockervolumes/ "Managing data in containers")


## Data Volume とは

マニュアルによると、 **Data Volume** とは「複数のコンテナ間で永続的なデータや共有データを扱うために Union File System を無視する特別なディレクトリ」のことらしい。 Data Volume は以下の特徴を持つとのこと。

* Data Volume はコンテナ間で共有や再利用することが可能
* Data Volume に対する変更は直接反映される
* Data Volume に対する変更はイメージの変更に含まれない
* Data Volume は参照するコンテナがなくなっても存続する


## Data Volume の作成

`docker run` コマンドに `-v` フラグをつけて実行すると Data Volume が追加される。 `-v` フラグは複数指定することが可能で、複数の Data Volume をマウントすることができる。 Dockerfile では `VOLUME` を使うことで Data Volume を追加できる。

マニュアルには次のようなサンプルが書いてあり、

```console
$ sudo docker run -d -P --name web -v /webapp training/webapp python app.py
```

これでコンテナの `/webapp` が Data Volume になるとのこと。でもこれだけだと使い道がよくわからないので、あとで手を動かして確認してみる。


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

`-v` フラグを使うとホスト側のディレクトリをマウントすることもできる。マニュアルのサンプルではイメージがわかなかたので、次の手順で試してみる。

```console
$ # ホスト側にファイルを作成
$ mkdir -p /tmp/sample
$ echo "In the host." > /tmp/sample/sample.txt
$ # コンテナ側でホスト側のファイルを表示
$ sudo docker run -v /tmp/sample:/mnt/sample:ro ubuntu:14.04 cat /mnt/sample/sample.txt
In the host.
```

ホスト側の `/tmp/sample` がコンテナ上にマウントされて `/mnt/sample` として参照できることが確認できた。

作成されるコンテナがホストマシンの環境に依存してしまうため、この機能は Dockerfile では利用することができない。開発やテストで使用するための機能のようだ。


## Data Volume Container

マニュアルによると、複数のコンテナ間で共有したい永続的なデータや非永続的なコンテナから参照したい永続的なデータは **Data Volume Container** と呼ばれるコンテナを作成して、そのコンテナからデータをマウントするのが良いとのこと。

マウント方法は、 `docker run` に `--volumes-from` フラグを指定する。これで指定したコンテナのボリュームを参照することができるようになる。 `--volumes-from` を複数指定することで、複数のコンテナのボリュームを参照することもできる。


## 手を動かして確認

次の手順で、ボリュームの動作を確認してみた。

まずは `/tmp` をボリュームとしたデータ用のコンテナ(data)を作成する。あとでアタッチするためにコンテナIDはシェル変数に入れて憶えておく。

```console
$ DATA_CONTAINER_ID=$(sudo docker run -i -t -d -P --name data -v /tmp ubuntu:14.04 /bin/bash)
```

次に、データを参照する側のコンテナ(app1)を作り、データコンテナ(data)のボリュームをマウントする。そしてマウントしたディレクトリにファイルを作成してみる。

```console
$ APP1_CONTAINER_ID=$(sudo docker run -i -t -d -P --name app1 --volumes-from data ubuntu:14.04 /bin/bash)
$ sudo docker attach ${APP1_CONTAINER_ID}
root@90e84781851d:/# echo "write from app1" > /tmp/message.txt
```

app1で作成したファイルがデータコンテナに反映されているのか、dataにアタッチして確認してみる。

```console
$ sudo docker attach ${DATA_CONTAINER_ID}
root@f43a44c15674:/# cat /tmp/message.txt
write from app1
```

きちんとデータが共有されているようだ。念のため逆も確認してみる。

```console
$ sudo docker attach ${DATA_CONTAINER_ID}
root@f43a44c15674:/# echo "write from data" >> /tmp/message.txt
root@f43a44c15674:/# cat /tmp/message.txt
write from app1
write from data
$ sudo docker attach ${APP1_CONTAINER_ID}
root@90e84781851d:/# cat /tmp/message.txt
write from app1
write from data
```

データが共有されていることが確認できた。

データボリュームコンテナは複数のコンテナから参照することができる。この場合もデータは各コンテナ間で共有される。

先ほどと同じデータコンテナ(data)を参照する別のコンテナ(app2)を作成して確認してみた。

```console
$ APP2_CONTAINER_ID=$(sudo docker run -i -t -d -P --name app2 --volumes-from data ubuntu:14.04 /bin/bash)
$ sudo docker attach ${APP2_CONTAINER_ID}
root@b70e2705bb74:/# cat /tmp/message.txt
write from app1
write from data
root@b70e2705bb74:/# echo "write from app2" >> /tmp/message.txt
$ sudo docker attach ${APP1_CONTAINER_ID}
root@f43a44c15674:/# cat /tmp/message.txt
write from app1
write from data
write from app2
```

ボリュームの特徴として、データコンテナを削除してもボリュームをマウントしているコンテナがなくなるまでは、ボリュームは存続し続ける。データコンテナ(data)を削除しても、参照している側(app1やapp2)は引き続きデータにアクセスできることが以下の手順で確認できる。

```console
sudo docker kill ${DATA_CONTAINER_ID}
sudo docker rm ${DATA_CONTAINER_ID}
$ sudo docker attach ${APP1_CONTAINER_ID}
root@f43a44c15674:/# cat /tmp/message.txt
write from app1
write from data
write from app2
```


## Read Only なボリューム

`--volumes-from` フラグのオプションに `:ro` を指定することで、ボリュームを読み取り専用としてマウントすることができる。

```console
$ sudo docker run -i -t -d -P --volumes-from data:ro ubuntu:14.04 /bin/bash
```


## Data Volume のバックアップ、復元

Data Volume を使うことで、コンテナのバックアップや、復元、移行を行うことができるとのこと。

まずはバックアップを試してみる。

```console
$ mkdir backup && cd $_
$ sudo docker run --volumes-from app1:ro -v $(pwd):/backup ubuntu:14.04 tar cvf /backup/backup.tar /tmp
tar: Removing leading `/' from member names
/tmp/
/tmp/message.txt
$ ls
backup.tar
```

ボリュームのデータがホスト側に `backup.tar` としてバックアップできた。今度はそれを復元する。

```console
$ sudo docker run -v /tmp --name data2 ubuntu:14.04 /bin/bash
$ sudo docker run --volumes-from data2 -v $(pwd):/backup ubuntu:14.04 tar xvf /backup/backup.tar
$ sudo docker run --volumes-from data2:ro ubuntu:14.04 cat /tmp/message.txt
write from app1
write from data
write from app2
```

別のコンテナでバックアップしたデータを復元することができた。


## まとめ

いままでなんとなく理解していた **Data Volume** について、きちんと理解することができた。

データ専用のコンテナを作って永続的なデータを扱うことを、[こちらのポスト](http://www.tech-d.net/2013/12/16/persistent-volumes-with-docker-container-as-volume-pattern/ "Persistent volumes with Docker – Data-only container pattern") では **Data-only container pattern** と呼んでいる（この呼び方が一般的なのかどうかわからなかった）。

アプリケーションとデータを分離することで永続的なデータが扱いやすくなるほか、アプリケーションが参照する読み取り専用のデータも異なる複数のデータを使って動かすことが容易にできるのでデプロイ前のテストもしやすくなるだろう。