Next.jsのstandaloneのビルドとDockerのマルチステージビルドを試してみたら良い感じだったので書いてみました!
Next.jsのstandaloneのビルドとは?
アプリケーションをビルドするために必要なファイル(node_modulesも含め)を特定してビルドしてくれます。
→ ビルドのサイズを大幅に削減することができる!
使い方は簡単で、next.config.js
の下記の部分に output: 'standalone'
を追加するだけです。
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
/* ↓↓↓追加した部分↓↓↓ */
output: 'standalone',
/* ↑↑↑追加した部分↑↑↑ */
}
module.exports = nextConfig
以前は、next start
を実行するためにパッケージの依存関係からすべてのファイルをインストールする必要があったみたいです。
内部では @vercel/nft
を使用して、import、require、およびfsの使用状況を静的に分析して、ページが読み込む可能性のあるファイルを決定しているみたいです。
↓より詳しく知りたい方は覗いてみてください!
マルチステージビルドについて
1つのDockerfile内で複数のステージを定義して、効率的なイメージを作成することができます!
→ 最終的なイメージには必要なファイルのみ含まれて、イメージサイズが小さくなる!
マルチステージビルドが出る前は Dockerfile
を複数作り、シェルスクリプトと組み合わせたりして、同じようなことをやっていたみたいです。
実際に書いてみる
実際にDockerfileを書いてみました。create-next-appで作ったものに追加しています。
シンプルなDockerfileを作ってビルド
まず、特に工夫せずに書いたシンプルなDockerfileがこんな感じだと思います。
npmの場合
FROM node:18-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build
CMD [ "npm", "start" ]
yarnの場合
FROM node:18-alpine
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install
COPY . .
RUN yarn build
CMD [ "yarn", "start" ]
ビルド
以下のコマンドでビルドします。
docker build -t before .
standaloneやマルチステージビルドを使ったDockerfileを作ってビルド
次に、standaloneやマルチステージビルドを使ったDockerfileです。
※ next.config.js
に output: 'standalone'
を追記するのを忘れずに
npmの場合
# 外部イメージをbaseステージとして扱う
FROM node:18-alpine AS base
# baseステージをもとにbuilderステージを開始
FROM base AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build
# baseステージをもとにrunnerステージを開始
FROM base AS runner
WORKDIR /app
# public と .next/static は nextjs の standalone を使う場合に含まれないため、コピーする必要がある
# https://nextjs.org/docs/advanced-features/output-file-tracing#automatically-copying-traced-files
# builderから必要なファイルだけコピーする
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/.next/standalone ./
# `next start` の代わりに `node server.js` を使用
# https://nextjs.org/docs/advanced-features/output-file-tracing#automatically-copying-traced-files
CMD ["node", "server.js"]
yarnの場合
# 外部イメージをbaseステージとして扱う
FROM node:18-alpine AS base
# baseステージをもとにbuilderステージを開始
FROM base AS builder
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install
COPY . .
RUN yarn build
# baseステージをもとにrunnerステージを開始
FROM base AS runner
WORKDIR /app
# public と .next/static は nextjs の standalone を使う場合に含まれないため、コピーする必要がある
# https://nextjs.org/docs/advanced-features/output-file-tracing#automatically-copying-traced-files
# builderから必要なファイルだけコピーする
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/.next/standalone ./
# `next start` の代わりに `node server.js` を使用
# https://nextjs.org/docs/advanced-features/output-file-tracing#automatically-copying-traced-files
CMD ["node", "server.js"]
ビルド
以下のコマンドでビルドします
docker build -t after .
結果
イメージサイズの結果はそれぞれ以下のようになりました!
※create-next-appで作ったもので比較しています🙇
docker images --format "{{.Repository}} {{.Size}}"
# after 204MB
# before 966MB
約1/5になった、すごい!!!
実際のプロジェクトでは正常に動作することを確認することをお勧めします🙇
(実際のプロジェクトだとどれくらい効果あるんだろう)
最後に
ここまで読んでくださりありがとうございます!
読んでくださった方の何かしらの役に立てれば嬉しいです!感想などいただけますと励みになります!
ちなみに Next.js の examples も standalone とマルチステージビルドを使って書かれてました!
npx create-next-app --example with-docker <ディレクトリ名>
みたいな感じで簡単に使えますね!
参考にしたもの