Dockerのあれこれを断捨離する

#はじめに
これも[『Docker/Kubernetes 実践コンテナ開発入門』読書会](https://docker-beginners.connpass.com/)で出てきたのですが、「溜まっていく不要なコンテナやイメージを全消しするコマンドってないのかな」という疑問。それに答えられるコマンドがあったのでそれに関してまとめてみます。

#pruneコマンド
以下は[Prune unused Docker objects](https://docs.docker.com/config/pruning/)に書かれている内容ほぼそのままですが、多少例を交えて書いてみます。

様々なDockerオブジェクトをprune(枝刈り)するということですが、　そもそも日本人はpruneという言葉にあまり馴染みがない人が多いように思います。私もその一人ですが、唯一知っている例として、

```shell-session
$ find . -name .git -prune -o -print
```

というようなのがあります。`find`コマンドでたまに使われる用法ですが、「その前の条件に当てはまるものは検索対象から除外」というオプションですね。上記の例だと「名前が.gitであるものは検索対象から外し、そうでなければ(`-o`)プリントアウトする(`-print`)」ということで、つまりは今のディレクトリ以下の.gitディレクトリを除いた全てのファイルを表示するコマンドになります。

やや脱線しましたが、Dockerではいくつかのオブジェクトに対してprune(枝刈り)する機能が提供されています。

##イメージの断捨離
まずはイメージから。Dockerで以下のようにタグ付けしてビルドすることはよくあると思います。

```shell
$ docker build . -t myimage:latest
```

ただこれを繰り返すと前のビルドで作られたイメージが名無しで積まれていくことになります。docker-composeを使ってビルドした場合も、明示的にタグ付けしてなくてもイメージに自動でタグが付くので同様です。

```shell
$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
myimage             latest              0663708e2fe9        8 seconds ago        101MB
<none>              <none>              0ef42d3b2d90        48 seconds ago       85.6MB
<none>              <none>              6de6344e5c7e        About a minute ago   59.3MB
<none>              <none>              f6852a322079        5 minutes ago        20.7MB
```

そんな時に、`docker image prune`を叩いてみると、「danglingイメージを全部消しちゃうけど良い？」という確認がでて、それに`y`と答えると、最新のビルドだけ残してキレイに消してくれる！

```
$ docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Deleted Images:
deleted: sha256:6de6344e5c7e0174e61e60850fd2b8141d45c25130a1d4721277bf50d587665b
deleted: sha256:2cd97da5bb5dffe5636663e29d8cc1a0ce466cffc0a7ccc711874e279b81a7b2
deleted: sha256:f6852a32207912f722f8f48f7a8f2e7ff19af7e0ec898b1568b4448eb88083b9
deleted: sha256:5fd80e022f1ae979431fc5ffc5c79fcd4ba17cf3599b438c9945e61aa3262f5f
deleted: sha256:0ef42d3b2d90f849ea528387cc431e102b8f4a359da4d01e1bb2f4fe376e8226
deleted: sha256:fd285edd6b4ad162dce41bd15d0127c0cf52dada43af4631776b7e515cabde96

Total reclaimed space: 152.3MB
$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
myimage             latest              0663708e2fe9        41 minutes ago      101MB
```

"dangling"というのは「ぶらぶらしている、宙ぶらりんな」という意味ですが、ここでは

* タグ付けされていない、かつ
* 現存しているコンテナから参照されていない(動いているか止まっているかは不問)

という条件を満たすもので、要するに消しちゃっても影響なさそうなイメージを自動的に消してくれるというスグレモノです。ちなみに、どのイメージが消されちゃうかを事前に知りたければ以下のコマンドで確認できます。

```shell
$ docker image ls --filter dangling=true
```

そして、`docker image prune`にはオプションがいくつかあります。

#####`-a` `-all`
タグが付いていてもコンテナから使われていないイメージは全て消去します。なので、可動しているコンテナがない状態だと全てのイメージが消えちゃいます。

#####`-f` `--force`
実行時に「消しちゃうけど大丈夫？」という確認をスキップするためのオプションです。

#####`--filter`
消去するイメージを選択する追加の条件を指定できます。

指定には2つあります。一つは`until`で、絶対時間で「この時刻よりも前のもの」あるいは相対時間で「何秒・何分・何時間前のもの」という指定ができます。例えば以下のような形になります。

```shell
$ docker image prune --filter until=2019-01-25 # 2019年1月25日以前に作成されたイメージを消す
$ docker image prune --filter until=10m # 10分以上前に作成されたイメージを消す
```

もう一つはラベルを指定するやり方で、各々のイメージを作成時にDockerfileの中でLABELコマンドで付加したラベルを検索条件とします。例えば、

```shell
$ docker image prune --filter label=maintainer # "maintainer"ラベルが設定されているイメージを消去
$ docker image prune --filter label=maintainer=maint@example.com # "maintainer"ラベルがmaint@example.comに設定されているイメージを消去
```

更に、ラベル指定の場合は`label`の後ろに`!`をつけることにより、「条件に当てはまらないイメージを消去」という指定もできます。

```shell
$ docker image prune --filter label!=maintainer # "maintainer"ラベルが設定されていないイメージを消去
$ docker image prune --filter label!=maintainer=maint@example.com # "maintainer"ラベルがmaint@example.comに設定されていないイメージを消去
```

なおこのfilterオプションでは、どのファイルが消去の対象になるのかの事前確認ができず、ちょっと困っています。`label`は`docker image ls --filter`でチェックできるのですが、`label!`だとか`until`は確認する方法を見つけられていません。実際に消去の実行をしない`--dry-run`みたいなオプションがあれば良いのになと思ったりします。

##コンテナの断捨離
次にコンテナの整理について。コンテナも気がつくと不要なものが溜まっていたりします。特に、`docker container ls`としての何も出てこないので安心していると、実行済みのコンテナがたんまり残っていたりします。確認するには　`-a`オプションを使います。

```
$ docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
$ docker container ls -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
5802c2477245        alpine              "ls"                11 seconds ago      Exited (0) 10 seconds ago                       awesome_hellman
247e211d6b59        alpine              "ls"                45 seconds ago      Exited (0) 44 seconds ago                       blissful_mclean
daa39f115422        alpine              "ls"                47 seconds ago      Exited (0) 46 seconds ago                       cranky_lamport
```
実行の終わったコンテナでも`docker restart`で再度起動できるためデフォルトでは自動で消えてくれないです。もし停止と同時にコンテナを消去したければ `--rm`オプションを付けて`docker run`すれば良いですが、それでもビルドに失敗したときなどにコンテナが残るため自然と溜まっていってしまいます。

そんな時に`docker container prune`を実行すると、imageの時と同様に本当に消すかどうかを聞かれ、`y`と答えると綺麗サッパリ消してくれます。対象は「停止中」のコンテナ。

```
$ docker container prune
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Deleted Containers:
be1381d7199a6dcf1083c4ef9792ae78e04044743a6c6e1ce3cd517dc22479ab
b9733c0160d16858ba2ef5ee877a23c3249e5313787debdb2c51e5863fdb2d31
f71351f2a878ab343c594675bb4cdff9966220a6d69fbb4d65e22205246525f1
4938b15ff8da0909eef220ce5254647e829fb6257a8ea8cbc9cd8361125f419b
5802c2477245b4f40fa6321a8780fbaac2d194e732b0813f58e305a2a9b444d2
247e211d6b59ce9bc1dbbd0c132d5877e81340c1b9e65d2eff1cf768bd03ebe7
daa39f1154227503b8c0da00153113d25c6305975b8801fbfa5aa317059398e7

Total reclaimed space: 3.831MB
```

そして、このコマンドにもオプションがいくつかあります。

#####`-f` `--force`
これは`docker image prune`と同様で、実行時に「消しちゃうけど大丈夫？」という確認をスキップするためのオプションです。

#####`--filter`
消去するコンテナを選択する追加の条件を指定できます。指定方法もimageと同じで`until`と`label`。ラベルは基本は元のイメージのものを引き継ぎますが、`docker run`で実行する時に上書きもできるのでその点は要注意かもしれません。

##ボリュームの断捨離

ボリュームも気をつけていないと溜まっていってしまうものの一つです。`docker volume create`コマンドで明示的に作ったり、あるいは `docker run -v <desitination_path>`やDockerfile内のVOLUMEコマンドで無名のボリュームを作ったりしますが、それはコンテナを消去しても自動的には消えません (`run --rm`を使った場合に一部例外あり)。

`docker volume ls`をすると今現在システムで抱えているボリュームのリストが出てきます。


```
$ docker volume ls
DRIVER              VOLUME NAME
local               0f181d792f4313abb670110c0ee079910fa3ca776463b515093a4fea4219dcc9
local               1d383f8ab7ec848c9e8e7969969f88a92d46d20592e7f3aa6ddb590feab2fb55
local               2c3afd63e16ecbb33bc54e8e7545ee1be5d5568236203dd69c5eeb38e98f9c72
...
```

ここで`docker volume prune`をしてあげると、使われていない(現存するコンテナから参照されていない)ボリュームはサクッといなくなります。

```
$ docker volume prune
WARNING! This will remove all local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
...

Total reclaimed space: 1.528GB
```

私は1.5GBものスペースを無駄に保持していたようです (^^;

他のpruneコマンド同様に、`docker volume prune`も `-f/--force`と`--filter`オプションが使えます。使い方は一緒なので説明は割愛させていただきます。

##ネットワークの断捨離
上記のイメージ、コンテナ、ボリュームに比べると溜まりにくいしそもそもデータ量もそれほどないので使う機会は少ないかも知れませんが、`docker network create`で明示的に作成したものは自動では消えないのでルーティングテーブルをシンプルに保ちたい方は定期的に`docker network prune`をやると良いかも知れません。

`docker-compose up`で複数コンテナを立ち上げたときには自動でデフォルトのbridgeネットワークが作られますが、`docker-compose down`するとそのネットワークは自動削除されるのでホントに出番が少なさそう(一方、自動で作られたボリュームは残り続けます…)。

基本は現存するコンテナから参照されていないネットワークが削除されますが、デフォルトで作成されている `bridge`、`host`、`none`は消えずに残るようです。

##まとめて断捨離
これまで、個別にpruneするコマンドを紹介してきましたが、実はまとめてキレイにしてくれるコマンドも存在します。それが、`docker system prune`。これをやると、

* 停止中のコンテナ
* どのコンテナからも使われていないネットワーク
* 宙ぶらりんな(タグがなくどのコンテナからも参照されていない)イメージ
* ビルドキャッシュ

を全て消してくれます。最後のはビルドの速度を早めるために持っている中間結果を破棄するもので、ビルド時に`--no-cache`をつけるとそれを破棄した状態からビルドをはじめてくれますが、当然ビルドが終わった後にはキャッシュが残るわけで、キャッシュだけを削除するというコマンドは今まで無かったように思います。

さらに、`--volumes`オプションをつけて起動すると

* どのコンテナからも使われていないボリューム

も合わせて消してくれます。また、これにも`-f/--force`オプションがあるので「実行するか一々確認しなくても良いよ」という男気のある方は是非ともご活用ください(笑)。

なお、個別にpruneしていくのも良いのですが、コンテナ以外のイメージ、ボリューム、ネットワークはコンテナから参照されていることを消す条件にしているので、先に不要なコンテナが消えていないと消せません。そういった順序関係を気にしたくなかったら`docker system prune`で一気にやってしまうのも手かなと思います。ただし、必要なモノを消してしまう可能性もあるのであくまでも自己責任でお願いします。

#まとめ
pruneコマンドに関してまとめてみました。書き始めたときにはすごく短いエントリになるかと思っていたら意外と長くなりました(^^;
ただ、書きながらボリュームやネットワークに関して自分はあまりわかっていなかったと再認識し、そういう意味でも書いた意味があったかなと思います。