【少しでも楽してコンテナイメージを減らしたかった】コンテナイメージサイズを縮小するためにDive使ってイメージを分析してみた

こんばんわ、札幌のヨシエです。
コンテナを使用する時に必要となるものがコンテナイメージです。
このコンテナイメージは小さければ小さいほど正義として扱われ、この正義を通すために人はアレやコレやと試行錯誤を繰り返してコンテナイメージ縮小の旅に出ます。
その旅に持っていけると良いツールとしてDiveを試してみたので紹介させていただきます。

検証用コンテナイメージ

今回の検証は軽量コンテナイメージを元にして、Dockerfileによるレイヤーを重ねる形で動作を確認したいと思います。
利用したコンテナイメージはamazonlinux:latestを使用しました。

% docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
amazonlinux         latest              dc34c260f454        5 weeks ago         163MB

Diveのインストール

brew install dive

Diveの基本使用

以下のコマンドにてコンテナイメージを指定します。

% dive amazonlinux:latest

このような画面が表示されました。

画面のそれぞれを確認します。

①イメージのレイヤー一覧

コンテナイメージのレイヤー情報が出力されます。
この画面ではamazonlinuxのイメージを見ているだけなので1レイヤーのみですがDockerfileによるbuildを行うとレイヤー数が増えます。

②レイヤーダイジェスト値

コンテナのレイヤーダイジェスト値とコマンド情報が出力されます。

③イメージファイル情報

目玉の一つがこちらのイメージファイル情報です。   コンテナイメージは複数のレイヤーを重ねることによって、イメージの肥大化要因になり得るファイルが発生します。
実験的機能と明記されておりますが、余計なファイル情報の有無をスコアとサイズによって表記してくれます。

  • イメージ自体のサイズ:docker imagesで確認できるイメージサイズ
  • 余分なスペースサイズ:余分と思われるファイルサイズ数を表示
  • コンテナイメージの効率スコア:コンテナイメージがどれだけ効率的な作りとなっているかを示すスコア

④イメージ内ディレクトリ構成

2つ目の目玉がこちらです。
コンテナイメージのディレクトリ構成を確認することが出来ます。  

動きを見てみる(ファイル作成編)

以下のようなDockerfileを用意してみました。
単純に/tmp配下に1から10の連番を付与したファイルを作成します。

FROM amazonlinux:latest

RUN for i in {1..10} ; do touch /tmp/testfile-${i} ; done
docker build . -t amazonlinux:test

このような結果になりました。

  • 今回テストファイルを実行したコマンドがレイヤー一覧に追加
  • ディレクトリ一覧の/var配下に連番のtestfileが緑色で表示

ファイルが追加された時は緑色でハイライトされます。

動きを見てみる(ファイル削除編)

次に作成した連番ファイルであるtestfile-6 ~ testfile-10を削除します。

FROM amazonlinux:latest

RUN for i in {1..10} ; do touch /tmp/testfile-${i} ; done

RUN for i in {6..10} ; do rm -f /tmp/testfile-${i} ; done
docker build . -t amazonlinux:test

このような結果になりました。

  • ファイル作成編と同様に今回追加したレイヤーがレイヤー一覧に追加
  • 削除されたtestfile-6 ~ testfile-10が赤色で表示

ファイルが削除された時は赤色でハイライトされます。

動きを見てみる(ファイル変更編)

作成済みのtestfile-1 ~ testfile-5/配下にmodify-testfile-1 ~ modify-testfile-5へ変更します。

FROM amazonlinux:latest

RUN for i in {1..10} ; do touch /tmp/testfile-${i} ; done

RUN for i in {6..10} ; do rm -f /tmp/testfile-${i} ; done

RUN for i in {1..5} ; do mv /tmp/testfile-${i} modify-testfile-${i} ; done
docker build . -t amazonlinux:test

このような結果になりました。

  • 元々存在していた場所からファイルが移動(削除)されたため、testfile-1 ~ testfile-5が赤色で表示
  • リネームされたファイルは新規追加ファイルとして緑色で表示

動きを見てみる(ファイル容量変更)

最後にddコマンドによって、modify-testfile-1のファイルをテストファイルに置き換えます。

FROM amazonlinux:latest

RUN for i in {1..10} ; do touch /tmp/testfile-${i} ; done

RUN for i in {6..10} ; do rm -f /tmp/testfile-${i} ; done

RUN for i in {1..5} ; do cp /tmp/testfile-${i} testfile-${i} ; done

RUN for i in {1..5} ; do mv /tmp/testfile-${i} /tmp/modify-testfile-${i} ; done

RUN dd if=/dev/zero of=/tmp/modify-testfile-1 bs=10M count=1
docker build . -t amazonlinux:test

この様な結果になりました。

  • ファイルサイズが変更されたため、modify-testfile-1が黄色で表示

便利な使い方

操作されたファイルのみ出力

Tabキーを押下することで右ペインを操作することができます。
この時に以下のキーを押下することで追加/変更/削除/未変更をフィルタリング出来ます。

キーマップ フィルタ
Ctrl + A 追加ファイル
Ctrl + R 削除ファイル
Ctrl + M 変更ファイル
Ctrl + U 未変更ファイル

