第27章:“キャッシュが効かない”典型パターン集 😭
この章は「なんで毎回 npm ci(または pnpm install)走るの…😇」「ちょっと直しただけなのにビルドが重い…🐢」を、原因→見分け方→直し方でサクッと潰す回です💪🔥
1) まず“キャッシュ判定”の超ざっくりルール 🧱👀
Dockerのビルドは、基本的に Dockerfileの命令1行=1レイヤ で進みます。 そしてキャッシュが使われるのは、ざっくり言うと👇
- 命令文が同じ
- その命令が参照するファイル(特に
COPY/ADD)が同じ
この条件が揃ったときだけ、レイヤを再利用します⚡️ しかも、どこか1個でもキャッシュが壊れたら、その後ろは全部作り直しになります😱 (Docker Documentation)
さらに重要ポイント👇
COPY/ADDは、関係するファイルのメタ情報からチェックサムを作って判定します(ただし mtimeだけ変わっても無視されます) (Docker Documentation)RUNは基本的に「コマンド文字列が同じか」で見ます(コンテナ内の更新されたファイルまで毎回チェックはしません) (Docker Documentation)
2) まずは“5分診断”しよう 🕵️♂️⏱️
キャッシュが効かないときの最短手順はこれ👇
- **ログを“見える化”**する
- どのステップから作り直してるか当てる
- **当てたステップの入力(COPY/ARG/依存ファイル)**を疑う
おすすめはこれ(ビルドログが読みやすい)👇
docker buildx build --progress=plain -t myapp:dev .
ポイント💡 「
[n/m]のどこからCACHEDが消えたか」を見るだけで、原因の当たりが付くよ🎯
3) 典型パターン集 😭(症状→原因→直し方)
ここからが本番!「あるある」をまとめて潰していきます🔥
パターンA:COPY . . が早すぎる(王様)👑😭
症状
- 1ファイル直しただけで、毎回依存インストールが走る(地獄)🔥
原因
- 先に
COPY . .しちゃうと、ソースのちょい変更で 依存インストールのレイヤまで巻き添えになる💥 (Docker Documentation)
直し方(鉄板)
- 依存系ファイルを先にコピー → 依存インストール → 最後にソースをコピー
## 悪い例:変更が全部巻き込む
COPY . .
RUN npm ci
## 良い例:依存のキャッシュが残る
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
この考え方自体がDocker公式の最適化ド定番です💪 (Docker Documentation)
パターンB:ビルドコンテキストがデカすぎる 🎒💣
症状
- 送るファイルが多くて、ビルド開始が遅い
- “関係ないファイルの更新”でキャッシュが壊れる
原因
- Dockerはビルド時に「コンテキスト(送るファイル一式)」をビルダーへ渡します。 デカい=遅い&壊れやすい💥 (Docker Documentation)
直し方
.dockerignoreをちゃんと書く(最優先)✅ (Docker Documentation)
## .dockerignore の例(まずはこれ)
node_modules
dist
coverage
.git
.vscode
*.log
.env
パターンC:生成物(dist/.next/coverage)を混ぜてる 🧪🌀
症状
- ローカルでビルドした後に
docker buildすると、急にキャッシュが効かない - しかもイメージが太る🐷
原因
- 生成物って「ちょいちょい内容が変わる」ので、
COPYの判定がズレて壊れやすい💥 - そもそも不要ならコンテキストに入れないのが正義🧹 (Docker Documentation)
直し方
.dockerignoreで生成物を除外- 生成物はコンテナ内のビルドで作って、必要なものだけ最終ステージへ(マルチステージ)🏗️
パターンD:ロックファイルが揺れてる(人によって違う)🎲
症状
- Aさんはキャッシュ効くのに、Bさんの環境では毎回壊れる
- CIでも毎回依存の解決が微妙に違う
原因
package-lock.jsonなどが微妙に変わると、依存レイヤは当然作り直し- さらに
npm installは lock を更新し得る(揺れやすい)⚠️
直し方(Docker/CI向き)
npm ciを使う(lockとpackage.jsonが一致してないとエラー、node_modulesは消してクリーンに入れる)🧼 (docs.npmjs.com)
COPY package.json package-lock.json ./
RUN npm ci
パターンE:.npmrc や .env を COPY してる(秘密も危険)🫣🔑
症状
- 認証が絡むと毎回壊れる
- うっかり秘密がイメージに入る(最悪)😱
原因
- それらのファイルが変わると
COPY判定が変わってキャッシュが壊れる - さらにセキュリティ面でも事故りやすい
直し方
- まず
.dockerignoreで除外 - 認証が必要なら ビルドシークレットを使う(秘密の“中身”はキャッシュに含まれない)🕶️ (Docker Documentation)
なお「秘密を変えたら、その後の工程を作り直したい」場合は、
ARGを使った“意図的キャッシュバスター”が必要になることがあります(ARGはキャッシュに影響します) (Docker Documentation)
パターンF:ARG / SOURCE_DATE_EPOCH を毎回変えてる 🕰️💥
症状
- コミットのたびに全部作り直しっぽい
- “同じ内容なのに”キャッシュが残らない
原因
ARGの値が変わると、そこから後ろが壊れるSOURCE_DATE_EPOCHを動的値(コミット時間など)にすると、毎回壊れるのは仕様です (Docker Documentation)
直し方
- 「毎回変える値」を、キャッシュ温存したい場所より後ろに寄せる
- 再現性重視なら
SOURCE_DATE_EPOCHを固定値にする選択肢もある (Docker Documentation)
パターンG:Windowsの置き場所で“体感が終わる” 🪟🐢
症状
- ビルドやホットリロードが遅い
- ファイル監視が不安定(自動リロード効かない)
原因
- bind mount(共有)のファイル性能が置き場所で変わります
- 特に
/mnt/c(Windows側)をマウントすると遅くなりがち&inotifyが効きにくいことがある、というのがDocker公式の推奨です (Docker Documentation)
直し方(王道)
- プロジェクトをLinux側ファイルシステム(例:WSL2の
~配下)に置いて使う → 速い&変更検知が素直になりやすい (Docker Documentation)
4) “キャッシュが効かない”を潰すチェックリスト ✅🧰
困ったら上から順に潰していけばOK!✨
- Dockerfileの前半に
COPY . .がいないか(いたら分割) (Docker Documentation) -
.dockerignoreがあるか/生成物・.git・秘密が除外されてるか (Docker Documentation) - 依存は
npm ci(または lock 固定)になってるか (docs.npmjs.com) -
ARGで毎回変わる値を前半に置いてないか (Docker Documentation) - Windowsなら、プロジェクト置き場がボトルネックになってないか (Docker Documentation)
5) 🧪ミニ演習:自分のDockerfileから“1個だけ”原因を潰す 🔨✨
Step 1:現状を2回ビルドしてログを見る 👀
docker buildx build --progress=plain -t myapp:dev .
docker buildx build --progress=plain -t myapp:dev .
2回目で「CACHED にならない場所」が、あなたの犯人候補です😈
Step 2:TypeScriptの適当なファイルを1行だけ変える ✍️
例:src/index.ts に空白を入れる、コメントを足す、などでOK🙆♂️
Step 3:もう一回ビルドして“どこから壊れたか”確認
docker buildx build --progress=plain -t myapp:dev .
- 依存インストールが走った → パターンAの可能性大👑
Sending build contextが重い → パターンB/Cの可能性大🎒- lockが変わってる → パターンD🎲
Step 4:直す(例:パターンAを直す)
COPY package*.json→ install →COPY . .に並べ替え.dockerignoreを足す
Step 5:もう一回ビルドして改善を体感 🎉
「依存がCACHEDになって、最後の方だけ作り直し」になれば勝ち!🏆✨
6) 🤖AI活用(Copilot / Codex向け)プロンプト集 🧠✨
そのままコピペで使えるやつ置いとくね📌(※トークンや秘密は貼らないでね🙏)
プロンプト①:犯人当て(最速)
このDockerfileと .dockerignore を読んで、
「キャッシュが効かない典型パターン」を上から3つ指摘して。
それぞれ、どう直すかを “差分パッチ形式” で提案して。
(npm/TypeScriptアプリ、BuildKit前提)
プロンプト②:依存レイヤの最適化だけ欲しい
依存インストールが毎回走る。
package.json / lockfile / ソースのCOPY順をどう変えるべき?
最小の修正で、依存レイヤがキャッシュされるDockerfileにして。
プロンプト③:Windowsの“遅い”を疑う
Windows + Docker Desktop環境で、bind mountが遅い。
プロジェクトの置き場所・node_modulesの扱い・ファイル監視の観点で
改善案を優先度順に出して。
まとめ 🏁😆
- キャッシュは「どこで壊れたか」が分かれば勝ち🎯
- 一番多いのは
COPY . .が早すぎる と.dockerignore不備 👑🎒 (Docker Documentation) - 依存は
npm ci(または lock 固定)で“揺れ”を消す🧼 (docs.npmjs.com) - Windowsは置き場所で体感が変わるので、そこも疑う🪟🐢 (Docker Documentation)
次の章(第28章)は「速いのに壊れない固定テク」だから、ここで潰した改善が“長持ち”するように仕上げていくよ〜!🔒✨