[公式]: Next.js × Docker 本番環境サンプル
Next.jsをDocker上で動かす
こちらの記事ではNext.js開発環境をDocker上に作成するための手順を紹介いたします。
要点は以下の通りです。
- Dockerとローカルそれぞれでnode_modulesなど必要モジュールを用意する
- 開発時はsrcディレクトリをバインドマウント → ホットリロードを可能にする
- 同環境で本番ビルドと実行ができる
問題点があればぜひご指摘ください
1. プロジェクト作成
まずはテンプレートの作成です。
npx create-next-app@latest
選択肢は全てYesにしました。
今回の構成と同じにする場合は、srcを使用する選択をしてください。
2. 構成ファイルの追加
今回は追加でDockerの設定ファイルを作成します。
頻繁に使用する開発(デフォルト)用と、
たまに使用する本番環境用とで、
それぞれのDockerfileとcompose.yamlを用意します。
共通になりますが、.dockerignoreも作成します。
共通ファイル
.dockerignore
build時、コンテクスト(構築用ファイル群)に含めたくないものを指定します。
ここに含めたものは、COPYなどの対象になりません。
必要に応じて追加します。
.git
.gitignore
.dockerignore
Dockerfile*
*.yaml
.md
.next
.swc
node_modules
開発環境実行用
develop.sh
./develop.shとして実行すると起動します。
ファイルの実行権限が必要です。
ない場合はchmod u+x ./develop.shなどで追加します。
-fオプションによってcomposeファイルが組み合わされて実行されます。
./develop.sh bと半角空白+bをつけて実行すると再ビルドが走ります。
本番環境実行直後、開発に戻る時はそのように実行するとよいです。
#!/bin/bash
if [ "$1" = "b" ]; then
docker compose -f compose.yaml build
fi
docker compose -f compose.yaml up
Dockerfile
FROM node:22.11.0-slim
ENV NODE_ENV=development
USER node
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY --chown=node:node ./*.*js* ./*.ts ./
COPY --chown=node:node ./public ./public
ENV HOSTNAME="0.0.0.0"
CMD ["npm", "run", "dev"]
コードの補足
-
npm ci
node_modulesを一度消してからpackage-lock.jsonを書き換えずに依存関係をインストールします。
package-lock.jsonの通りに再現するためのコマンドです。 -
COPY
./にある.js, .json, .mjs, .tsをコピーします。
./publicも同様です。他のディレクトリを作成した場合は追記してください。
補足事項
追加のインストールが必要な時は、ローカルでインストールしてから、Dockerの再ビルドを行います。
npm install <モジュール名> <モジュール名> ...
↓
↓
./develop.sh b
これによってローカルのモジュール更新 + package-lock.jsonの更新によりDockerのビルド時に更新が入ります。
また、「/」配下の各設定ファイルはマウント非対象のため、これらを変更した場合も再ビルドが必要になります。
compose.yaml
dev用の項目を書きます。
prodと共通の項目があってもどちらにも書きます。
他のコンテナなどを追加する場合も同様です。
services:
nextjs:
build:
context: .
dockerfile: Dockerfile
container_name:
nextjs-dev
ports:
- "3000:3000"
volumes:
- ./src/:/app/src/
本番環境実行用
production.sh
./production.shとして実行すると起動します。
ファイルの実行権限が必要です。
ない場合はchmod u+x ./production.shなどで追加します。
#!/bin/bash
if [ "$1" = "b" ]; then
docker compose -f compose.prod.yaml build
fi
docker compose -f compose.prod.yaml up
Dockerfile.prod
本番実行の時はcompose.prod.yaml内の指定でこちらが選択されます。
公式のものを多少削って利用しております。
FROM node:22.11.0-slim AS base
FROM base AS deps
ENV NODE_ENV=production
WORKDIR /app
COPY package*.json ./
RUN npm ci --include=dev
FROM base AS builder
ENV NODE_ENV=production
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM base AS runner
ENV NODE_ENV=production
WORKDIR /app
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]
compose.prod.yaml
prod用の項目を書きます。
devと共通の項目があってもどちらにも書きます。
他のコンテナなどを追加する場合も同様です。
services:
nextjs:
build:
context: .
dockerfile: Dockerfile.prod
container_name:
nextjs-prod
ports:
- "3000:3000"
next.config.ts
1行追加します。
本番環境に最適化されたコードが出力されるそうです。
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
+ output: "standalone",
};
export default nextConfig;
以上です!
最後まで見て頂き、ありがとうございます!
再ビルドが必要なタイミング
- モジュール追加後
- develop ↔ production環境を切り替える時
- 設定ファイルに変更を加えた時
今回はDockerに関する部分を紹介させていただきましたが、他にもESLintの設定などもありますね。
Dockerのドキュメントを眺めながら作ってみました。
一応やりたいことができて満足しましたが、もっといいやり方があるなどあればぜひ教えて頂きたいです!
この方法では、AWSのEC2インスタンス無料枠:t2.micro内でのビルドはcpu使用量が厳しくて難しいようでした。
(ソースコードの量や使用モジュールの量が少なければ大丈夫です)
今回のサンプルコードの状態でcpu最大使用率68%のようです。(メトリクス参照)
シェルスクリプト作成の参考にさせて頂きました!