docker imagesを極める:イメージ管理の実践ガイド

docker images は単なる「イメージ一覧表示」のためのコマンドだと軽視されがちですが、運用現場ではストレージ効率、キャッシュ管理、セキュリティ、CI/CDの安定性に直結する重要なツールです。本記事ではコマンドの深い理解から、実運用で役立つ自動化スクリプト、CI連携、セキュリティ運用まで、実践的に解説します。実際の運用でそのまま使えるコマンド例やスニペットを多めに載せています。


イントロダクション:なぜ docker images を深く知るべきか

運用中のホストやビルドサーバは、知らぬ間に大量のイメージや中間レイヤーを溜め込んでいきます。これらはディスクを圧迫するだけでなく、古い脆弱なイメージが残ることでセキュリティリスクにもなります。docker images を単に眺めるだけでなく、メタデータを解析し、ポリシーに沿って自動で整理することが中〜上級者の仕事です。


基本のおさらい(重要なフィールドの意味)

まずは docker images が返す主なフィールドの意味を復習しておきます。

  • REPOSITORY:イメージのリポジトリ名
  • TAG:リポジトリ内でのタグ。latest
  • IMAGE ID:ローカルで割り当てられる短縮ID(同じレイヤを共有する他のタグと同一IDになる)
  • DIGEST:コンテンツアドレス(@sha256:...)で、リモートの同一性・検証に使う
  • CREATED:作成日時(人間向け)
  • SIZE:イメージが占める見積もりサイズ(実際は共有レイヤを含むので注意)

現場では IMAGE IDDIGEST の違い(ローカルIDは再ビルドやタグ付けで変わるが、Digestはコンテンツで決まる)を正しく扱うことが重要です。デプロイ固定は image@sha256:... 形式を使い、タグの揺らぎを避けましょう。


実用コマンド:見やすく・解析しやすく出力する

--format を使うとマシンで扱いやすい出力が得られます。jq と組み合わせて自動化スクリプトに組み込むのが定石です。

# JSONライクに出力して jq で加工
docker images --format '{{json .}}' | jq -r '. | "\(.Repository):\(.Tag) \(.ID) \(.CreatedSince) \(.Size)"'

# DIGEST を含めた出力(Docker 18.03+で --digests)
docker images --digests --format '{{.Repository}}:{{.Tag}} {{.Digest}} {{.ID}} {{.CreatedSince}} {{.Size}}'

CreatedSince(「3 weeks ago」等)の自動判定は便利ですが、機械判定には向かない場合があるため、厳密な日時比較が必要なら docker image inspectCreated のISOタイムを取り出して比較しましょう。

docker image inspect --format '{{.Created}} {{.RepoTags}}' nginx:latest

レイヤーと履歴の読み方(サイズ最適化の第一歩)

イメージの中身を理解するには docker historydocker image inspect を併用します。history はレイヤーの作られ方(どの命令で追加されたか)を示し、不要に肥大化したレイヤを特定できます。

# 履歴(逆順: 最上位レイヤが上)
docker history --no-trunc myapp:1.0

# inspect で RootFS のレイヤ一覧やサイズを確認
docker image inspect myapp:1.0 | jq '.[0].RootFS'

ポイント

  • 大きな差分は ADD / COPY 命令に起因することが多い。 artifacts をまとめてコピーしている場所を分割・最適化する。
  • マルチステージビルドを使ってビルドアーティファクトを最終イメージに残さない。

中間イメージと <none> タグの扱い

ビルド時にマルチステージやキャッシュの結果として <none>:<none>(dangling images)が残ることがあります。これらはディスクを消費するため定期クリーンアップが必要です。

# ダングリングイメージだけ削除
docker image prune

# すべての未使用イメージ(注意:稼働中のコンテナで使われているイメージは削除されない)
docker image prune -a

ただし CI で prune -a を原則実行するのは危険です。重要なイメージを誤って削除しないためにフィルタを用い、ルールに基づく削除を行います。


実践:安全なイメージ削除スクリプト例

タグや作成日時(ISO形式)で選別して削除するスクリプトの例。運用環境ではログ出力・ドライランが必須です。

#!/usr/bin/env bash
# safe-rmi.sh
set -eo pipefail

DRY_RUN=true  # 本番では false にする
KEEP_LAST_N=3

# 対象リポジトリ
REPO="myorg/myapp"

# 1) 対象イメージ一覧を取得(created timestampを比較するためinspectを使う)
images=$(docker images --format '{{.Repository}} {{.Tag}} {{.ID}}' | awk -v repo="$REPO" '$1==repo {print $0}')

# 2) タグ毎に最新N個を残して古いものを削除
echo "$images" | awk '{print $2 " " $3}' | while read tag id; do
  echo "Processing $REPO:$tag ($id)"
done

