Dockerイメージ分析ツール「dive」を利用してDockerイメージを軽量化する
はじめに
Docker イメージサイズは小さければ小さいほど、Push と Pull の高速化につながり嬉しいです。
docker history
によってイメージレイヤーごとのサイズは分かりますが、どのレイヤーのどのファイルのサイズが大きいかは分かりません。
$ docker history maven:3-amazoncorretto-11 IMAGE CREATED CREATED BY SIZE COMMENT eb8a5bbcd061 12 days ago /bin/sh -c #(nop) CMD ["mvn"] 0B <missing> 12 days ago /bin/sh -c #(nop) ENTRYPOINT ["/usr/local/b… 0B <missing> 12 days ago /bin/sh -c #(nop) COPY file:2bbb488dd73c55d6… 327B <missing> 12 days ago /bin/sh -c #(nop) COPY file:1b3da5c58894f705… 1.65kB <missing> 12 days ago /bin/sh -c #(nop) ENV MAVEN_CONFIG=/root/.m2 0B <missing> 12 days ago /bin/sh -c #(nop) ENV MAVEN_HOME=/usr/share… 0B <missing> 12 days ago |4 BASE_URL=https://apache.osuosl.org/maven/… 11.3MB <missing> 12 days ago |4 BASE_URL=https://apache.osuosl.org/maven/… 200MB <missing> 12 days ago /bin/sh -c #(nop) ARG BASE_URL=https://apac… 0B <missing> 12 days ago /bin/sh -c #(nop) ARG SHA=c35a1803a6e70a126… 0B <missing> 12 days ago /bin/sh -c #(nop) ARG USER_HOME_DIR=/root 0B <missing> 12 days ago /bin/sh -c #(nop) ARG MAVEN_VERSION=3.6.3 0B <missing> 12 days ago /bin/sh -c #(nop) ENV JAVA_HOME=/usr/lib/jv… 0B <missing> 12 days ago /bin/sh -c #(nop) ENV LANG=C.UTF-8 0B <missing> 12 days ago |1 version=11.0.8.10-1 /bin/sh -c set -eux … 279MB <missing> 12 days ago /bin/sh -c #(nop) ARG version=11.0.8.10-1 0B <missing> 3 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B <missing> 3 weeks ago /bin/sh -c #(nop) ADD file:788af9048b1c16334… 163MB
dive
A tool for exploring a docker image, layer contents, and discovering ways to shrink the size of your Docker/OCI image.
このツールでは、レイヤーごとにどのファイルが追加・削除されたのか、どれくらいのサイズなのかが分かります。 Go で書かれていて、20000 スターくらいついています。
インストール
# brewでインストール $ brew install dive # go getでインストール $ go get github.com/wagoodman/dive
Dockerでも実行できたり他のインストール方法もREADMEに書いてあります。
使い方
# Analyze $ dive <your-image-tag> # Build & Analyze $ dive build -t <some-tag> .
以上のコマンドでツールが起動します。 ターミナル上であれこれできるのをTUIというようです。
dive で軽量化
今回は、maven:3-amazoncorretto-11
を軽量化します。
dive の実際の使い方を紹介しながら説明します。
まず、上のリポジトリのdocker-maven/amazoncorretto-11/Dockerfile
をビルドし、dive
を起動します。
$ docker build -t before-maven-amazoncorretto-11 .
$ dive before-maven-amazoncorretto-11
いきなり画面にたくさん表示されて最初は訳がわからないと思います。
- 画面左上
Layers
:どのレイヤーまでを調査するか選択 - 画面左中
Layer Details
: そのレイヤーでなにを実行しているかを表示 - 画面左下
Image Details
: イメージ全体の情報を表示 - 画面右
Current Layer Contents
:Layers
で指定したレイヤーに含まれているファイルツリーをサイズと共に表示
Tab
でLayers
とCurrent Layer Contents
を移動しながら使います。
最初はディレクトリが開かれていて画面右にファイルがたくさん表示されて見づらいので、Space
でディレクトリ閉じていきます。
^Space
で一気に閉じられるようですが、Mac のショートカットと衝突して使えませんでした。
見やすくなりました。
Layers
で上下に移動すると、それに対応してCurrent Layer Contents
も変化しディレクトリやファイズのサイズの変化が分かります。
Layers
を確認すると、サイズ大きく増えるボトルネックとなりうるレイヤーは上から 3 つと分かります。
上から1 番目と 2 番目のレイヤーはAmazon Linux の/usr
と amazoncorretto の/usr/lib/jvm
の追加であり、これはベースイメージなので改善できません。
上から3 番目レイヤーでは、/var/cache/yum
が 82B から 195MB へ増加しました。
このレイヤー全体のサイズは 206MB なので、ほとんどが/var/cache/yum
と分かります。
黄色は選択したレイヤーによってサイズが変わった部分を表しています。
このレイヤーではyum install -y tar which gzip
が実行されています。
これらのコマンドは実行できれば問題ないので 195MB の膨大なキャッシュは不要です。
このように dive を利用することで不要なファイルを発見することができました。 あとは、Dockerfile 上で不要なファイルを削除すれば完了です。
RUN yum install -y tar which gzip \ && rm -rf /var/cache/yum/* \ && yum clean all
このようにすれば、キャッシュは削除できます。
&&
でつなげないと余計なレイヤーが追加されるだけで軽量化には繋がりません。
元々のイメージサイズは 659MB でしたが、変更後は 464MB に削減することができました。
ちょうど、/var/cache/yum
の 195MB 分です。
ここでの修正はプルリクエストとして出してマージされました。
Mavenの公式DockerイメージにコントリビュートしてOSSデビューした、嬉しい https://t.co/RxBbrRem5f
— kotaroooo0 (@kotaroooo0) 2020年7月28日
おわりに
diveによってレイヤーのディレクトリごと、ファイルごとにサイズの変化を観察することができました。
かなり強力なツールだなと感じました。
欲を言うなら、名前順でソートではなくサイズ順でソートできるようになったり、^Space
でなく違うコマンドでディレクトリが畳めたら嬉しいと思いました。
また、コマンドによってどんなファイルが生成されるか分かりやすいため、パッケージマネージャのコマンドがなにをしているかも理解が進むという嬉しい副作用もありました。
今回は dive を使うのが半分目的だったのでdive -> Dockerfileの順で見たのですが、Dockerfile -> diveの順で見ればyum install
のキャッシュを削除すればよさそうだなと当たりをつけることができたかもしれません。
まさか、公式Dockerイメージにこんなにシンプルな修正箇所が残っているとは思いませんでした。
偶然にもOSSへ貢献することができラッキーでした。
過去に書いた Docker ビルド関係の記事
GitHub Actionsを例にCI環境でのマルチステージビルドのキャッシュの活用について🐳 - 🤖
GitHub ActionsでのDockerビルドをキャッシュで高速化する - 🤖
Dockerfileを正しく書けるように指摘してくれる静的解析ツール「hadolint」 - 🤖
Dockerイメージのビルドで使うキャッシュの種類(レイヤーキャッシュ, BuildKit, CI) - 🤖