気づかないうちにDockerイメージのサイズが大きくなってることってありますよね。最初は1GB切ってたのに、いつの間にやら1.xxGBに。Dockerfileを見て「あぁ、ここだな」っと気づける人は、そもそもイメージサイズを大きくしないので今回は対象外です。Dockerfileを見ても「ようわからん」という方はdiveというツールを使ってみてください。
dive ???
DockerはDockerfileの各命令ごとにレイヤーを作成します。命令っていうのはDockerfile内のFROM
やRUN
などです。このレイヤーが積み重なって1つのイメージになっています。
diveはDockerイメージのレイヤーに含まれるファイルを可視化できるツールです。ファイルを可視化できると何がいいのかというと、それぞれのレイヤーになるゴミファイルを発見できます。例えば、あるレイヤーで生まれたファイルを別のレイヤーで削除しても、すべてのレイヤーが積み重なって1つのイメージなので、イメージサイズは変わりません。これを解消できます。
インストール
インストールは以下を参考にしてください。
https://github.com/wagoodman/dive#installation
私の環境はMacなのでbrew install dive
しました。Dockerイメージも公開されてるので、マシンにインストールしたくない方はこちらを使ってもよさそうです。今回はインストールした前提で話を進めます。
試す
まずはゴミファイルが残った状態のDockerイメージを作ります。以下のファイルをDockerfile
にというファイル名に保存してください。
FROM debian:bullseye RUN apt-get update && apt-get install -y bash RUN apt-get clean && rm -rf /var/lib/apt//var/lib/apt/lists/* ENTRYPOINT ["/usr/bin/bash"]
ビルド
docker build -t bash .
今回使ったDockerfileは1つ目のRUNでbashをインストールし、2つ目のRUNでキャッシュの削除をしています。キャッシュを削除してるのでイメージサイズは節約できていそうですが、Dockerイメージは全てのレイヤーが積み重なっていることを思い出してください。つまり、1つ目のRUNで作成されたレイヤーには、まだキャッシュが残った状態です。これでは、Dockerイメージのサイズは節約できません。
実際にdiveみてみましょう。
dive bash
こんな感じの画面になります。
それぞれがどういうものか見ていくと
赤枠がレイヤーについての情報です。カーソルキー上下でレイヤーを選択できます。
緑枠がレイヤーに含まれるファイルの一覧です。こちらもカーソルキー上下でファイルを選択できます。
黄枠がイメージについての情報です。イメージのサイズや追加されたファイルの一覧が見れます。
LayersとCurrent Layer ContentsはTabキーで操作対象を変更できます。デフォルトではLayersが選択されています。LayersかCurrent Layer Contentsの左側にある●
が、現在の操作対象です。
黄枠のImage Detailsをみるとわかると思いますが、2つ目のRUNで削除したはずの/var/lib/apt/lists/*
のファイルが残っています。これがイメージサイズが膨れ上がる原因ですね。
では、実際にファイルがどうなっているのか見てみましょう。
Layersで1つ目のRUNを見てみます。Size
の列をみると、このレイヤーが18MBあることがわかります。
Current Layer Contentsを見ていきましょう。カラフルに表示されています。黄色が変更のあったファイル/ディレクトリ、緑色が追加、赤が削除ファイルです。
余談ですが、この色の説明が見つけられなくてちょっと困ってたんですが、ソースコードに書いてありました。
https://github.com/wagoodman/dive/blob/master/dive/filetree/file_node.go#L20
話を戻して、このレイヤーにはキャッシュが残っているのがわかります。
Image Detailsを見てみると、このキャッシュファイルだけで18MBくらいつかってるのがわかりますね。つまり、このレイヤーのサイズのほとんどがキャッシュということになります。
2つ目のRUNでは以下のようにキャッシュファイルが赤文字になっていて消されたのがわかります。Layer DetailsのCommandを見ても削除コマンドが実行されています。
じゃあどうすればよかったのかというと
FROM debian:bullseye RUN apt-get update \ && apt-get install -y bash \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* ENTRYPOINT ["/bin/bash"]
このように、1つのRUNで削除まで実行してあげればOKです。キャッシュができたら同じレイヤーで消してあげることで、1つのレイヤーのサイズを削減できます。
docker build -t bash-slim .
中身を見てみるとこんな感じです。
dive bash-slim
レイヤーが1つ減って、1つのレイヤーのサイズが5.6kBになりました。Image Detailsを見ても余計なファイルはなさそうです。Current Layer Contentsの方にもキャッシュがなくなっていますね。
まとめ
diveを使って不要なファイルを探してみました。今回は容量削減の方で説明しましたが、他にも、どこかのレイヤーに機密情報を置き忘れて、それを探す時にも使えそうですね。レイヤーの中身は結構簡単に取れてしまうので、気をつけたいものです。