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

第13章 pnpm × キャッシュマウントの定番レシピ 🍳⚡️📦

この章は「pnpm install が毎回やり直しで遅い😵‍💫」を、BuildKit のキャッシュマウントで一気に軽くする回だよ!🚀 ポイントは pnpm の“ストア”をキャッシュすること。pnpm 公式もこの方針で例を出してるよ。(pnpm.io)


1) まず結論:キャッシュするのは「pnpmストア」🧠✨

pnpm は、ダウンロードしたパッケージを ストアに貯めて再利用する思想(≒同じものは何度も落とさない)だから、Docker ビルドではそのストアを RUN --mount=type=cache で“ビルド間で持ち越す”のが超効く!💥 BuildKit のキャッシュマウントは「ビルドのたびに積み上がるレイヤキャッシュ」と違って、レイヤが壊れても“ダウンロード済み資産”を再利用できるのが強みだよ。(Docker Documentation)


2) 定番レシピ:最小で効く Dockerfile(まずはこれ)🧩

ここでは pnpm 公式例と同じく、ストアを /pnpm/store に置いてキャッシュします。(pnpm.io)

## syntax=docker/dockerfile:1

FROM node:24-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable

WORKDIR /app

## 依存関係だけ先にコピー(ここがキャッシュの命🔥)
COPY package.json pnpm-lock.yaml ./

## pnpmストアをキャッシュマウントして install を高速化⚡️
RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store \
pnpm install --frozen-lockfile

## ここから先でソースをコピー(依存レイヤを壊しにくくする)
COPY . .

## 例:TypeScriptをビルドするなら
RUN pnpm run build

3) ここが大事!レシピの“効きポイント”解説 🔍✨

✅ (A) COPY package.json pnpm-lock.yaml を先にする理由 📌

ここを先にしておくと、普段の編集(src/ いじるだけ)では依存レイヤが壊れにくくなる! つまり「毎回 install 😭」が「install スキップ😎」になりやすい。

✅ (B) --mount=type=cache の意味(ざっくり)🧠

RUN の間だけ、指定ディレクトリに“永続っぽいキャッシュ”を付けてくれる。 BuildKit はこのキャッシュをビルド間で再利用できるので、再ビルドでも 変更分だけDL になりやすい。(Docker Documentation)

✅ (C) --frozen-lockfile を付ける理由 🔒

ロックファイルを勝手に書き換えない=ビルドが再現しやすい。 (「動いたり動かなかったり」事故が減る👍)


4) “効いてるか”の確認方法(超だいじ)👀⏱️

① 2回ビルドして差を見る 🏎️💨

1回目は当然遅い(初回DLだから) 2回目が 目に見えて速くなるのが理想!

docker build --progress=plain -t myapp:cachetest .
docker build --progress=plain -t myapp:cachetest .

② ログで「また落としてない」感を探す 🔎

2回目、pnpm のダウンロード量が減ってたり、インストール時間が短くなってたら勝ち!🎉 (全部がゼロにはならないこともあるけど、体感で軽くなればOK)


5) ありがちな落とし穴 🕳️😵‍💫(ここで詰まりやすい)

❌ 落とし穴1:COPY . . を先にやっちゃう

ソース更新のたびに依存レイヤまで巻き添えで壊れがち。 👉 依存ファイルだけ先に COPY が鉄則!

❌ 落とし穴2:キャッシュの“場所”がズレてる

キャッシュマウントしてるのに、pnpm がそこを使ってないと意味ゼロ😂 この章のレシピは、pnpm 公式例と同じ target=/pnpm/store で揃えてるからまず安心。(pnpm.io)

❌ 落とし穴3:並列ビルドでキャッシュが競合する(たまに)⚔️

複数ビルドが同じキャッシュを同時に触ると、ツールによっては壊れることがある。 BuildKit には sharing=locked で「順番待ち」にする考え方があるよ(apt の例)。(Docker Documentation) pnpm で問題が出る場合は、次みたいに“ロック付き”を試す価値あり👇

RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store,sharing=locked \
pnpm install --frozen-lockfile

⚠️ 注意:CI だと効かないことがある

pnpm 公式も「CI は環境が使い捨てで、キャッシュマウントが期待通り残らないことがある」って注意してる。(pnpm.io) その場合の“別解”が次章の pnpm fetch につながるよ(ここでは「そういう世界もある」だけ覚えればOK😉)


6) 🧪 ミニ演習:キャッシュ有/無で比較して“勝ち筋”を掴む 🏁📊

演習1:わざとキャッシュ無しにして時間を測る😈

--mount=type=cache を一旦消してビルド → 時間をメモ 戻してビルド → 時間をメモ 差が出たら成功!🎉

演習2:依存が変わらない編集をして再ビルド✍️

src/ の1行だけ変えて再ビルド。 install が走らない/軽いなら設計勝ち!🥇


7) 🤖 AI活用プロンプト(コピペOK)🧰✨

① キャッシュが壊れる原因を見つける

「この Dockerfile のどの行がキャッシュを壊しやすい?理由もセットで3つ教えて。改善案も出して」

② pnpmストアの最適 target を点検

「pnpm のキャッシュ/ストアの保存先がこの Dockerfile で正しいかチェックして。ズレてたら修正案を出して」

③ “変更頻度”で COPY を再設計

「変更頻度が低い順に COPY と RUN を並び替えて、ビルドが速くなる Dockerfile を提案して(理由つき)」


この第13章が終わると、“pnpm install が重い”の最短ルートが手に入るよ 😆⚡️ 次の第14章(pnpm fetch)は、CIでも速くしたい人向けの必殺技に繋がる!🔪✨