ビルドとタグとレイヤ — Docker Tips
docker build -t でイメージに名前を付けます。再ビルドが速いか遅いかは、レイヤキャッシュがどこまで効くかで決まります。case01 を材料にします。
参考: docker buildx build · Docker build cache · Build cache invalidation · Building best practices
目次
- docker build の基本
- タグの付け方と命名規則
- ビルドキャッシュの仕組み
- キャッシュが無効化される条件
- --no-cache で強制的に作り直す
- docker images で確認する
- case01 / case02 でビルドを比較する
- 詰まりやすい点
docker build の基本
以降のコマンドは、リポジトリの Docker フォルダ(case01 / case02 の親)をカレントにして実行します。
docker build -t case01-ubuntu .\case01docker 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この連鎖の性質から、変更頻度が低い命令を上に、変更頻度が高い命令を下に 書くのがベストプラクティスです。たとえば依存パッケージのインストール(case01 の apt-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 imagesREPOSITORY 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:v2IMAGE 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.yml の image: に固定 |
| キャッシュの扱い | 通常の docker build と同じ |
同じく通常の docker build と同じ仕組み |
| 再ビルドの手間 | docker build を都度実行 |
docker compose up -d --build で起動と同時に反映 |
Compose を使う最大の利点は、ビルドとタグ付けと起動をまとめて 1 コマンドで再現できる ことです。CLI 単体では build → run を毎回覚えて打つ必要がありますが、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 で全行のログを出し、どの命令でキャッシュが切れているか・どこでエラーが起きているかを確認するのが近道です。