[Docker] containerが使ってる全てのvolumeをCSV出力するワンライナー

# これは何？
- dockerやdocker-composeで多くのコンテナを動かすと、「このvolumeは、どのconteinerで使ってるんだ？」と悩むことがあります。
- 当初は `docker volume inspect <ボリューム名>` で、調べられるんじゃないかと思っていたのですが、残念ながらこの方法では「そのvolumeが、どのcontainerで使われているか」は表示されませんでした。
- 代わりに Dockerfile や docker-compose.yml を grep したり、`docker container inspect <コンテナ名>` で調べてましたが、毎回これをするのは手間がかかります。

# 解決方法

- ワンライナーを考えました。以下の通りです。
    - `jq` を使ってますので、事前に `apt install jq` などしておいてください。
    -  出力はCSVで、Name(コンテナ名)、Type(bind or volume)、Src(ホストリソース)、Dst(コンテナ側リソース) の順です。
        -  Type=bind は、ホストのファイル(フォルダ)をコンテナへマウント
        -  Type=volumeは、ボリュームをコンテナへマウントを意味します。

```console
$ docker ps -a --format "{{.Names}}" | xargs docker inspect | jq -r '["Name","Type","Src","Dst"], (.[] | { Name:.Name, Mounts:.Mounts[] } | [ .Name, .Mounts.Type, .Mounts.Source, .Mounts.Destination ]) | @csv' 
"Name","Type","Src","Dst"
"/c4x","bind","/tmp","/mount/tmp"
"/c4x","volume","/var/lib/docker/volumes/cc445a32103d6285d6f62694a74016c61255bbfbdcb4bed018ecda82f72f48fa/_data","/mount/unnamedvolume"
"/c4x","volume","/var/lib/docker/volumes/nemedvolume/_data","/mount/namedvolume"
"/c4","volume","/var/lib/docker/volumes/1543500c35fd8a7d4a63abf281733a83eac0ae4f863f36bda31f3d1aff1200d5/_data","/mount/unnamedvolume"
"/c4","volume","/var/lib/docker/volumes/nemedvolume/_data","/mount/namedvolume"
"/c4","bind","/tmp","/mount/tmp"
"/c3x","bind","/tmp","/mount/tmp"
"/c3","bind","/tmp","/mount/tmp"
"/c2x","volume","/var/lib/docker/volumes/nemedvolume/_data","/mount/namedvolume"
"/c2","volume","/var/lib/docker/volumes/nemedvolume/_data","/mount/namedvolume"
"/c1x","volume","/var/lib/docker/volumes/cc445a32103d6285d6f62694a74016c61255bbfbdcb4bed018ecda82f72f48fa/_data","/mount/unnamedvolume"
"/c1","volume","/var/lib/docker/volumes/1543500c35fd8a7d4a63abf281733a83eac0ae4f863f36bda31f3d1aff1200d5/_data","/mount/unnamedvolume"
```

# 参考、解決方法に至るまでの過程

## docker volume inspect で調べてみる（期待した結果を得られず）

- ある時、volumeの一覧を見てみました。

```console
$ docker volume ls
DRIVER              VOLUME NAME
local               1543500c35fd8a7d4a63abf281733a83eac0ae4f863f36bda31f3d1aff1200d5
local               cc445a32103d6285d6f62694a74016c61255bbfbdcb4bed018ecda82f72f48fa
local               nemedvolume
$ 
```

- `1543500c35・・・` って誰が使ってるんだろう？
- dockerコマンドのお決まりパターンとして、各リソースは `inspect` で詳細が見られるので、これを使ってみます。

```console
$ docker volume inspect 1543500c35fd8a7d4a63abf281733a83eac0ae4f863f36bda31f3d1aff1200d5
[
    {
        "CreatedAt": "2019-04-01T00:28:19+09:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/1543500c35fd8a7d4a63abf281733a83eac0ae4f863f36bda31f3d1aff1200d5/_data",
        "Name": "1543500c35fd8a7d4a63abf281733a83eac0ae4f863f36bda31f3d1aff1200d5",
        "Options": null,
        "Scope": "local"
    }
]
```

- 残念ながらこの方法(volume inspect)では、どの container が使っているかという情報は、表示されませんでした。

## 各 container が使用している volume をリストアップするワンライナー

- アプローチを変えて、conteiner inspect から、volume を抽出してみます。
  - `jq` を使ってますので、事前に `apt install jq` などしておいてください。

