diveとは

dive は、Dockerイメージをレイヤーごとに分析するツール。
各レイヤーで追加・変更・削除されたファイルを確認できるほか、無駄なファイルによって肥大化した容量(Wasted Space)を検出できる。

インストール

Dockerイメージとして提供されているため、Dockerさえあればインストール不要で実行できる。

$ docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock wagoodman/dive <イメージ名>

macOSの場合はHomebrewでインストールも可能。

$ brew install dive

基本的な使い方

分析したいイメージ名を指定して実行する。

$ dive node:20-alpine

起動するとTUI(ターミナルUI)が開き、左側にレイヤーの一覧、右側に選択中のレイヤーで変更されたファイルツリーが表示される。
ファイルツリーでは、追加されたファイルは+、削除されたファイルは-、変更されたファイルはΔのマークが付く。

画面下部にはイメージ全体のTotal Image sizeと、削除しても実行に影響しない無駄な容量を示すPotential wasted space、イメージの効率を示すImage efficiency scoreが表示される。

操作方法

TUIでは以下のキー操作が使える。

キー機能
Tabレイヤー一覧とファイルツリーの切り替え
↑↓ / J K1行ずつ移動
PageUp/PageDown / U Dページ単位で移動
Ctrl+A集約された変更を表示(レイヤー一覧選択時) / 追加ファイルの表示切り替え(ファイルツリー選択時)
Ctrl+L現在選択中のレイヤーの変更のみ表示
Spaceディレクトリの展開/折りたたみ
Ctrl+Spaceすべてのディレクトリの展開/折りたたみ
Ctrl+Fファイルをパスでフィルタリング
Ctrl+C終了

Ctrl+Aでレイヤーごとの変更ではなく、イメージ全体でファイルがどう変化したかを集約して確認できるため、どのレイヤーで肥大化したファイルが追加されたかを追いやすい。

無駄なレイヤーを検出する例

パッケージ追加・ファイルコピー・不要ファイル削除をそれぞれ別レイヤーで行う、以下のようなDockerfileで試す。

FROM alpine:3.20
RUN apk add --no-cache curl
COPY big-file.txt /big-file.txt
RUN rm /big-file.txt

RUN rmは最後のレイヤーでファイルを削除しているだけであり、COPYで追加したレイヤー自体にはbig-file.txtが残ったままである。
このイメージをdiveのCIモード(--ci)で確認すると、無駄な容量を検出できる。

$ dive dive-sample:latest --ci
  Using default CI config
Image Source: docker://dive-sample:latest
Extracting image from docker-engine... (this can take a while for large images)
Analyzing image...
  efficiency: 73.2271 %
  wastedBytes: 5502580 bytes (5.5 MB)
  userWastedPercent: 50.8104 %
Inefficient Files:
Count  Wasted Space  File Path
    2        5.0 MB  /big-file.txt
    2        436 kB  /etc/ssl/certs/ca-certificates.crt
    2         44 kB  /lib/apk/db/installed
    2         22 kB  /lib/apk/db/scripts.tar
    2        288 B   /lib/apk/db/triggers
    2        123 B   /etc/apk/world
Results:
  FAIL: highestUserWastedPercent: too many bytes wasted, relative to the user bytes added (%-user-wasted-bytes=0.508103685810424 > threshold=0.1)
  SKIP: highestWastedBytes: rule disabled
  FAIL: lowestEfficiency: image efficiency is too low (efficiency=0.7322710060027512 < threshold=0.9)
Result:FAIL [Total:3] [Passed:0] [Failed:2] [Warn:0] [Skipped:1]

Inefficient Files/big-file.txtが2件表示されており、COPYで追加したレイヤーとRUN rmのレイヤーの両方に容量が残っていることが分かる。
COPYRUN rmを1つのレイヤーにまとめるか、そもそも不要なファイルをコピーしないようにDockerfileを修正すれば、無駄な容量を削減できる。

CIで自動チェックする(–ci)

--ciオプションを付けると、TUIを起動せずに分析結果を出力して終了する。
効率が閾値を下回った場合は終了コードが非0になるため、CIパイプラインに組み込んでイメージの肥大化を自動検知できる。

$ dive <イメージ名> --ci

閾値は--lowestEfficiency--highestUserWastedPercentで調整できる。

$ dive <イメージ名> --ci --lowestEfficiency=0.95 --highestUserWastedPercent=0.05

--ci-configオプションでYAMLファイルを指定すると、チェックルールをまとめて管理できる。

rules:
  lowestEfficiency: 0.95
  highestWastedBytes: 20MB
  highestUserWastedPercent: 0.05
$ dive <イメージ名> --ci --ci-config .dive-ci.yaml

分析結果をJSONで出力する(–json)

--jsonオプションを付けると、レイヤーごとのファイル一覧や無駄な容量の情報をJSONファイルに出力できる。

$ dive <イメージ名> --json result.json

出力されるJSONにはイメージ全体の情報と、レイヤーごとの情報が含まれる。

{
  "image": {
    "sizeBytes": 135587976,
    "inefficientBytes": 74804,
    "efficiencyScore": 0.9996386478989848,
    "fileReference": [
      {
        "count": 3,
        "sizeBytes": 47207,
        "file": "/lib/apk/db/installed"
      }
    ]
  },
  "layer": [
    {
      "index": 0,
      "sizeBytes": 8691209,
      "command": "ADD alpine-minirootfs-3.23.4-aarch64.tar.gz / # buildkit"
    }
  ]
}

fileReferenceには無駄になっているファイルの一覧、layerには各レイヤーのサイズと生成したコマンドが含まれており、他のツールと組み合わせて集計する用途にも使える。

参考

» dive