Dockerを軽量化するマルチステージビルド(Next.jsとNest.jsのTypeScript開発)

# 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はざっくりこんな感じです。
```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```です。
```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```です。
```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```ほどです。
***シングルストレージの半分の容量で構築***ができています。

![スクリーンショット 2025-02-07 0.58.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748789/32dd5771-ad5b-18b1-a9d7-06b66086f8b4.png)


# 参考動画、資料
Dockerイメージを100分の1の容量にする手法がまとめられている動画はこちら
1.2GBから10MBまで圧縮できるみたいで、
***マルチステージビルド***というやり方はdocker使う上では常識的に知っておくべき手法みたいです。

[Docker Image BEST Practices - From 1.2GB to 10MB](https://www.youtube.com/watch?v=t779DVjCKCs)

- ChatGPT参照

https://zenn.dev/rehabforjapan/articles/save-data-space-dockerfile