# もっと実用的には、リストを作って created に基づくソートをする実装を行う
# (ここは実際の稼働用に拡張してください)

上のスクリプトは骨格です。現実運用では次の点を追加します:

  • ドライラン(--dry-run)フラグ
  • 削除前にスナップショット(ログ/監査)を保存
  • レジストリの最新リモートDigestと照合して安全に削除する仕組み

CI/CD でのイメージキャッシュと docker images

CI環境(GitHub Actions / GitLab CI / Jenkins等)では、ビルドキャッシュを再利用することで速度化できます。しかしそのためにはローカルのイメージ管理が重要です。

  • 推奨策:ビルドキャッシュをリモートに保存(BuildKit の --cache-to / --cache-from)、ローカルは最小限にする。
  • イメージの固定:デプロイ時は image@sha256:... を使うことで「タグが書き換わる」リスクを排除する。
  • CI側での定期清掃:古いキャッシュ・イメージは週次などで安全にクリーンアップ(builder prune --filter until=168h など)。

例:GitHub Actions のステップ(概念)

- name: Build and push with cache
  run: |
    docker buildx build \
      --cache-from=type=registry,ref=example.com/cache:latest \
      --cache-to=type=registry,ref=example.com/cache:latest,mode=max \
      --push \
      -t example.com/myapp:${{ github.sha }} .

セキュリティ運用:Digest とスキャン

docker images --digests で得られる Digest は、イメージのコンテンツを一意に表すため、署名検証や脆弱性スキャン運用に必須です。CIでビルドしたイメージをレジストリにPushする際には Digest を取得し、デプロイ系の環境変数やマニフェストに記録しておくと良いです。

また、TrivyやClairなどのイメージスキャナと組み合わせ、docker images のリストを元に未スキャンのイメージを検出して自動スキャンを走らせるルールを作ると堅牢です。


高度な出力処理:監査ログ・メトリクス化

運用で便利なのは docker images を構造化してログに流すこと。--format '{{json .}}'jq を使えば簡単に JSON ラインが作れます。これをログ収集基盤(ELK / Loki 等)に送り、イメージ増減の履歴・ディスク消費の推移を監視できます。

例(監査用出力):

docker images --format '{{json .}}' | jq -c '.'
# 各行ごとに JSON を監査ログへ送信

Prometheus や Grafana で可視化するには、docker system dfbuildx du の出力を exporter 経由で取り込み、イメージ毎のサイズや古さをメトリクス化すると便利です(カスタム exporter が必要)。


よくあるトラブルと対処法

  • ディスクが一杯でdockerコマンドが失敗する
    docker system df で使用量を確認し、まずは不要なコンテナ・ボリューム・ネットワークを削除。緊急時は docker image prune -a --force を実行するが、重要イメージの有無は事前確認を。
  • 同じイメージなのに複数IDがある
    → 名前やタグを付け直した場合は同一レイヤでもIDが変わり得る。Digestで比較する方が確実。
  • CIでキャッシュを使っても遅い
    → キャッシュのキー付けやキャッシュ先(registry / local)を見直す。キャッシュがヒットしない原因はDockerfileの命令順やキャッシュ破棄につながるファイル更新にあることが多い。

実践チェックリスト(導入すべき運用ポリシー)

  1. Digestベースのデプロイ:デプロイ記録に image@sha256:... を保存する。
  2. 定期スキャン:レジストリにある新しいイメージは自動で脆弱性スキャン。スキャン結果がNGならタグ付け・プロモーションを止める。
  3. 安全なクリーンアップdocker image prune はドライラン→承認→実行のフローをCIで作る。
  4. 監査ログdocker images --format '{{json .}}' をログ収集に送る。古いイメージの増減を追えるようにする。
  5. マルチステージ採用:不要ファイルが含まれないようビルド設計を統一する。

次のステップ(改善案・拡張)

  • buildx--cache-to / --cache-from を使ったリモートキャッシュの導入でCI時間の大幅短縮。
  • Docker Registry API(/v2/_catalog 等)を使い、レジストリ側のイメージメトリクスとローカル状況を突き合わせる連携。
  • Grafana での可視化ダッシュボード作成(イメージ数・古いイメージ数・総サイズ・増加トレンド)。
  • イメージ署名(Notary/Cosign 等)の導入で、デプロイの信頼性を高める。

まとめ

docker images は「一覧を見るだけ」のコマンドではなく、イメージライフサイクルの制御点です。運用現場ではこの出力を起点に、ビルド設計(小さなレイヤ)/CIキャッシュ設計/定期クリーンアップ/脆弱性スキャン/監査 を組み合わせていく必要があります。本稿のスニペットやチェックリストをベースに、まずは「可視化」と「ドライラン付きの自動化」を導入してみてください。


投稿日

カテゴリー:

投稿者:

タグ:

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です