Docker

ビルドとタグとレイヤ

8

ビルドとタグとレイヤ — Docker Tips

docker build -t でイメージに名前を付けます。再ビルドが速いか遅いかは、レイヤキャッシュがどこまで効くかで決まります。case01 を材料にします。

参考: docker buildx build · Docker build cache · Build cache invalidation · Building best practices


目次

  1. docker build の基本
  2. タグの付け方と命名規則
  3. ビルドキャッシュの仕組み
  4. キャッシュが無効化される条件
  5. --no-cache で強制的に作り直す
  6. docker images で確認する
  7. case01 / case02 でビルドを比較する
  8. 詰まりやすい点

docker build の基本

以降のコマンドは、リポジトリの Docker フォルダ(case01 / case02 の親)をカレントにして実行します。

docker build -t case01-ubuntu .\case01

docker build の引数は「ビルドコンテキスト」と呼ばれるディレクトリで、そのディレクトリ内の Dockerfile を読み込んでビルドします。-t <名前> でイメージにタグを付けます。

要素 意味
ビルドコンテキスト(.\case01 Dockerfile と、COPY / ADD の対象になり得るファイル群の探索範囲
-t case01-ubuntu 作成するイメージの名前(タグ省略時は latest
-f <パス> Dockerfile のファイル名・場所を明示的に指定(省略時は <コンテキスト>/Dockerfile

bash(WSL / Git Bash)でも同じ書き方です。

docker build -t case01-ubuntu ./case01

現在の Docker はビルドの実体として BuildKit(docker buildx build)を使っていますが、コマンドとしては従来どおり docker build で呼び出せます。出力形式が見慣れない場合は docker build --progress=plain -t case01-ubuntu . のように詳細ログを出すと、各命令の実行状況が読みやすくなります。

ビルドが終わったら、コンテナを起動して確認します。

docker run -d --name case01 --restart unless-stopped case01-ubuntudocker ps --filter name=case01

タグの付け方と命名規則

イメージ名は [レジストリ/][ユーザーやOrg/]リポジトリ名[:タグ] の形式です。学習用のローカルビルドでは、レジストリ部分を省略してシンプルに名付けるのが一般的です。

用途
case01-ubuntu タグ省略(latest になる)
case01-ubuntu:v1 独自のバージョン管理
case01-ubuntu:2024-07-14 日付タグで作成日を分かるようにする
myrepo/case01-ubuntu:v1 将来レジストリへ push する想定の命名

タグを固定する理由: latest タグは「最後にその名前でビルドした結果」を指すだけで、特定バージョンを意味しません。複数回ビルドし直して違いを比較したいときは、v1 / v2 のように区別できるタグを付けておくと、後から docker images で見比べやすくなります。

docker build -t case01-ubuntu:v1 .\case01# Dockerfile を編集した後docker build -t case01-ubuntu:v2 .\case01docker images | Select-String case01-ubuntu

同じイメージに複数のタグを付けたい場合は docker tag を使います。

docker tag case01-ubuntu:v2 case01-ubuntu:latest

これは新しいレイヤーを作るわけではなく、既存のイメージ ID に別名を追加するだけの軽い操作です。


ビルドキャッシュの仕組み

docker build は、Dockerfile の各命令を 1 つのレイヤー として扱い、以前のビルドで同じ内容の命令があれば、そのレイヤーを再利用します。これにより、変更していない部分の再ビルドをスキップして高速化します。

FROM ubuntu:24.04         ← キャッシュ済みなら再利用RUN apt-get update && ...  ← キャッシュ済みなら再利用CMD ["sleep", "infinity"] ← メタデータのみ、実質コストなし

キャッシュが効いているビルドでは、各行の横に CACHED という表示が出ます。

=> CACHED [2/2] RUN apt-get update && apt-get install -y ...

キャッシュの判定は「命令の内容が完全に同じか」「(COPY/ADD の場合は)対象ファイルの内容やメタデータが変わっていないか」で行われます。コメントの追加や空白の変更のように見た目だけの差でも、命令文字列が変われば別扱いになる点には注意が必要です。


キャッシュが無効化される条件

キャッシュは「ある命令のキャッシュが無効になったら、それより 後ろの命令はすべて 無効になる」という連鎖的な仕組みです。

変更内容 影響
FROM のベースイメージが更新された それ以降の全レイヤーが再実行
RUN の内容(コマンド文字列)を変更 その RUN 以降が再実行
COPY / ADD の対象ファイルの内容が変わった その命令以降が再実行
Dockerfile の命令の順序を入れ替えた 入れ替えた箇所以降が再実行される可能性が高い
RUN の中身は同じだが前の行を変更 前の行が無効化された時点で、後続の RUN も連鎖的に無効化
flowchart LR  A["FROM ubuntu:24.04"] --> B["RUN apt-get ..."]  B --> C["CMD sleep infinity"]  A2["FROM 変更"] -.無効化.-> B  B -.連鎖して無効化.-> C

この連鎖の性質から、変更頻度が低い命令を上に、変更頻度が高い命令を下に 書くのがベストプラクティスです。たとえば依存パッケージのインストール(case01apt-get install 部分)はアプリコードよりも変更頻度が低いため、アプリのソースを COPY するよりも前に置くと、コード修正時の再ビルドが速くなります。case01 は依存インストールと CMD のみの単純な構成のため、この並び替えの効果を体感するには、COPY でファイルを追加する構成に発展させると分かりやすくなります。


--no-cache で強制的に作り直す

キャッシュを使わず、すべての命令を最初から実行し直したい場合は --no-cache を付けます。

docker build --no-cache -t case01-ubuntu .\case01

主な使いどころ:

  • ベースイメージの latest に新しいセキュリティ更新が出ているか確認したい
  • キャッシュのせいで意図した変更が反映されていないと疑われるとき
  • 「クリーンな状態から本当にビルドが通るか」を検証したいとき

特定の命令(ビルドステージ)だけキャッシュを無効化したい場合は --no-cache-filter <ステージ名> が使えますが、case01 のような単一ステージの Dockerfile では --no-cache で十分です。

docker build --pull --no-cache -t case01-ubuntu .\case01

--pull を付けると、ローカルにベースイメージのキャッシュがあっても、レジストリ側の最新版を確認してから使います。ベースイメージ自体の更新を取り込みたいときに組み合わせます。


docker images で確認する

docker images
REPOSITORY       TAG      IMAGE ID       CREATED         SIZEcase01-ubuntu    v2       1a2b3c4d5e6f   5 seconds ago   145MBcase01-ubuntu    v1       9f8e7d6c5b4a   2 minutes ago   145MBubuntu           24.04    3f25ecba9da5   3 weeks ago     78.1MB

同じ IMAGE ID が複数のタグに紐づくこともあります。これは「内容は同じイメージだが、別名で参照できるようにしている」状態です。

イメージ 1 つあたりの詳細(レイヤー構成、作成コマンドなど)を見たい場合は docker history が使えます。

docker history case01-ubuntu:v2
IMAGE          CREATED         CREATED BY                                      SIZE1a2b3c4d5e6f   5 seconds ago   CMD ["sleep" "infinity"]                        0B<missing>      5 seconds ago   RUN apt-get update && apt-get install -y ...    67MB<missing>      3 weeks ago     /bin/sh -c #(nop) CMD ["bash"]                  0B

各行が Dockerfile の命令 1 つに対応しており、どの命令がどれだけのサイズを占めているかを確認できます。イメージが想定より大きいときの調査に役立ちます。


case01 / case02 でビルドを比較する

case01 は Dockerfile のみ、case02 は同じ Dockerfile を docker-compose.yml からビルドする構成です。ビルド方法の違いだけを比べます。

# case01: CLI で直接ビルドdocker build -t case01-ubuntu .\case01# case02: Compose がビルドを担うcd .\case02docker compose build
比較項目 case01(CLI) case02(Compose)
ビルドコマンド docker build -t <名前> <パス> docker compose build(または up --build
タグの決め方 -t で毎回明示 docker-compose.ymlimage: に固定
キャッシュの扱い 通常の docker build と同じ 同じく通常の docker build と同じ仕組み
再ビルドの手間 docker build を都度実行 docker compose up -d --build で起動と同時に反映

Compose を使う最大の利点は、ビルドとタグ付けと起動をまとめて 1 コマンドで再現できる ことです。CLI 単体では buildrun を毎回覚えて打つ必要がありますが、Compose では docker-compose.yml に記述済みの内容がそのまま実行されます。キャッシュの仕組み自体はどちらも同じ docker build の裏側を使っているため、この記事で説明したキャッシュ・タグの考え方はそのまま両方に当てはまります。


詰まりやすい点

症状 原因 対処
ビルドしても変更が反映されない キャッシュが有効なレイヤーが再利用されている 該当命令の内容を変える、または --no-cache を使う
docker build が「Dockerfile を見つけられない」 ビルドコンテキストのパスが違う、Dockerfile の場所が違う パスを確認し、必要なら -f で明示指定
タグを付け忘れて <none> イメージが増える -t を省略してビルドを繰り返した -t を必ず付ける。不要な <none>docker image prune で削除
ビルドはできるがイメージが大きい 依存関係のキャッシュファイルが残っている、レイヤーが分割されすぎ RUN をまとめ、rm -rf などの掃除コマンドを同一レイヤーに含める
キャッシュが効かず毎回全部ビルドされる Dockerfile の順序や内容を頻繁に変えている、--no-cache を付けている 変更頻度の低い命令を上に配置し、--no-cache は必要なときだけ使う
docker compose build が古いイメージのままに見える Compose 側もキャッシュを使っている docker compose build --no-cache を使う

ビルドで詰まったときは、まず docker build --progress=plain で全行のログを出し、どの命令でキャッシュが切れているか・どこでエラーが起きているかを確認するのが近道です。

シェア