Docker、ボリューム(Volume)について真面目に調べた

2019-10-5追記
この記事を書いて１年が経ちましたが、実は今もほぼ毎日いいねやストックをもらっています。
やはり、Docker の Volume が気になる人は多いんだな、と感じています。
その一方、同じテーマで書いた [『[Docker] containerが使ってる全てのvolumeをCSV出力するワンライナー - Qiita』](https://qiita.com/gounx2/items/444a341acd2fd18f2c58) は、未だに「１いいね」なのですが、どちらかと言えば、こちらの記事のほうが実践的なので、紹介しておきます。
具体的には「gitからcloneしてきてdocker-composeで起動したけど、これのコンテナとボリュームってどうつながってるの？」といった疑問の解決に使える思います :wink:

# はじめに
Docker の Volume って難しくないですか？
理解に自信が無かった部分を、真面目に調べてまとめてみました。

# これを読むとわかること

- `コンテナ` と `ボリューム` の違い
- `-v /aaa:/bbb` と `-v /bbb` と `-v ccc:/bbb` の違い(docker-compose では `volumes`)
- `--volumes-from ddd` の意味(docker-compose では `volumes_from`)
- Data Volume と Data Volume Container の違い
- Data Volume Container は、「永続化したいデータの保存先となるコンテナ」という意味ではない。（永続化したいデータの保存先はボリュームです）

# ボリューム(Data Volume)とは

- **ボリュームとは、データを永続化できる場所のことである。**
    - 外部HDDのようなイメージ。コンテナ本体にマウント(`-v`)して使う。
    
- `コンテナ` と `ボリューム` の違いについて
    - コンテナ内部にデータ(ファイル)を保存しても、コンテナ破棄すると消えてしまう。
    - なので、データを永続化したいときは、コンテナの外にデータを置く必要がある。
    - その場所のことを、ボリュームと呼ぶ。

- `ボリューム`(=データを永続化できる場所) は２種類ある。
    - ホストのディレクトリ(ファイル)
        - ホストで `ls` で見えるモノ。
    - Docker の リソースとしての Volume
        - `docker volume ls` で見えるモノ。
    - （本当は、これ以外にもNFSなども指定できるようだ）

- ボリュームをコンテナにマウントする(`-v`)ことで、コンテナからアクセスできるようになる。
- あるいは`--volumes-from <コンテナ名>` とすると、指定したコンテナのマウントと同じようにマウントできる。（指定コンテナの `-v` の真似をする）

# 例

- 以下コマンドを実行したとします。
    - 1 `docker run -it --name c1 -v /hdir:/c1-hdir -v /hfile.txt:/c1-hfile.txt ubuntu:18.04 bash`
    - 2 `docker volume create v1`
    - 3 `docker run -it --name c2 -v v1:/c2-v1 ubuntu:18.04 bash`
    - 4 `docker run -it --name c3 --volumes-from c1 --volumes-from c2 ubuntu:18.04 bash`

- イメージは。

```
+--<Host>----------+   +--<Docker>-------------------------------------+
|                  |   |  +--<Container:c1>---+  +--<Volume:v1>-----+  |
|                  |   |  |                   |  |                  |  |
| /hdir/--------------------> /c1-hdir/       |  |                  |  |
| /hfile.txt----------------> /c1-hfile.txt   |  |                  |  |
|                  |   |  |                   |  |                  |  |
|                  |   |  +-------------------+  |                  |  |
|                  |   |                         |                  |  |
|                  |   |  +--<Container:c2>---+  |                  |  |
|                  |   |  |                   |  |                  |  |
|                  |   |  |  /c2-v1/ <-----------------/            |  |
|                  |   |  |                   |  |                  |  |
|                  |   |  |                   |  |                  |  |
+------------------+   |  +-------------------+  +------------------+  |
                       |                                               |
                       |  +--<Container:c3>-------------------------+  |
                       |  | c1,c2 と同じものが同じ場所にマウントされる |  |
                       |  |   /c1-hdir/                             |  |
                       |  |   /c1-hfile.txt                         |  |
                       |  |   /c2-v1/                               |  |
                       |  +-----------------------------------------+  |
                       +-----------------------------------------------+
```
- このようになるので、コンテナ `c1` `c2` を破棄しても、データ元であるボリュームは保持されるので、消えない。
- コンテナ `c3` は `c1` `c2` のマウントポイントを持つ。（同じボリュームを同じマウントポイントにマウント）
- メモ
    - `docker volume create v1` は実行しなくてもいい。なぜなら `-v v1:/c2-v1` しいたときに `v1` が無ければ、自動で `v1` が作られるから。
    - ボリューム名無し `-v /c2-v1` でも自動的にボリュームが作成される。しかしながら、ボリューム名がハッシュ値になるので、`docker volume ls` してもどれが使われているのか分かりづらい。

        ```
        $ docker volume ls
        DRIVER    VOLUME NAME
        local     35ad7529d3e1c981bbc2d9235b9e3e9f6f71aaf8143f5d80015a84db7bb06019・・・ボリューム名無しのとき
        local     v1・・・ボリューム名有のとき
        ```

    - 実は 17.06 から `--mount` オプションが追加され、こちらが公式で推奨されている。
        - Use bind mounts | Docker Documentation https://docs.docker.com/storage/bind-mounts/#choosing-the--v-or---mount-flag

# Data Volume Container とは

- **「永続化したいデータの保存先となるコンテナ」という意味ではありません。**
    - 私は勘違いしてましたorz

- 公式サイトで、Data Volume Container という用語はヒットしませんでした。
    - 厳密には **Container ≠ Volume** なので、個人的にはこの用語には違和感があります。
- 複数コンテナでボリュームをマウントするときの `-v` の記述を省略するテクニックなのだと理解しています。
    - 1 軽量なイメージ(busybox)を使って、ボリュームをマウント(`-v`)したコンテナを作っておく（これを Data Volume Container、あるいは Data Container と命名。このコンテナ自身は終了していてよい）
    - 2 新しいコンテナを作る時 `--volumes-from <Data Volume Container>` するだけで、ボリュームを同じマウントポイントで共有できる。

## Docker Volume Container の利点がもう１つありました。`--rm` でボリューム削除されないようにできます。(2018/09/28追記)

- 普通に `docker run --rm ...` するとコンテナ停止で、ボリュームも削除される。
    - 1 `docker run -it --rm --name c2 -v /c2-v1 ubuntu:18.04 bash`
    - 2 bashを抜けるとコンテナ停止となるので、コンテナ `c1` ボリューム`v1` が自動で削除される。
- 上記の操作をしてもボリュームが残るようにしたいなら、先に Data Volume Conteiner を作っておけばよい。（コンテナにマウントされているボリュームは自動削除されない）
- もっとも、Docker Volume Container を使わなくとも、ボリューム名有りマウント `-v v1:/c2-v1` すれば `--rm` しても ボリューム `v1` を残すことができる。
    - 1 `docker run -it --rm --name c2 -v v1:/c2-v1 ubuntu:18.04 bash`
    - 2 bashを抜けると、コンテナ `c1` が削除される。ボリューム `v1` は残る。

# まとめ

ここまでのポイントを整理しておきます。

## ボリュームとは、データを永続化できる場所のことである。
外部HDDのようなイメージ。コンテナ本体にマウント(`-v`)して使う。

## コンテナ≠ボリューム(Data Volume) である。
コンテナ破棄しても、これにマウントされているボリュームは残る。

## `--volumes-from <コンテナ名>` は「コンテナをマウント」しているのではない。
実は「指定コンテナのマウント(`-v`)のマネ」をしているだけ。マウントできるのはボリュームだけ。


## 永続化したいならボリューム名有りマウント `-v v1:/c2-v1` を使うべき(2018/09/28記述見直し)
- ボリューム名無しマウント `-v /c2-v1` のデメリット
    - ボリューム名がハッシュ値になるので、`docker volume ls` してもどれが使われているのか分かりづらい。
    - `--rm` オプションを付けてコンテナ起動したとき、コンテナ破棄だけでなく ボリュームも削除されてしまう。
- ボリューム名有りマウント `-v v1:/c2-v1` を使えば、上記のデメリットはない。