TL;DR
- 思ったより綺麗なNext.js v14用Dockerfileが書けた記念
- distroless/nodejs22を使用。530MBのイメージ。
本題
Dockerfile本体
# === base
FROM --platform=${BUILDPLATFORM:-linux/arm64} node:22-slim AS base
# === deps
FROM base AS deps
WORKDIR /app
COPY package* .
RUN npm install
# === builder
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# === prod deps
FROM base AS prod_deps
WORKDIR /app
COPY --from=deps /app/package* .
COPY --from=deps /app/node_modules ./node_modules
RUN npm ci --omit=dev
# === runner
FROM --platform=${BUILDPLATFORM:-linux/arm64} gcr.io/distroless/nodejs22-debian12 AS runner
WORKDIR /app
ENV HOSTNAME=0.0.0.0
ENV NODE_ENV=production
ENV PORT=3000
COPY --from=prod_deps --chown=nonroot:nonroot /app/package* .
COPY --from=prod_deps --chown=nonroot:nonroot /app/node_modules ./node_modules
COPY --from=builder --chown=nonroot:nonroot /app/public ./public
COPY --from=builder --chown=nonroot:nonroot /app/.next ./.next
USER nonroot
EXPOSE 3000
CMD ["node_modules/.bin/next", "start"]
解説
# === base
# マルチプラットフォーム対応のベースとなる中間イメージ。ベースイメージとしてこれを使用する。
FROM --platform=${BUILDPLATFORM:-linux/arm64} node:22-slim AS base
# === deps
# npm install をするだけの中間イメージ
# composeでtargetをこのイメージにすることで使用することを想定。ローカル開発用に分けている。
FROM base AS deps
WORKDIR /app
COPY package* .
RUN npm install
# === builder
# nextのビルドを実行し、 ".next" ディレクトリを生成
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# === prod deps
# 最終イメージで使用しないパッケージをオミットする中間イメージ
FROM base AS prod_deps
WORKDIR /app
COPY --from=deps /app/package* .
COPY --from=deps /app/node_modules ./node_modules
RUN npm ci --omit=dev
# === runner
# distrolessを使用した最終イメージ
FROM --platform=${BUILDPLATFORM:-linux/arm64} gcr.io/distroless/nodejs22-debian12 AS runner
WORKDIR /app
ENV HOSTNAME=0.0.0.0
ENV NODE_ENV=production
ENV PORT=3000
# 必要最低限なnode_modulesを取得
COPY --from=prod_deps --chown=nonroot:nonroot /app/package* .
COPY --from=prod_deps --chown=nonroot:nonroot /app/node_modules ./node_modules
# 生成したディレクトリを取得
COPY --from=builder --chown=nonroot:nonroot /app/public ./public
COPY --from=builder --chown=nonroot:nonroot /app/.next ./.next
# nonrootにすることでセキュリティ対策
USER nonroot
EXPOSE 3000
# distrolessはnpmが入っていないので、nextコマンドから直接起動
CMD ["node_modules/.bin/next", "start"]