概要
-
Dockerfile
のVOLUME
は予期せずディスクを食い潰す原因になる - 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 と呼ばれるもので、以下のような場合に作成されます。
-
Dockerfile
にVOLUME
指示を書いた場合 -
docker-compose.yml
やdocker 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 イメージの中にありました。
VOLUME /var/lib/mysql
Dockerfile
で一度指定された VOLUME
は、派生イメージやコマンドラインオプションなどで解除できないため、
ベースイメージを使用する全てのユーザーが同様の問題に当たる可能性があります。
これは MySQL 以外にも多くのメジャーな公式イメージで issue として挙がっており、現在でも解決されていません。
- Why is there volume for data in the first place? · Issue #255 · docker-library/mysql
- Volumes should not be defined in base images · Issue #404 · docker-library/postgres
- Volumes should not be defined in base images · Issue #140 · docker-library/redis
- Should not declare VOLUME for /data/db · Issue #306 · docker-library/mongo
- VOLUME declaration can result in difficult to diagnose misbehavior · Issue #410 · docker-library/rabbitmq
対処法
Dockerfile から VOLUME を削除
根本的に volume を作成させないための方法です。
既に述べた通り Dockerfile
の VOLUME
は解除できないので、イメージの使用方法の選択肢を狭めてしまいます。
volume の設定は Dockerfile
の中ではなく、実行時に docker コマンドや docker-compose.yml
で指定する方が良いでしょう。
問題は、 MySQL 公式のイメージなど、管理外の Dockerfile
で VOLUME
が指定されている場合です。
この場合は自身でレポジトリをフォークして 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 をまとめて削除してくれます。
- 使用されていない volume のリストを取得
- 名前が英数字 64 文字の anonymous volume だけをフィルタ
- 該当する volume を削除
ただし、本当に必要な volume まで削除してしまわないか事前によく確認しておきましょう。