周回遅れのDocker 5: Data volume と Data volume container


https://docs.docker.com/userguide/dockervolumes/ ([日本語訳](http://qiita.com/zembutsu/items/373a5df90db2a72d822e))

まず「Data volume」というもの自体が何であるかということを理解する必要がある。
コンテナ上に存在する特別なデータ領域（ディレクトリ）として扱われるという位置付けで、コンテナ間の共有に利用、Volume に対して直接データの書き換えが可能、かつコンテナイメージの変更点に影響を受けないという、外部からマウントされたストレージというように見える存在といえそう。

コンテナを作成する際に `-v` オプション (あるいは Dockerfile を使ってビルドする場合は `VOLUME`) でディレクトリパスを指定すると、そのパスが Data volume となる。１コンテナに複数の Data volume を用意することも可能な模様。

## Data volume の作成

以下の例では、作成するコンテナに /data というパスが data volume となるよう設定している。

```bash:Data-volumeの作成
$ docker run -i -t -v /data --name storage busybox /bin/sh

(以下 Container による /bin/sh)
/ # ls -la
total 60
drwxr-xr-x   25 root     root          4096 Jul  8 14:10 .
drwxr-xr-x   25 root     root          4096 Jul  8 14:10 ..
-rwxr-xr-x    1 root     root             0 Jul  8 14:10 .dockerenv
-rwxr-xr-x    1 root     root             0 Jul  8 14:10 .dockerinit
drwxrwxr-x    2 root     root          4096 May 22  2014 bin
drwxr-xr-x    2 root     root          4096 Jul  8 14:10 data
drwxr-xr-x    5 root     root           380 Jul  8 14:10 dev
drwxr-xr-x    6 root     root          4096 Jul  8 14:10 etc
drwxrwxr-x    4 root     root          4096 May 22  2014 home
drwxrwxr-x    2 root     root          4096 May 22  2014 lib
lrwxrwxrwx    1 root     root             3 May 22  2014 lib64 -> lib
lrwxrwxrwx    1 root     root            11 May 22  2014 linuxrc -> bin/busybox
drwxrwxr-x    2 root     root          4096 Feb 27  2014 media
drwxrwxr-x    2 root     root          4096 Feb 27  2014 mnt
drwxrwxr-x    2 root     root          4096 Feb 27  2014 opt
dr-xr-xr-x  129 root     root             0 Jul  8 14:10 proc
drwx------    2 root     root          4096 Jul  8 14:10 root
lrwxrwxrwx    1 root     root             3 Feb 27  2014 run -> tmp
drwxr-xr-x    2 root     root          4096 May 22  2014 sbin
dr-xr-xr-x   13 root     root             0 Jul  8 14:10 sys
drwxrwxrwt    3 root     root          4096 May 22  2014 tmp
drwxrwxr-x    6 root     root          4096 May 22  2014 usr
drwxrwxr-x    4 root     root          4096 May 22  2014 var
```

試しにこのまま Data volume 内に適当なファイルを作ってみる。

```bash
/ # cd /data
/data # echo "add on storage" > ./foo.txt
/data # cat foo.txt
add on storage
```

対象コンテナの Data volume の状態は `docker inspect` で掲載される情報でチェックできる。

```json:inspect(抜粋)
{
    ....
    "Volumes": {
        "/data": "/mnt/sda1/var/lib/docker/volumes/739a035f219f82ba922c93d54d43b790755c280948a135da3e741ecaa52eee6f/_data"
    },
    "VolumesRW": {
        "/data": true
    },
    ....
}
```

元になるコンテナにそのディレクトリパスが存在している必要はなさそう。存在しなければそのパスのディレクトリとして利用可能になるし、既存パスであればそのディレクトリパスが Data volume として扱われることになる模様。

### ローカルパスのマウント