```console
$ docker ps -a --format "{{.Names}}" | xargs docker inspect | jq -r '["Name","Type","Src","Dst"], (.[] | { Name:.Name, Mounts:.Mounts[] } | [ .Name, .Mounts.Type, .Mounts.Source, .Mounts.Destination ]) | @csv' 
"Name","Type","Src","Dst"
"/c4x","bind","/tmp","/mount/tmp"
"/c4x","volume","/var/lib/docker/volumes/cc445a32103d6285d6f62694a74016c61255bbfbdcb4bed018ecda82f72f48fa/_data","/mount/unnamedvolume"
"/c4x","volume","/var/lib/docker/volumes/nemedvolume/_data","/mount/namedvolume"
★"/c4","volume","/var/lib/docker/volumes/1543500c35fd8a7d4a63abf281733a83eac0ae4f863f36bda31f3d1aff1200d5/_data","/mount/unnamedvolume"
"/c4","volume","/var/lib/docker/volumes/nemedvolume/_data","/mount/namedvolume"
"/c4","bind","/tmp","/mount/tmp"
"/c3x","bind","/tmp","/mount/tmp"
"/c3","bind","/tmp","/mount/tmp"
"/c2x","volume","/var/lib/docker/volumes/nemedvolume/_data","/mount/namedvolume"
"/c2","volume","/var/lib/docker/volumes/nemedvolume/_data","/mount/namedvolume"
"/c1x","volume","/var/lib/docker/volumes/cc445a32103d6285d6f62694a74016c61255bbfbdcb4bed018ecda82f72f48fa/_data","/mount/unnamedvolume"
★"/c1","volume","/var/lib/docker/volumes/1543500c35fd8a7d4a63abf281733a83eac0ae4f863f36bda31f3d1aff1200d5/_data","/mount/unnamedvolume"

```

- `1543500c35・・・` を使っているのは、`c1` `c4` コンテナでした。 (行頭★印)

# 参考、検証するのに使ったスクリプト

```sh
# c1: 名前無しボリューム
docker container run -d -v /mount/unnamedvolume --name c1 alpine tail -f /dev/null
docker container run -d --mount type=volume,target=/mount/unnamedvolume --name c1x alpine tail -f /dev/null

# c2: 名前有りボリューム
docker container run -d -v nemedvolume:/mount/namedvolume --name c2 alpine tail -f /dev/null
docker container run -d --mount type=volume,source=nemedvolume,target=/mount/namedvolume --name c2x alpine tail -f /dev/null

# c3: ローカルフォルダをマウント
docker container run -d -v /tmp:/mount/tmp --name c3 alpine tail -f /dev/null
docker container run -d --mount type=bind,source=/tmp,target=/mount/tmp --name c3x alpine tail -f /dev/null

# c4: c1,c2,c3 と同じものを自分にマウント
docker container run -d --volumes-from c1 --volumes-from c2 --volumes-from c3 --name c4 alpine tail -f /dev/null
docker container run -d --volumes-from c1x --volumes-from c2x --volumes-from c3x --name c4x alpine tail -f /dev/null

# 全コンテナのボリューム、マウント状況
docker ps -a --format "{{.Names}}" | xargs docker inspect | jq -r ".[] | { Name:.Name, Status:.State.Status, Mounts:.Mounts, Binds:.HostConfig.Binds }" 

#
docker container ps --all
docker volume ls

# 全コンテナ停止
docker stop $(docker ps -aq)

# 停止コンテナ削除、未使用ボリューム削除など
docker system prune --volumes
```

- 実行結果

