🤖

🤖

:gijutsu_burogu:

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 スターくらいついています。

github.com

インストール

# 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 の実際の使い方を紹介しながら説明します。

github.com

まず、上のリポジトリのdocker-maven/amazoncorretto-11/Dockerfileをビルドし、diveを起動します。

$ docker build -t before-maven-amazoncorretto-11 .
$ dive before-maven-amazoncorretto-11

f:id:kotaroooo0:20200728232512p:plain
dive起動時の画面

いきなり画面にたくさん表示されて最初は訳がわからないと思います。

  • 画面左上Layers:どのレイヤーまでを調査するか選択
  • 画面左中Layer Details: そのレイヤーでなにを実行しているかを表示
  • 画面左下Image Details: イメージ全体の情報を表示
  • 画面右Current Layer Contents: Layersで指定したレイヤーに含まれているファイルツリーをサイズと共に表示

TabでLayersとCurrent Layer Contentsを移動しながら使います。 最初はディレクトリが開かれていて画面右にファイルがたくさん表示されて見づらいので、Spaceでディレクトリ閉じていきます。 ^Spaceで一気に閉じられるようですが、Mac のショートカットと衝突して使えませんでした。

f:id:kotaroooo0:20200728223238p:plain
画面右はディレクトリを閉じるとスッキリ

見やすくなりました。 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と分かります。 黄色は選択したレイヤーによってサイズが変わった部分を表しています。

f:id:kotaroooo0:20200801152712p:plain
/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 分です。

ここでの修正はプルリクエストとして出してマージされました。 github.com

おわりに

diveによってレイヤーのディレクトリごと、ファイルごとにサイズの変化を観察することができました。 かなり強力なツールだなと感じました。 欲を言うなら、名前順でソートではなくサイズ順でソートできるようになったり、^Space でなく違うコマンドでディレクトリが畳めたら嬉しいと思いました。

また、コマンドによってどんなファイルが生成されるか分かりやすいため、パッケージマネージャのコマンドがなにをしているかも理解が進むという嬉しい副作用もありました。

今回は dive を使うのが半分目的だったのでdive -> Dockerfileの順で見たのですが、Dockerfile -> diveの順で見ればyum installのキャッシュを削除すればよさそうだなと当たりをつけることができたかもしれません。 まさか、公式Dockerイメージにこんなにシンプルな修正箇所が残っているとは思いませんでした。 偶然にもOSSへ貢献することができラッキーでした。

過去に書いた Docker ビルド関係の記事

GitHub Actionsを例にCI環境でのマルチステージビルドのキャッシュの活用について🐳 - 🤖

GitHub ActionsでのDockerビルドをキャッシュで高速化する - 🤖

Dockerfileを正しく書けるように指摘してくれる静的解析ツール「hadolint」 - 🤖

Dockerイメージのビルドで使うキャッシュの種類(レイヤーキャッシュ, BuildKit, CI) - 🤖

DockerイメージのビルドをBuildKitで並列実行し高速化する - 🤖

Go製CLIツールを使うDockerイメージをダイエットしてみた - 🤖