Skip to main content

第28章:“速いのに壊れない”ための固定テク(バージョン/再現性)🔒⚡️

「昨日ビルドできたのに、今日ビルドしたら壊れた😇」 これ、原因の多くは “どこかが勝手に新しくなった” です💥

この章では、速さ(キャッシュ)を活かしながら、再ビルドしても同じ結果になりやすい 固定テクをまとめて身につけます💪✨ 固定できると、キャッシュも効きやすくなって「待ち時間」も減ります⏱️🎉


1) 今日の主役:固定すべき“3点セット”🧊🧊🧊

再現性は、だいたいこの3つで決まります👇

  1. ベースイメージ固定(タグだけに頼らない)🧱
  2. 依存固定(ロックファイル+CI向けインストール)📦
  3. ツール固定(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 の“固定力”を体感する🧼

  1. ロックファイルをコミットしている前提で、まずビルド
  2. package.json をちょっと変えて(例:依存のバージョン範囲を変える)ビルド
  3. npm ciズレを許さず止まるのを確認(=再現性の番人)🛡️(npm ドキュメント)

演習B:digest固定の効果を確認する🧷

  1. FROM node:20-slim(タグのみ)でビルド
  2. FROM node:20-slim@sha256:...(digest固定)でビルド
  3. 「固定できてる感」を得るために、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、レビュー用にそのまま投げられる状態にしておくと気持ちいいです😆👍