DockerのVolume

dockerのvolumeについてよく分からなかったので、自分用メモ書き。

# 対象
- Docker V18.09
- Linux 4.18.0-16(ホストがWindowsのケースは対象としません)
- dockerコマンド(Dockerfile/docker composeについては応用だと思うので触れません)

# 何が分からなかったか
```shell_session:console
$ docker volume ls
DRIVER              VOLUME NAME
local               ad4c0f7038a3a323f6669ac59065248fa30796027d63c831fb498888caf1f214
local               c2d816bc17333b5665785ff55e725b1c4862e0517a5f1e6c9d6c0ec1e6536857
local               da512d42f360b623034937d44e0b158ed075c6ad285b7f2a2a268f1235e5cf7b
```

なんでこんなものが残っているのか

# 調査

docker docs[^1][^2][^3][^4] を読んで、実環境で確認

## ボリュームの属性の調べ方[^volume_inspect]

```shell_session:console
$ docker volume inspect ad4c0f7038a3a323f6669ac59065248fa30796027d63c831fb498888caf1f214
[
    {
        "CreatedAt": "2019-03-09T06:36:48+09:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/ad4c0f7038a3a323f6669ac59065248fa30796027d63c831fb498888caf1f214/_data",
        "Name": "ad4c0f7038a3a323f6669ac59065248fa30796027d63c831fb498888caf1f214",
        "Options": null,
        "Scope": "local"
    }
]
```

ただこれを見ても大して情報は増えない。難しいことをすると有用らしい。

## ボリュームって何をするもの？[^2]

コンテナ/イメージの外でディレクトリ/ファイルを格納しているもの。
dockerで用意された仕組であり、複数のコンテナからマウントされる。

## マウント？[^3]

コンテナやイメージの外部にあるディレクトリやファイルをコンテナからアクセスできるようにすること。
docker runのオプション--mountで指定できる。
マウントの種類は以下の3種類。

- bind: ホストにあるディレクトリやファイルの直接マウント
- volume: dockerが管理しているボリュームのマウント
- tmpfs: tmpfs(メモリを使ったファイルシステム)のマウント

## ボリュームのマウント[^2]

docker runのオプション--mountか--volumeでマウントできる。
ここでは--volumeによるマウントのみを説明する(tmpfsを除くと機能的には同じなので)。
指定されたvolumeが未作成の場合は、自動的に作成される。

### コンテナ外部にあるディレクトリやファイルのマウント(bind相当)

\[ホストでのパス]:\[コンテナでのパス]:\[マウントオプション] という文字列で指定する。

```shell_session
$ docker run --volume $PWD/hoge:/home/hoge -it --name=mybash bash
bash-5.0# ls -lAF /home/hoge
total 4
-rw-rw-r--    1 1000     1001             6 Mar  8 22:56 fuga.txt
bash-5.0# 
```

上の例では、ホスト上の./hogeディレクトリをコンテナ上の/home/hogeにマウントしている。
コンテナの中でlsを実行しているが、見えているファイルfuga.txtは、ホスト上のファイル。
bind相当だと、**volumeは作成されない**。
mountのtypeも実際にbindとなる。

### dockerが管理しているディレクトリやファイルのマウント(volume相当)

\[名前(省略可)]:\[コンテナでのパス]:\[マウントオプション] という文字列で指定する。

```shell_session
$ docker run --volume /home/hoge -it --name=mybash bash
bash-5.0# ls -lAF /home/hoge
total 0
bash-5.0# 
```

上の例では、新しいボリュームを匿名で作成し、コンテナ上の/home/hogeにマウントしている。
コンテナの中でlsを実行しているが、作成したばかりなので空。
volume相当なので、**volumeは作成される**。

### ボリュームの確認

実際にこのタイミングで別端末から見てみると…

```shell_session
$ docker volume ls
DRIVER              VOLUME NAME
local               9274a58e30ad65cac10d469e513a8900194816ff8ae0d0be78c3d4a42d70f0f3 ←コレ
local               ad4c0f7038a3a323f6669ac59065248fa30796027d63c831fb498888caf1f214
local               c2d816bc17333b5665785ff55e725b1c4862e0517a5f1e6c9d6c0ec1e6536857
local               da512d42f360b623034937d44e0b158ed075c6ad285b7f2a2a268f1235e5cf7b
```

