Dockerのマルチステージビルドとは
Dockerfile内で複数のビルドステージを定義し、軽量な本番用イメージを作成するやり方です。これのおかげで大きなイメージ(例えば1.2GBなど)から、運用に必要な最小限のファイルだけを含む軽量なイメージ(例:100MB)にまで縮小することが可能になります。
Dockerって構築すると容量などが取られるのでそこがネックでしたが、それを解決する手法になりそうです。
メリット
不要なファイルやツールの排除ができ、転送・デプロイが高速になり最終イメージも軽量化できます。
デメリット
Dockerfileの複雑化、中間ステージの内容が最終イメージに含まれずデバックが難しくなり、学習コストが増加します。
マルチステージビルドの仕組み
-
複数のFROM
Dockerfile内に複数のFROMを記述することで、複数のステージを作成します。 -
ビルドステージ
コンパイルやトランスパイル、テストなどを実行し、最終成果物を作ります。 -
本番ステージ
ビルドステージから必要な成果物だけをCOPY --from=<ステージ名>で取り込み、実行環境のみを構築する。 -
不要なファイル・ツールを含まない
ビルド時に必要なツールは最終イメージには含めないため、イメージサイズを大幅に削減できます。
Next.jsとNest.jsで構築
Next.jsとNest.jsのTYpeScriptの開発で検証してみます。あくまで一例です。
至らない点はありますがご了承ください。
ファイル構成はこんな感じにします。
docker-multi-stage-build-next-nest-ts/
├── client/ # Next.js(フロントエンド)プロジェクト
│ ├── package.json
│ ├── tsconfig.json
│ ├── src/
│ │ └── pages/
│ │ └── index.tsx
│ └── Dockerfile
├── server/ # Nest.js(バックエンド)プロジェクト
│ ├── package.json
│ ├── tsconfig.json
│ ├── src/
│ │ ├── main.ts
│ │ ├── app.module.ts
│ │ └── todos/
│ │ ├── todos.controller.ts
│ │ ├── todos.module.ts
│ │ └── todos.service.ts
│ └── Dockerfile
└── docker-compose.yml
それぞれの中身を見ていきます。docker-compose.ymlはざっくりこんな感じです。
version: '3'
services:
client:
build: ./client
ports:
- "3000:3000"
depends_on:
- server
server:
build: ./server
ports:
- "3001:3001"
ここからマルチステージビルドをやっていきます。
Next.jsのclient/Dockerfile
です。
# --- Stage 1: ビルドステージ ---
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# --- Stage 2: 本番用ステージ ---
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./
RUN npm install --only=production
EXPOSE 3000
CMD ["npm", "start"]
Nest.jsのserver/Dockerfile
です。
# --- Stage 1: ビルドステージ ---
FROM node:16 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# --- Stage 2: 本番用ステージ ---
FROM node:16-alpine
WORKDIR /app
# ビルド済みの成果物だけをコピー
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./
RUN npm install --only=production
EXPOSE 3001
CMD ["node", "dist/main.js"]
Docker
上の状態でdockerを起動します。
docker compose up --build -d
Dockerは普通に停止できます。
docker compose down
起動する時は以下のコマンドにします。
docker compose up -d
両者の比較
実際にこんだけ違いました。In useがマルチストレージ
、Unusedがシングルストレージ
です。
マルチストレージが817MB
、シングルストレージが1.64GB
ほどです。
シングルストレージの半分の容量で構築ができています。
参考動画、資料
Dockerイメージを100分の1の容量にする手法がまとめられている動画はこちら
1.2GBから10MBまで圧縮できるみたいで、
マルチステージビルドというやり方はdocker使う上では常識的に知っておくべき手法みたいです。
Docker Image BEST Practices - From 1.2GB to 10MB
- ChatGPT参照