第28章:“速いのに壊れない”ための固定テク(バージョン/再現性)🔒⚡️
「昨日ビルドできたのに、今日ビルドしたら壊れた😇」 これ、原因の多くは “どこかが勝手に新しくなった” です💥
この章では、速さ(キャッシュ)を活かしながら、再ビルドしても同じ結果になりやすい 固定テクをまとめて身につけます💪✨ 固定できると、キャッシュも効きやすくなって「待ち時間」も減ります⏱️🎉
1) 今日の主役:固定すべき“3点セット”🧊🧊🧊
再現性は、だいたいこの3つで決まります👇
- ベースイメージ固定(タグだけに頼らない)🧱
- 依存固定(ロックファイル+CI向けインストール)📦
- ツール固定(Node/パッケージマネージャの版)🔧
Docker のタグは“同じ名前でも中身が変わる”ことがあるので、digest固定が効きます🧷(ただし更新運用もセット)(Docker Documentation)
2) 図解:どこが揺れると壊れる?🫨🧩
Dockerfileの結果 =
[ベースイメージ(Node入り)] ← ここが揺れると全部揺れる😵
+ [OSパッケージ] ← apt等は日々変化しやすい⚠️
+ [パッケージマネージャ] ← pnpm/npm/yarnの版がズレると地獄😇
+ [依存関係(node_modules)] ← ロックが無い/揺れると事故💥
+ [あなたのソースコード] ← ここは毎日変わってOK👍
ポイントは、毎日変わるのはソースだけに寄せることです🎯 それができると「壊れない」+「キャッシュが効く」になります✨(Docker Documentation)
3) 手順①:ベースイメージを digest で固定する🧷🐳
✅ なぜ digest?
タグは“同じタグ名でも別バージョンに差し替え可能”です。 digest にすると 中身が同一であることが保証されます🔒(Docker Documentation)
🛠 digestの取り方(例)
(どれか1つでOK👌)
A. buildxで見る(おすすめ)
docker buildx imagetools inspect node:20-slim
B. pullしてRepoDigestsを見る
docker pull node:20-slim
docker image inspect node:20-slim --format "{{index .RepoDigests 0}}"
出てきた node:20-slim@sha256:... を Dockerfile の FROM に貼ります📌
✅ 固定例(digestは置き換えてね)
## syntax=docker/dockerfile:1
FROM node:20-slim@sha256:<ここにdigest> AS base
WORKDIR /app
📌 注意:digest固定は「勝手にアップデートされない」ので、更新はPRで管理するのが相性いいです🧹(後で触れるね)(Docker Documentation)
4) 手順②:依存は “CI用の固定インストール” にする📦🧼
✅ npm派:npm ci を使う
npm ci は “ロックファイル必須&ロックを書き換えない” ので、再現性の王道です👑
さらに node_modules があっても消してから入れるので、ズレが残りにくいです🧽(npm ドキュメント)
Dockerfileの定番(キャッシュも効かせる)⚡️
## syntax=docker/dockerfile:1
FROM node:20-slim@sha256:<digest> AS deps
WORKDIR /app
## 依存だけ先にコピー(ソース変更で依存が入れ直しにならないように)
COPY package.json package-lock.json ./
## npmキャッシュをBuildKitで保持(DL地獄を減らす)
RUN --mount=type=cache,target=/root/.npm \
npm ci
FROM node:20-slim@sha256:<digest> AS app
WORKDIR /app
COPY --from=deps /app/node_modules /app/node_modules
COPY . .
CMD ["npm","start"]
BuildKit のキャッシュマウントは「再ビルドしてもDLを減らす」ための道具です🧠✨(Docker Documentation)
npm ci 自体の仕様も “固定” の根拠になります🔒(npm ドキュメント)
✅ pnpm派:--frozen-lockfile +(状況により)pnpm fetch
pnpm は 公式でDocker向けレシピがあります🍳
BuildKitのキャッシュマウントや、CIでの pnpm fetch の使い分けもまとまってて助かるやつです🙏(pnpm)
Dockerfile例(BuildKitキャッシュマウント)⚡️
## syntax=docker/dockerfile:1
FROM node:20-slim@sha256:<digest> AS base
WORKDIR /app
## corepackが使える前提なら enable(後で注意点あり)
RUN corepack enable
COPY pnpm-lock.yaml package.json ./
## pnpmストアをキャッシュして爆速化
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --frozen-lockfile
FROM base AS app
COPY . .
CMD ["pnpm","start"]
CIでキャッシュマウントが効きにくいなら:pnpm fetch も検討🧳
pnpm公式は「CIだと環境が使い捨てで、cache mountが効かない場合がある → そのとき pnpm fetch が有効」と説明しています📌(pnpm)
5) 手順③:ツール(特にCorepack周り)を固定する🔧🔒
ここ、2025〜2026の罠ポイントです🕳️
⚠️ Corepackは “Node v25 以降は同梱されない” 🧨
Node のドキュメントで、Node.js v25 からCorepackを同梱しないと明記されています。(Node.js)
つまり、Dockerfileで corepack enable 前提の書き方をすると、ベースイメージ次第で突然コケることがあります😵
✅ 対策(どれか)
- Corepackが入ってる系のベースを使う(例:LTS系など)
- もしくは userland版Corepackを自分で入れる(Nodeドキュメントが案内)(Node.js)
“どれを選ぶか”はチーム事情でOKだけど、教材的には 👉 **「Corepackが無い環境でも動くように明示する」**が最強です💪
6) “固定してるつもり”で壊れる典型パターン集 😭🧯
❌ npm install をDockerfileで使ってる
npm install はロックを書き換えることがあり得るので、再現性が落ちやすいです😵
固定したいなら npm ci に寄せるのが安全です🔒(npm ドキュメント)
❌ npm ci がエラーで止まる
package.json とロックの内容がズレてると npm ci はエラーで止まります(ロックを直してくれません)🧊
これが “固定のための仕様” です✅(npm ドキュメント)
❌ ベースイメージをタグだけで指定してる
同じタグでも中身が変わるので、再ビルドで挙動が変わります🫨 digest固定で止血できます🩹(Docker Documentation)
❌ 固定しすぎて脆弱性アップデートが止まる
digest固定は「勝手に更新されない」=良くも悪くも止まります🧊 だから、更新はPRで回すのが安全です(Docker Scout等でPR化も可能)(Docker Documentation)
7) 🧪ミニ演習:再現性テスト(“同じ結果になる?”)✅🔁
演習A:npm ci の“固定力”を体感する🧼
- ロックファイルをコミットしている前提で、まずビルド
package.jsonをちょっと変えて(例:依存のバージョン範囲を変える)ビルドnpm ciが ズレを許さず止まるのを確認(=再現性の番人)🛡️(npm ドキュメント)
演習B:digest固定の効果を確認する🧷
FROM node:20-slim(タグのみ)でビルドFROM node:20-slim@sha256:...(digest固定)でビルド- 「固定できてる感」を得るために、Dockerfile上で参照が固定になってることを確認📌(Docker Documentation) (※タグのみの“中身が将来変わる”問題は、時間差で効いてくるタイプです⌛️)
演習C:pnpmで “CI向け” の作戦を言語化する🧠
- 「ローカルは cache mount で速く」
- 「CIは
pnpm fetchでレイヤキャッシュを活かす」 この方針を 自分の言葉で2行にしてメモ✍️(pnpm)
8) 🤖AI活用:レビューの型(プロンプト付き)🧰✨
プロンプト1:固定ポイント棚卸し(超おすすめ)🕵️♂️
あなたはDockerfileレビュー担当です。
このDockerfileの「再現性が壊れる可能性がある箇所」を列挙し、
(1) 何が起きるか
(2) どう直すか(具体的なDockerfile差分)
(3) 直した結果、キャッシュはどう良くなるか
をセットで提案してください。
特に「ベースイメージ」「依存インストール」「Corepack周り」を重点的に見てください。
プロンプト2:npm ci エラーの直し方ガイド🧯
npm ci が「package.json と lock が一致しない」と言って失敗します。
原因の候補を優先度順に出して、最短で直す手順を提案してください。
(例:npm installしてlock更新 → どこまでコミット → CIで再確認)
※ npm ci は “ズレたら止まる” のが仕様です🧊(npm ドキュメント)
プロンプト3:pnpmのDocker最適化(状況別)🍳
pnpmプロジェクトです。
ローカル開発ではBuildKitのcache mountを使えますが、CIでは効かないことがあります。
pnpm公式の考え方に沿って、
(1) ローカル用Dockerfile
(2) CI用Dockerfile(pnpm fetch活用)
の2案を最小構成で出してください。
pnpm公式の「CIではfetchが効く」説明に沿わせるのがコツです🧠(pnpm)
9) おまけ:さらに“壊れない”を強くする(SBOM/Provenance)🧾🔏
「同じものが作れた」だけじゃなく、 「どう作られたか」をメタデータで残せます📎
Dockerは BuildKit による attestations(SBOMやProvenance) を用意しています。(Docker Documentation) 教材では最後に軽く触れるくらいでOKだけど、将来の実務でめちゃ効きます🔥
まとめ:第28章の“持ち帰り”🎁✅
- タグだけは危ない → digest固定で“同じ土台”にする🧷(Docker Documentation)
- 依存はロック+固定インストール → npmは
npm ciが王道👑(npm ドキュメント) - ツールも揺れる → CorepackはNode v25以降で同梱されない点に注意⚠️(Node.js)
次章(第29章)は、この固定ポイントを AIにレビューさせて“差分の説明ができる”ようになる回だよ🤖🗣️✨ 第28章で作ったDockerfile、レビュー用にそのまま投げられる状態にしておくと気持ちいいです😆👍