※Ctrl + Bはファイルパーミッション等を表示/非表示が可能です

dive buildを使ってdocker builddiveをシーケンシャルに起動

dive build -t <イメージ名> .を実行することでdocker buildを実行した後にdiveを開くことが出来ます。
.部分はdocker buildコマンドのDockerfileパスの指定と同じなのでDockerfileが配置されているディレクトリ指定が可能です

% dive build -t amazonlinux:test .
Building image...
Sending build context to Docker daemon  18.19MB
Step 1/8 : FROM amazonlinux:latest
 ---> dc34c260f454
Step 2/8 : RUN for i in {1..10} ; do touch /tmp/testfile-${i} ; done
 ---> Using cache
 ---> b6bb763d53e5
Step 3/8 : RUN for i in {6..10} ; do rm -f /tmp/testfile-${i} ; done
 ---> Using cache
 ---> 80fe5e4e730a
Step 4/8 : RUN for i in {1..5} ; do cp /tmp/testfile-${i} testfile-${i} ; done
 ---> Using cache
 ---> 11339d266087
Step 5/8 : RUN for i in {1..5} ; do mv /tmp/testfile-${i} /tmp/modify-testfile-${i} ; done
 ---> Using cache
 ---> eff7d1bd1402
Step 6/8 : RUN dd if=/dev/zero of=/tmp/modify-testfile-1 bs=10M count=1
 ---> Using cache
 ---> 38e8a0c4ce0b
Step 7/8 : RUN dd if=/dev/zero of=/tmp/modify-testfile-2 bs=30M count=1
 ---> Using cache
 ---> 31464c5894d0
Step 8/8 : RUN dd if=/dev/zero of=/tmp/modify-testfile-3 bs=40M count=1
 ---> Using cache
 ---> 27470ae7cd0f
Successfully built 27470ae7cd0f
Successfully tagged amazonlinux:test
Fetching image... (this can take a while with large images)
Parsing image...
Analyzing image...
Building cache...

環境変数を指定してCI環境に対応した分析を行う

CI=trueという環境変数を渡してdiveを実行すると、予め指定された余分なファイルサイズや分析結果を評価してリターンコードをで合否を出力します。
例えばCodeBuildのbuildspecにこのリターンコードを判定する文を追加すると面白いと思います。

合格パターン

% CI=true dive amazonlinux:test ; echo $?
  Using default CI config
Fetching image... (this can take a while with large images)
Parsing image...
Analyzing image...
  efficiency: 100.0000 %
  wastedBytes: 0 bytes (0 B)
  userWastedPercent: 0.0000 %
Inefficient Files:
Count  Wasted Space  File Path
    2           0 B  /tmp/testfile-5
    2           0 B  /tmp/testfile-4
    2           0 B  /tmp/testfile-3
    2           0 B  /tmp/testfile-2
    2           0 B  /tmp/testfile-1
    2           0 B  /tmp/testfile-9
    2           0 B  /tmp/testfile-8
    2           0 B  /tmp/testfile-7
    2           0 B  /tmp/testfile-6
    2           0 B  /tmp/testfile-10
Results:
  PASS: highestUserWastedPercent
  SKIP: highestWastedBytes: rule disabled
  PASS: lowestEfficiency
Result:PASS [Total:3] [Passed:2] [Failed:0] [Warn:0] [Skipped:1]
0

不合格パターン

% CI=true dive amazonlinux:test ; echo $?
  Using default CI config
Fetching image... (this can take a while with large images)
Parsing image...
Analyzing image...
  efficiency: 60.8036 %
  wastedBytes: 104857600 bytes (105 MB)
  userWastedPercent: 46.4845 %
Inefficient Files:
Count  Wasted Space  File Path
    2         42 MB  /tmp/modify-testfile-4
    2         32 MB  /tmp/modify-testfile-3
    2         21 MB  /tmp/modify-testfile-2
    2         10 MB  /tmp/modify-testfile-1
    2           0 B  /tmp/testfile-5
    2           0 B  /tmp/testfile-4
    2           0 B  /tmp/testfile-3
    2           0 B  /tmp/testfile-2
    2           0 B  /tmp/testfile-1
    2           0 B  /tmp/testfile-9
    2           0 B  /tmp/testfile-8
    2           0 B  /tmp/testfile-7
    2           0 B  /tmp/testfile-6
    2           0 B  /tmp/testfile-10
Results:
  FAIL: highestUserWastedPercent: too many bytes wasted, relative to the user bytes added (%-user-wasted-bytes=0.4648447895112585 > threshold=0.1)
  SKIP: highestWastedBytes: rule disabled
  FAIL: lowestEfficiency: image efficiency is too low (efficiency=0.6080361515417759 < threshold=0.9)
Result:FAIL [Total:3] [Passed:0] [Failed:2] [Warn:0] [Skipped:1]
1

最後に

先日のecspressoに続きまして、今回はDiveというレイヤー分析ツールを触ってみました。
コンテナイメージの削減はコンテナ環境を運用する時に重要なポイントとなり、様々なコマンドを駆使すること形が多いと思います。
今回のツールでは手っ取り早く余分と考えられる部分を抽出出来るので使いやすいツールと思ってます。
少しでもコンテナイメージの削減を行いたい人に届くと嬉しいです。