pnpmを使ったNode.js Dockerマルチステージビルドの最適化とレイヤー軽量化
Docker pnpm Node.js DevOps
結論
BuildKitキャッシュマウントと pnpm --prod deploy を組み合わせ、最終イメージへ本番アセットのみをコピーします。
# pnpm公式仕様:マルチステージ Dockerfile
FROM node:20-alpine AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME/bin:$PATH"
RUN corepack enable
FROM base AS build
COPY . /app
WORKDIR /app
-- pnpm store のキャッシュを有効化
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
RUN pnpm run build
-- 本番用パッケージの抽出
FROM base AS deployer
WORKDIR /app
COPY --from=build /app /app
RUN pnpm --filter=my-app --prod deploy /pruned
-- 最終本番イメージ
FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=deployer /pruned .
USER node
EXPOSE 3000
CMD ["node", "dist/index.js"]
pnpm公式推奨:pnpm deploy の仕組み
pnpm公式ドキュメントで解説されている pnpm deploy コマンドは、モノレポや単一プロジェクトから devDependencies(ビルドツール類)を完全に削ぎ落とし、指定のディレクトリ(例: /pruned)に実行に必要な本番アセットだけを集約・コピーする機能です。
実際に起こる事故:pnpm deploy が失敗する罠
1. pnpm-workspace.yaml の設定漏れ
モノレポ環境で pnpm deploy を実行する際、pnpm-workspace.yaml に inject-workspace-packages: true を記述していないと、ローカルパッケージの依存関係解決に失敗してビルドがストップします。
# pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'
inject-workspace-packages: true
2. Corepack のストアキャッシュ未適用によるビルド低速化
RUN --mount=type=cache を記述しない場合、Dockerビルドのたびに全パッケージがネットワーク経由で再ダウンロードされ、ビルド時間が数分単位に肥大化します。
最適化手順
- Dockerfileの各ステージで Corepack を有効化し
pnpmコマンドを使用可能にする RUN --mount=type=cache,id=pnpm,target=/pnpm/storeでローカルビルドキャッシュを保持する- 最終ランタイムステージに
COPY --from=deployer /pruned .で最小構成のアセットのみを移植する