とことんDevOps | 日本仮想化技術が提供するDevOps技術情報メディア

DevOpsに関連する技術情報を幅広く提供していきます。

日本仮想化技術がお届けするとことんDevOpsでは、DevOpsに関する技術情報や、日々のDevOps業務の中での検証結果など、DevOpsのお役立ち情報をお届けします。
主なテーマ: DevOps、CI/CD、アジャイル開発、コンテナ開発など
各種SNSのフォローもよろしくお願いいたします。

dive式ダイエット

気づかないうちにDockerイメージのサイズが大きくなってることってありますよね。最初は1GB切ってたのに、いつの間にやら1.xxGBに。Dockerfileを見て「あぁ、ここだな」っと気づける人は、そもそもイメージサイズを大きくしないので今回は対象外です。Dockerfileを見ても「ようわからん」という方はdiveというツールを使ってみてください。

dive ???

github.com

DockerはDockerfileの各命令ごとにレイヤーを作成します。命令っていうのはDockerfile内のFROMRUNなどです。このレイヤーが積み重なって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を使って不要なファイルを探してみました。今回は容量削減の方で説明しましたが、他にも、どこかのレイヤーに機密情報を置き忘れて、それを探す時にも使えそうですね。レイヤーの中身は結構簡単に取れてしまうので、気をつけたいものです。