メインコンテンツまでスキップ

第02章:Dockerビルドの仕組み:レイヤとキャッシュ 🧱✨

この章のゴールはシンプル! 「なぜキャッシュが効く/効かないのか」を説明できて、Dockerfileの順序を自分で直せるようになることです 💪😆


1) まず結論:Dockerfileは“積み木”🧱(=レイヤ)でできてるよ

DockerはDockerfileを上から順に実行して、各命令ごとに“レイヤ(層)”を積み上げます 🧱🧱🧱 そして次のビルドでは、同じレイヤをキャッシュから再利用できると速い!⚡️

FROM node:xx     ← レイヤ1(ベース)
WORKDIR /app ← レイヤ2
COPY ... ← レイヤ3(ここが壊れやすい!)
RUN npm ci ← レイヤ4(重い!)
COPY ... ← レイヤ5(ここも壊れやすい!)
RUN npm run build← レイヤ6(重い!)

「速くしたい」=重いレイヤ(npm ciとか)を、なるべく作り直さないようにすることだよ 🏎️💨


2) キャッシュの基本ルール(ここが最重要)🎯

Dockerは各命令について、ざっくりこう判断します:

  • 命令が“前回と同じ”ならキャッシュ命中(速い)
  • 命令が一致しなければ、その時点でキャッシュ崩壊(遅い) 💥 そしてそこから下の命令は全部作り直しになる 😱

この「一回崩れたら、その後ぜんぶ巻き添え」が超重要です。(Docker Documentation)


3) なんで COPY が“地雷”になりがち?💣📦

COPY / ADD は、コピー対象ファイルの情報(メタデータ)からチェックサムを作ってキャッシュ可否を決めます。 なので、対象ファイル群に変更があるとキャッシュが壊れやすいんだよね 💥(Docker Documentation)

さらにポイント:

  • COPY . . は「全部コピー」=ちょっとした変更でも壊れやすい 😵‍💫
  • しかもキャッシュが壊れたら、その後ろ(npm ci / buildなど)も巻き添えで作り直し 😭(Docker Documentation)

4) “正しい順序”の考え方:変わりにくいもの→変わりやすいもの 🥇

基本戦略はこれだけです👇

  • あまり変わらないもの(依存定義)を先に
  • よく変わるもの(アプリのソース)を後に

公式ドキュメントでも「依存ファイルを先にCOPY→依存インストール→最後に全部COPY」が推奨されています。(Docker Documentation)


5) 🧪ミニ演習:順序を変えてキャッシュ命中/ミスを体感しよう 👀⚡️

ここは“体感”がいちばん早い!😆 同じプロジェクトで Dockerfile を2つ作って比べます。

5-1) サンプル前提(Node + TypeScript想定)📁

  • package.json
  • package-lock.json
  • src/(TSソース)
  • tsconfig.json

5-2) 悪い例:COPY . . が早すぎる 😭

## Dockerfile.bad
FROM node:22-alpine
WORKDIR /app

COPY . . # ← ここで毎回だいたい壊れる!
RUN npm ci # ← 重いのに毎回走りがち😱
RUN npm run build

CMD ["node", "dist/index.js"]

5-3) 良い例:依存だけ先にCOPYして、npm ci を守る 🛡️✨

## Dockerfile.good
FROM node:22-alpine
WORKDIR /app

COPY package.json package-lock.json ./ # ← 依存定義だけ先に
RUN npm ci # ← 依存が変わらない限り再利用されやすい✅

COPY . . # ← ソースは後に
RUN npm run build

CMD ["node", "dist/index.js"]

この形にすると、ソースだけ変えた時に “npm ci が走らない” 体験が起きやすいよ!(Docker Documentation)


5-4) 実行して比べる(PowerShell例)🪟⚙️

  1. 初回ビルド(当然遅い)
Measure-Command { docker build --progress=plain -f Dockerfile.bad  -t cache-demo:bad  . }
Measure-Command { docker build --progress=plain -f Dockerfile.good -t cache-demo:good . }
  1. 何も変えずにもう1回(キャッシュで速くなるはず)
Measure-Command { docker build --progress=plain -f Dockerfile.bad  -t cache-demo:bad  . }
Measure-Command { docker build --progress=plain -f Dockerfile.good -t cache-demo:good . }
  1. src/ のTSファイルを1行だけ変えて再ビルド ✍️
Measure-Command { docker build --progress=plain -f Dockerfile.bad  -t cache-demo:bad  . }
Measure-Command { docker build --progress=plain -f Dockerfile.good -t cache-demo:good . }

観察ポイント 👀

  • --progress=plain のログで、CACHED が出る工程は再利用されてる合図 ✅ 依存レイヤが生きてると気持ちいい!😆(Docker Documentation)
  • Dockerfile.badソース変更だけでも npm ci まで巻き添えになりがち
  • Dockerfile.goodソース変更なら npm ci がキャッシュでスキップされやすい 🎉(Docker Documentation)

6) “キャッシュが効かない”あるある 😵‍💫➡️😇

あるある1:COPY . . が早すぎる

  • その後ろが全部作り直しになりやすい
  • 解決:依存ファイルを先にCOPYへ寄せる (Docker Documentation)

あるある2:ビルドに不要なファイルがコンテキストに混ざってる 🎒💦

  • コンテキスト(ビルドに送られるファイル集合)が大きいと遅くなりやすい
  • .dockerignore で削るのが基本(これは次章以降で本格的にやるよ)(Docker Documentation)

あるある3:一度キャッシュが壊れたら“下全部”巻き添え

  • これがDockerビルドの基本仕様!
  • だから「重い工程は前の方に」「変わりやすいものは後ろに」(Docker Documentation)

7) 🤖AI活用(Copilot / Codex向け)最強プロンプト3つ 🧠✨

プロンプトA:キャッシュ破壊ポイントの特定 🔍

次のDockerfileで「キャッシュが壊れやすい命令」と「その理由」を箇条書きで教えて。
特に COPY / ADD / RUN の依存関係(どのファイル変更で崩れるか)を説明して。

プロンプトB:最小差分で“速い順序”に直す 🛠️

このDockerfileを、依存インストールのキャッシュが最大限効く順序に並べ替えて。
変更は最小にして、diff形式で出して。npm ci を前提にしてOK。

プロンプトC:自分のプロジェクト用に最適化案を3つ出す 🎁

このリポジトリ構成(package.json/lock, src, tsconfig, etc)を前提に、
Dockerビルドを速くする改善案を「効果が大きい順に3つ」提案して。
それぞれ、なぜ速くなるかをレイヤ/キャッシュの観点で説明して。

8) まとめ ✅🎉

  • Dockerfileは命令ごとのレイヤでできてる 🧱(Docker Documentation)
  • キャッシュは「一致しない命令」が出た瞬間に壊れて、以降ぜんぶ作り直し 💥(Docker Documentation)
  • COPY . . は壊れやすいので、依存ファイル→依存インストール→ソースの順が基本 🥇(Docker Documentation)
  • 体感演習で「順序だけでこんなに変わる!」を掴めたら勝ち 🏁😆

次の章(第3章)では、ここで触れた「現代Dockerの標準ビルダー(BuildKit)」が、なぜ速いのかを“もっと気持ちよく”理解していくよ 🚀😎