この `-v` オプションを用いて、Docker コマンドを実行する自分自身のローカルパスをコンテナ上の任意のディレクトリパスとしてマウントすることができる。（さらっと[2回目](http://qiita.com/kumatch/items/7ea3bd4f87237980a399)のときに使っている）

以下のコマンド例では、ローカルパス /path/to/src をコンテナの /src としてマウントしている。コンテナの /src にアクセスすると、ローカルの /path/to/src が (存在していれば) 表示される。

```bash:ローカルパスのマウント
$ docker run -i -t -v /path/to/src:/src busybox /bin/sh

(以下 Container による /bin/sh)
/ # ls -la /src
```

この /src もまた Data volume である。



## Data volume を他コンテナと共有する　(data volume container)

前述のとおり、Data volume はコンテナ間の共有のために利用することを想定している。Docker では、Data volume を持ったコンテナを別のコンテナへマウントすることで、Data volume の領域をマウントした側のコンテナで参照や更新を行うことができる。

この時の被マウント側、すなわち Data volume を持ったコンテナのことを Data volume container と呼んでいる。永続させたいデータの格納領域を Data volume container に持たせた上で、その永続データの読み書きを行うサービスのコンテナに Data volume container をマウントした上で自分自身は Immutable に扱うといった利用法になりそう。

### Data volume container を試す

新たに起動するコンテナに `--volumes-from` オプションで既に起動中の storage コンテナを Data volume container として指定。起動後, storage コンテナ側で Data volume として振舞っている /data が新しい serviceコンテナ側でも見えていてデータを参照することができる。

```bash
$ docker run -i -t --volumes-from storage --name service busybox /bin/sh

/ # ls -la /data
total 12
drwxr-xr-x    2 root     root          4096 Jul  8 14:59 .
drwxr-xr-x   25 root     root          4096 Jul  8 15:02 ..
-rw-r--r--    1 root     root            15 Jul  8 15:00 foo.txt
/ # cat /data/foo.txt
add on storage
```


では新しい service コンテナ側でこの Data volume を操作してみる。

```bash:serviceコンテナ側でファイル更新
# echo "add on service" >> /data/foo.txt
```

storage コンテナ側でチェックしてみると、service コンテナ側による変更が参照できる。

```bash:storageコンテナ側で更新分を確認
# cat /data/foo.txt
add on storage
add on service
```


## バックアップ、リストア

Data volume からデータを抽出したり、逆に注入したりすることで、バックアップ／リストアのフローを行うことができる。

バックアップの場合は、Data volume container とローカルパスをマウントしたコンテナ上で、コンテナ側の必要な Data volume 上のファイルをマウントしたローカルパス側の Data volume に転送してあげればよい。

```bash:backup
$ docker run --volumes-from storage -v $(pwd):/backup busybox tar cvf /backup/backup.tar /data
```

これで、このコマンドを実行したディレクトリパスには backup.tar ファイルが作成される。中身を確認してみると、先ほどまでのテスト実行ファイルが含まれていることがわかる。

```bash:backupファイルの確認
$ tar xf backup.tar
$ cat ./data/foo.txt
add on storage
add on service
```

バックアップしている内容を新しい Data volume container へリストア。

```bash:backupファイルを新しいコンテナへ
$ docker run -v /data --name storage2 busybox /bin/sh
$ docker run -i -t --volumes-from storage2 -v $(pwd):/backup busybox /bin/sh

# tar xf /backup/backup.tar -C /
# cat /data/foo.txt
add on storage
add on service
```


## 考察

[ドキュメント](https://docs.docker.com/reference/run/#volume-shared-filesystems)をみると、`--volues-from` で指定するコンテナ (複数可) 側で定義されている全ての Data volume を作成コンテナにマウントする、と書かれている。

Data volume container の Data volume をマウントする側のコンテナのどういったディレクトリパスとするか、といったような指定はできないように見受けられる。したがって、マウントする側のコンテナ内サービスがどういったディレクトリパスを利用するのかという仕様に基づいて Data volume container を用意する必要がある。（たとえばサービスのデータベースサーバがデータを永続化するためのストレージ領域を /opt としているならば、Data volume container の　Data volume も /opt として作成しなければならない。）

何とか任意のディレクトリパスとしてマウントしたい、という要望に対して

1. Data volume container を inspect して Data volume のフルパスを調べ、そのフルパスをサービスコンテナの Data volume として使う（`--volumes-from` を使わない）
http://stackoverflow.com/questions/23137544/how-to-map-volume-paths-using-dockers-volumes-from

1. マウント側コンテナでシンボリックリンクを張る (ディレクトリパス依存性の反転)
http://stackoverflow.com/questions/24871006/how-to-mount-docker-container-volumes-to-different-path

といった案がある模様。

… 仮にサービスコンテナ側の仕様変更でパスが変わったとしてもリストアして Data volume container を作り直せばいいだけだし、基本的には普通に「サービスコンテナ側の仕様に基づいて Data volume container を作る」でいいと思う。