作成されているのが分かる。inspectしてみると…

```shell_session
$ docker volume inspect 9274a58e30ad65cac10d469e513a8900194816ff8ae0d0be78c3d4a42d70f0f3
[
    {
        "CreatedAt": "2019-03-09T19:31:49+09:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/9274a58e30ad65cac10d469e513a8900194816ff8ae0d0be78c3d4a42d70f0f3/_data",
        "Name": "9274a58e30ad65cac10d469e513a8900194816ff8ae0d0be78c3d4a42d70f0f3",
        "Options": null,
        "Scope": "local"
    }
]
```

### ボリュームの実体

Mountポイントの内容が気になるので、コンテナからファイルを作ってみる。

```shell_session
bash-5.0# touch /home/hoge/touch
```

ホスト上でMountポイントを調べてみると…

```shell_session
$ sudo ls -lAF /var/lib/docker/volumes/9274a58e30ad65cac10d469e513a8900194816ff8ae0d0be78c3d4a42d70f0f3/_data
合計 0
-rw-r--r-- 1 root root 0  3月  9 19:48 touch
```

実体がここにあることが分かる(ドキュメントでは言及されていないので実装依存)。
また、マウント方法も気になるので確認してみる(コンテナの中から、mountコマンドで確認する)。

```shell_session
bash-5.0# mount | grep /home/hoge
/dev/sda1 on /home/hoge type ext4 (rw,relatime)
```

通常のデバイスをマウントする形になっているので、overlayでないことが分かる。
つまり、**ボリュームはコンテナ/イメージの外にあり、版管理されない**と分かる。

### 削除

ボリュームは、複数のコンテナ/イメージから参照される可能性があるので、コンテナやイメージの削除に伴って(デフォルトでは)削除されない。
明示的な削除が必要。

#### コンテナの削除と同時に削除する

```shell_session
$ docker container rm -v mybash
mybash
```

\-vオプションが関連するボリュームを同時に削除するという意味。
なお、imageには匿名の新規のボリューム以外はcommitしても残らないように見え、**docker image rmにも-vオプションは存在しない**。

#### ボリュームを直に指定して削除する

今回のようになんか分からない匿名の(名前が自動生成の)ボリュームが残っているケースが該当する。

```shell_session
$ docker container inspect mybash | grep /var/lib/docker/volumes
                "Source": "/var/lib/docker/volumes/2d7f06255d264378aaec4443bb09788246f300e5141df1164221a53d406a9d0a/_data",
$ docker container rm mybash
mybash
$ docker volume rm 2d7f06255d264378aaec4443bb09788246f300e5141df1164221a53d406a9d0a
2d7f06255d264378aaec4443bb09788246f300e5141df1164221a53d406a9d0a
```

既にコンテナもない場合は、マウントしたコンテナを起動して中身を確認する。
/var/lib/docker/volumes/\[名前]/_dataを見てもいいが、これは実装依存。

#### コンテナ実行時に終了時削除指定する

```shell_session
$ docker run --rm --volume /home/hoge -it --name=mybash bash
```

コンテナ実行終了時に、コンテナごとボリュームも削除される(ただし、匿名のボリュームのみ)。

#### 全削除

```shell_session
$ docker volume prune
```

現在未使用のボリュームを全て削除する。
つまり今そのボリュームを使うコンテナが動いてなければ、今後必要なボリュームも根こそぎ削除するということ。

# で、何が分かったか

- コンテナ終了時に不要なボリュームが削除するように構成を考える
- コンテナ終了後も必要なボリュームは、面倒でも予め名前を付けておく
- 名前も中身も分からないボリュームが残っていた場合はマウントして中身を確認する

# 実際には

実際に残っていたボリュームはただのゴミだった。
docker-composeなどではなく、手で起動したコンテナを-v付けずに削除したため、残っていた模様。

[^1]: [Manage data in Docker](https://docs.docker.com/storage/)
[^2]: [Use volumes](https://docs.docker.com/storage/volumes/)
[^3]: [Use bind mounts](https://docs.docker.com/storage/bind-mounts/)
[^4]: [Use tmpfs mounts](https://docs.docker.com/storage/tmpfs/)
[^volume_inspect]: [docker volume inspect](https://docs.docker.com/engine/reference/commandline/volume_inspect/)