🎉

Docker のマルチステージビルドとは?

2024/06/26に公開

はじめに

Cloud Run でアプリを公開した時、 Dockerfile を作成した。
開発環境で見かける Dockerfile と異なり、 FROM 命令が多い印象を受けた。
なぜ FROM が多いのか?

https://zenn.dev/masayuki_0319/articles/056622534b4056

FROM node:20-alpine as base

# 作業ディレクトリを設定
WORKDIR /app

# 依存関係のインストール
FROM base as deps
COPY package.json yarn.lock ./
RUN yarn install

# ビルド
FROM base as build
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN yarn build

# 本番環境
FROM base as production
ENV NODE_ENV=production

# 必要なファイルをコピー
COPY --from=build /app/build ./build
COPY --from=build /app/public ./public
COPY --from=deps /app/node_modules ./node_modules
COPY package.json ./

# アプリケーションを起動
CMD ["yarn", "start"]

# ポートを公開
EXPOSE 3000

マルチステージビルド

1つの Dockerfile に FROM を複数記述する方式は、マルチステージビルドと呼ぶ。
以下のメリットがあるとのこと。

  • イメージサイズの最小化
    • 最終的な本番用イメージには、アプリケーションの実行に必要な最小限のファイルのみが含まれる。ビルドツールや中間生成物は含まれない。
  • セキュリティの向上
    • ソースコードやビルドツールが最終イメージに含まれないため、攻撃対象が減少する。
  • ビルドプロセスの最適化
    • 依存関係のインストールやビルドプロセスが分離されているため、キャッシュを効率的に利用できる。
  • 開発と本番環境の分離
    • 開発用のツールと本番環境で必要なものを明確に分離できる。

Dockerfile の説明

今回作成した Dockerfile は4つのステージで構成されている。

  1. base: 基本イメージを設定
  2. deps: 依存関係をインストール
  3. build: アプリケーションをビルド
  4. production: 本番環境用の最終イメージを作成

初めに、基本イメージを設定し、作業ディレクトリを指定する。

FROM node:20-alpine as base
WORKDIR /app

次に、deps ステージを作成し、基本イメージを元に依存関係をインストールする。

FROM base as deps
COPY package.json yarn.lock ./
RUN yarn install

次に、 build ステージを作成し、基本イメージを元にアプリケーションをビルドする。deps ステージからnode_modulesをコピーし、ソースコードをコピーしてビルドを実行する。

FROM base as build
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN yarn build

最後に、本番用イメージを作成する。必要なファイルのみを前のステージからコピーし、アプリケーションを起動するコマンドを設定している。
このイメージが Cloud Run でデプロイされるイメージとなる。

FROM base as production
ENV NODE_ENV=production
COPY --from=build /app/build ./build
COPY --from=build /app/public ./public
COPY --from=deps /app/node_modules ./node_modules
COPY package.json ./
CMD ["yarn", "start"]
EXPOSE 3000

終わりに

最初この Dockerfile を見た時、FROM を1つにして、全てを1つのコンテナイメージでビルドすると良いと思った。

しかし、そうするとイメージサイズが大きくなり、不要なツールや中間ファイルが本番環境に含まれてしまう可能性がある。

確かに Google Cloud の料金を鑑みると、コンテナイメージを保存する Artifact Registry はイメージサイズが大きいほど料金が発生するし、 CI/CD を担う Cloud Build もビルド時間が長くなると料金が発生する。そして、昔からコンテナのイメージサイズを小さくするよう言われた記憶もある。

クラウドでコンテナを扱う時の良い手法と理解した。

参考情報

Discussion