```console
$ # c1: 名前無しボリューム
$ docker container run -d -v /mount/unnamedvolume --name c1 alpine tail -f /dev/null
39476806bd6de03df0959bb3a3090e0578b7195041d59b8d9dfd496f53ab8788
$ docker container run -d --mount type=volume,target=/mount/unnamedvolume --name c1x alpine tail -f /dev/null
f9ff47a6e53e5f13503c4fefe4d25cee38b6aff40fdb4d1a8be00b4bdad2b792
$ 
$ # c2: 名前有りボリューム
$ docker container run -d -v nemedvolume:/mount/namedvolume --name c2 alpine tail -f /dev/null
c5d02e034ba4131017a306023ed709ec4eb58716d2096419cb5f42457f074f47
$ docker container run -d --mount type=volume,source=nemedvolume,target=/mount/namedvolume --name c2x alpine tail -f /dev/null
83aaa13c98846accef04c4866ce421896d6483d3d6ad279a68a4bd3f46b34e79
$ 
$ # c3: ローカルフォルダをマウント
$ docker container run -d -v /tmp:/mount/tmp --name c3 alpine tail -f /dev/null
fc5499024b21e6eb2198668b3fbfe17d36f1d8bc8979ae366c5e7c9a8ba6b178
$ docker container run -d --mount type=bind,source=/tmp,target=/mount/tmp --name c3x alpine tail -f /dev/null
14e026db8f80ae011ea95c9b6a7ea07d8ebddf0f1f2b6d0a83b3cb9cbd90159c
$ 
$ # c4: c1,c2,c3 と同じものを自分にマウント
$ docker container run -d --volumes-from c1 --volumes-from c2 --volumes-from c3 --name c4 alpine tail -f /dev/null
c5d7f45ec7e42099692a446f65e64a40389342ac0e225811bed4c798817309bd
$ docker container run -d --volumes-from c1x --volumes-from c2x --volumes-from c3x --name c4x alpine tail -f /dev/null
7a3823a7c0421d3c56d101301caa79c2542eadbd1177f9bab93caeed5184629f
$ 
$ # 全コンテナのボリューム、マウント状況
$ docker ps -a --format "{{.Names}}" | xargs docker inspect | jq -r ".[] | { Name:.Name, Status:.State.Status, Mounts:.Mounts, Binds:.HostConfig.Binds }" 
{
  "Name": "/c4x",
  "Status": "running",
  "Mounts": [
    {
      "Type": "volume",
      "Name": "31712e5037e79400b29fa2d58369845bdb1c56a8ca34e7b34da1842e5a65c6ee",
      "Source": "/var/lib/docker/volumes/31712e5037e79400b29fa2d58369845bdb1c56a8ca34e7b34da1842e5a65c6ee/_data",
      "Destination": "/mount/unnamedvolume",
      "Driver": "local",
      "Mode": "",
      "RW": true,
      "Propagation": ""
    },
    {
      "Type": "volume",
      "Name": "nemedvolume",
      "Source": "/var/lib/docker/volumes/nemedvolume/_data",
      "Destination": "/mount/namedvolume",
      "Driver": "local",
      "Mode": "",
      "RW": true,
      "Propagation": ""
    },
    {
      "Type": "bind",
      "Source": "/tmp",
      "Destination": "/mount/tmp",
      "Mode": "",
      "RW": true,
      "Propagation": "rprivate"
    }
  ],
  "Binds": null
}
{
  "Name": "/c4",
  "Status": "running",
  "Mounts": [
    {
      "Type": "volume",
      "Name": "cc056258411e7f796fbdc682a34079a241cd3bb9f52e8d9e697b1d3e7c5eaf7c",
      "Source": "/var/lib/docker/volumes/cc056258411e7f796fbdc682a34079a241cd3bb9f52e8d9e697b1d3e7c5eaf7c/_data",
      "Destination": "/mount/unnamedvolume",
      "Driver": "local",
      "Mode": "",
      "RW": true,
      "Propagation": ""
    },
    {
      "Type": "volume",
      "Name": "nemedvolume",
      "Source": "/var/lib/docker/volumes/nemedvolume/_data",
      "Destination": "/mount/namedvolume",
      "Driver": "local",
      "Mode": "",
      "RW": true,
      "Propagation": ""
    },
    {
      "Type": "bind",
      "Source": "/tmp",
      "Destination": "/mount/tmp",
      "Mode": "",
      "RW": true,
      "Propagation": "rprivate"
    }
  ],
  "Binds": null
}
{
  "Name": "/c3x",
  "Status": "running",
  "Mounts": [
    {
      "Type": "bind",
      "Source": "/tmp",
      "Destination": "/mount/tmp",
      "Mode": "",
      "RW": true,
      "Propagation": "rprivate"
    }
  ],
  "Binds": null
}
{
  "Name": "/c3",
  "Status": "running",
  "Mounts": [
    {
      "Type": "bind",
      "Source": "/tmp",
      "Destination": "/mount/tmp",
      "Mode": "",
      "RW": true,
      "Propagation": "rprivate"
    }
  ],
  "Binds": [
    "/tmp:/mount/tmp"
  ]
}
{
  "Name": "/c2x",
  "Status": "running",
  "Mounts": [
    {
      "Type": "volume",
      "Name": "nemedvolume",
      "Source": "/var/lib/docker/volumes/nemedvolume/_data",
      "Destination": "/mount/namedvolume",
      "Driver": "local",
      "Mode": "z",
      "RW": true,
      "Propagation": ""
    }
  ],
  "Binds": null
}
{
  "Name": "/c2",
  "Status": "running",
  "Mounts": [
    {
      "Type": "volume",
      "Name": "nemedvolume",
      "Source": "/var/lib/docker/volumes/nemedvolume/_data",
      "Destination": "/mount/namedvolume",
      "Driver": "local",
      "Mode": "z",
      "RW": true,
      "Propagation": ""
    }
  ],
  "Binds": [
    "nemedvolume:/mount/namedvolume"
  ]
}
{
  "Name": "/c1x",
  "Status": "running",
  "Mounts": [
    {
      "Type": "volume",
      "Name": "31712e5037e79400b29fa2d58369845bdb1c56a8ca34e7b34da1842e5a65c6ee",
      "Source": "/var/lib/docker/volumes/31712e5037e79400b29fa2d58369845bdb1c56a8ca34e7b34da1842e5a65c6ee/_data",
      "Destination": "/mount/unnamedvolume",
      "Driver": "local",
      "Mode": "z",
      "RW": true,
      "Propagation": ""
    }
  ],
  "Binds": null
}
{
  "Name": "/c1",
  "Status": "running",
  "Mounts": [
    {
      "Type": "volume",
      "Name": "cc056258411e7f796fbdc682a34079a241cd3bb9f52e8d9e697b1d3e7c5eaf7c",
      "Source": "/var/lib/docker/volumes/cc056258411e7f796fbdc682a34079a241cd3bb9f52e8d9e697b1d3e7c5eaf7c/_data",
      "Destination": "/mount/unnamedvolume",
      "Driver": "local",
      "Mode": "",
      "RW": true,
      "Propagation": ""
    }
  ],
  "Binds": null
}
$ 
$ #
$ docker container ps --all
CONTAINER ID        IMAGE               COMMAND               CREATED             STATUS                  PORTS               NAMES
7a3823a7c042        alpine              "tail -f /dev/null"   1 second ago        Up Less than a second                       c4x
c5d7f45ec7e4        alpine              "tail -f /dev/null"   2 seconds ago       Up 1 second                                 c4
14e026db8f80        alpine              "tail -f /dev/null"   3 seconds ago       Up 2 seconds                                c3x
fc5499024b21        alpine              "tail -f /dev/null"   4 seconds ago       Up 3 seconds                                c3
83aaa13c9884        alpine              "tail -f /dev/null"   5 seconds ago       Up 4 seconds                                c2x
c5d02e034ba4        alpine              "tail -f /dev/null"   6 seconds ago       Up 5 seconds                                c2
f9ff47a6e53e        alpine              "tail -f /dev/null"   7 seconds ago       Up 6 seconds                                c1x
39476806bd6d        alpine              "tail -f /dev/null"   8 seconds ago       Up 7 seconds                                c1
$ docker volume ls
DRIVER              VOLUME NAME
local               31712e5037e79400b29fa2d58369845bdb1c56a8ca34e7b34da1842e5a65c6ee
local               cc056258411e7f796fbdc682a34079a241cd3bb9f52e8d9e697b1d3e7c5eaf7c
local               nemedvolume
$ 
$ # 全コンテナ停止
$ docker stop $(docker ps -aq)
7a3823a7c042
c5d7f45ec7e4
14e026db8f80
fc5499024b21
83aaa13c9884
c5d02e034ba4
f9ff47a6e53e
39476806bd6d
$ 
$ # 停止コンテナ削除、未使用ボリューム削除など
$ docker system prune --volumes
WARNING! This will remove:
        - all stopped containers
        - all networks not used by at least one container
        - all volumes not used by at least one container
        - all dangling images
        - all build cache
Are you sure you want to continue? [y/N] y
Deleted Containers:
7a3823a7c0421d3c56d101301caa79c2542eadbd1177f9bab93caeed5184629f
c5d7f45ec7e42099692a446f65e64a40389342ac0e225811bed4c798817309bd
14e026db8f80ae011ea95c9b6a7ea07d8ebddf0f1f2b6d0a83b3cb9cbd90159c
fc5499024b21e6eb2198668b3fbfe17d36f1d8bc8979ae366c5e7c9a8ba6b178
83aaa13c98846accef04c4866ce421896d6483d3d6ad279a68a4bd3f46b34e79
c5d02e034ba4131017a306023ed709ec4eb58716d2096419cb5f42457f074f47
f9ff47a6e53e5f13503c4fefe4d25cee38b6aff40fdb4d1a8be00b4bdad2b792
39476806bd6de03df0959bb3a3090e0578b7195041d59b8d9dfd496f53ab8788

Deleted Volumes:
nemedvolume
cc056258411e7f796fbdc682a34079a241cd3bb9f52e8d9e697b1d3e7c5eaf7c
31712e5037e79400b29fa2d58369845bdb1c56a8ca34e7b34da1842e5a65c6ee

Total reclaimed space: 0B
$ 
```