ディスクを食い潰す Docker volume の落とし穴


概要

  • DockerfileVOLUME は予期せずディスクを食い潰す原因になる
  • MySQL など多数の公式 Docker イメージで VOLUME が使われているため要注意

遭遇した問題

ある時、CI サーバーのディスクが圧迫されており、その大部分を Docker volume が占めていることがわかりました。

$ docker system df
TYPE                TOTAL               ACTIVE              SIZE                RECLAIMABLE
Images              14                  5                   6.787GB             3.283GB (48%)
Containers          8                   4                   137.5MB             131.4MB (95%)
Local Volumes       1281                7                   224.8GB             224.3GB (99%)
Build Cache         0                   0                   0B                  0B

更に詳しく調べてみると、ランダムな名前でほぼ同じ中身の volume が大量に残っていて、無駄にディスク容量を食っているようでした。

$ docker system df -v
...
Local Volumes space usage:

VOLUME NAME                                                        LINKS               SIZE
68d96aaf9e8a9098c4ab38c26315f4d25b02b96a51eaf70a7037f655e0dccba4   0                   226.6MB
cdfb0a70f73d997bf5f7abee7d37adea98891d938da17bda8ae6d25ca5decc71   0                   228.6MB
f043ee71a3a318ae6479bf0205d3192666ca58b43582572b41046421f502cdef   0                   226.6MB
8d15e829f575564683006cb65340687423749fc6b1e52a5fe0e62c81ed40f2e4   0                   228.6MB
...

問題の原因

ランダムな名前の volume は anonymous volume と呼ばれるもので、以下のような場合に作成されます。

  • DockerfileVOLUME 指示を書いた場合
  • docker-compose.ymldocker run のオプションで -v /path/in/container のように指定した場合

前述のケースでは CI のテストに Docker を使用し、終了後にコンテナを削除して後始末していたため、
起動時に毎回 anonymous volume が新しく作成され、削除されることも再利用されることもなくゴミとして溜まっていたわけです。

一度コンテナから切り離された anonymous volume の出所を特定するのは厄介ですが、
適当なコンテナに volume をアタッチするなどして中身を調べれば手がかりが得られます。

$ docker run -it --rm -v <volume の名前>:/volume busybox sh
$ ls /volume
auto.cnf     client-key.pem  ibdata1         private_key.pem  sys
ca-key.pem   ib_buffer_pool  ibtmp1          public_key.pem
ca.pem       ib_logfile0     mysql           server-cert.pem
client-cert.pem  ib_logfile1     performance_schema  server-key.pem

今回 anonymous volume を作り出していた大元の原因箇所は、ベースとして使用する MySQL 公式の Docker イメージの中にありました。

Dockerfile
VOLUME /var/lib/mysql

Dockerfile で一度指定された VOLUME は、派生イメージやコマンドラインオプションなどで解除できないため、
ベースイメージを使用する全てのユーザーが同様の問題に当たる可能性があります。
これは MySQL 以外にも多くのメジャーな公式イメージで issue として挙がっており、現在でも解決されていません。

対処法

Dockerfile から VOLUME を削除

根本的に volume を作成させないための方法です。

既に述べた通り DockerfileVOLUME は解除できないので、イメージの使用方法の選択肢を狭めてしまいます。
volume の設定は Dockerfile の中ではなく、実行時に docker コマンドや docker-compose.yml で指定する方が良いでしょう。

問題は、 MySQL 公式のイメージなど、管理外の DockerfileVOLUME が指定されている場合です。
この場合は自身でレポジトリをフォークして Dockerfile を書き換える以外に解決策がありません。
しかしこの方法だと、管理すべきコードが増え、フォーク元の変更に追随する手間が発生するためお勧めできません。

定期的に volume を掃除

上記の方法が使えない場合、対症療法として volume を定期的に削除する手があります。

docker volume ls -f dangling=true --format "{{ .Name }}" | grep -E '^[a-z0-9]{64}$' | xargs --no-run-if-empty docker volume rm

例えばこのコマンドを実行すると、以下の流れで anonymous volume をまとめて削除してくれます。

  1. 使用されていない volume のリストを取得
  2. 名前が英数字 64 文字の anonymous volume だけをフィルタ
  3. 該当する volume を削除

ただし、本当に必要な volume まで削除してしまわないか事前によく確認しておきましょう