Traffine I/O

日本語

2022-06-03

Dockerにおけるマルチステージビルド

マルチステージビルドとは

マルチステージビルドは、コンテナ化されたアプリケーションにおいて、ビルドプロセスを簡素化し、イメージサイズを縮小し、セキュリティを強化する機能です。 Dockerfile内で複数のFROMステートメントを許容することで、マルチステージビルドはビルドプロセス中のみ存在する一時的な中間イメージの作成を可能にします。

マルチステージビルドの各ステージは異なるベースイメージから継承されるため、特定のタスクに合わせてカスタマイズされたさまざまなツール、ライブラリ、および環境を使用することができます。その結果、開発者は、コンパイルやテストなどの各ステージを最適化することができますが、最終イメージには影響を与えません。ビルドプロセスの最後のステージのみがデプロイされる出力イメージを生成するため、必要なファイルと依存関係のみが含まれるようになります。

マルチステージビルドの使用により、クリーンでスリムなDockerイメージを作成するだけでなく、複雑なビルドプロセスを管理するのが簡単になります。ビルドプロセスを異なるステージに分割することで、開発者は個々のタスクに集中でき、コードの可読性を高め、問題が発生した際にトラブルシューティングを簡素化できます。さらに、マルチステージビルドにより、特定のステージを他のプロジェクトでも再利用できるため、コードのモジュラリティと再利用性が向上します。

マルチステージビルドのメリット

イメージサイズの縮小

マルチステージビルドの主な利点の1つは、アプリケーションの実行に必要なファイルと依存関係のみを含めることで最終的なDockerイメージサイズを最小限に抑えることができます。中間のビルドステージには、ビルドプロセス中に必要なビルドツール、コンパイラ、およびその他の依存関係が含まれていることがありますが、最終ステージに必要なアーティファクトのみを中間ステージから最終ステージにコピーすることで、よりスリムで効率的なイメージを作成することができます。これにより、展開が高速になり、リソースを消費することが少なくなります。

セキュリティの強化

マルチステージビルドは、最終的なDockerイメージ内のコンポーネントの数を最小限に抑えることで、コンテナ化されたアプリケーションのセキュリティの強化に貢献することができます。潜在的なセキュリティ上の脆弱性の攻撃対象を減らすことは、アプリケーションの完全性と安全性を維持するために重要です。より少ないコンポーネントでフォーカスされたイメージを作成することで、開発者はセキュリティを管理し、組織のポリシーや業界基準とのコンプライアンスを確保することができます。

また、マルチステージビルドにより、アプリケーションのビルドと実行に別々の環境を使用できるため、セキュリティをさらに向上させることができます。例えば、ビルドステージでは、開発ツールやライブラリが含まれたイメージを使用し、最終ステージでは、ランタイム環境のみが含まれた最小限のプロダクションイメージを使用することができます。

ビルドプロセスの簡素化

マルチステージビルドにより、ビルドプロセスを複数のステージに分割することで、複雑なビルドプロセスを簡素化し、可読性とメンテナンス性を向上させることができます。各ステージは、ソースコードのコンパイル、テストの実行、またはアセットの生成など、特定のタスクに焦点を当てることができるため、全体のプロセスを理解し、問題が発生した場合のトラブルシューティングが簡単になります。

さらに、マルチステージビルドにより、特定のビルドステージを異なるプロジェクトで再利用できるため、コードのモジュラリティと再利用性を促進することができます。例えば、複数のプロジェクトで同じツールチェーンやビルド環境を使用する場合、共通の中間ステージを作成して共有することができ、コードの重複とメンテナンス作業を減らすことができます。

マルチステージビルドの実装方法

マルチステージビルドを実装するには、以下の手順に従います。

  1. 各ステージのベースイメージを定義
    Dockerfile内で複数のFROMステートメントを使用して、各ステージのベースイメージを定義します。ASキーワードを使用して、各ステージに一意のエイリアスを付けます。これは、後でステージを参照するために使用されます。
Dockerfile
# Build stage
FROM node:14 AS build
  1. 作業ディレクトリを指定
    WORKDIRステートメントを使用して、各ステージの作業ディレクトリを指定します。
Dockerfile
WORKDIR /app
  1. 必要なファイルをコピーして依存関係をインストール
    COPYおよびRUN命令を使用して、各ステージの必要なファイルをコピーし、依存関係をインストールします。
Dockerfile
# Copy package files and install dependencies
COPY package*.json ./
RUN npm install
  1. アプリケーションをコンパイルまたはビルド
    アプリケーションに固有の必要なコンパイルやビルドステップを実行します。
Dockerfile
# Copy source files and build the application
COPY . .
RUN npm run build
  1. 最終ステージを定義
    最終ステージのベースイメージを指定し、作業ディレクトリを設定します。
Dockerfile
# Final stage
FROM node:14-alpine AS final
WORKDIR /app
  1. 前のステージからアーティファクトをコピー
    COPY命令に--fromフラグを使用して、前のステージからアーティファクトまたはファイルを最終ステージにコピーします。
Dockerfile
# Copy build artifacts from build stage
COPY --from=build /app/dist /app/dist
  1. ランタイム環境を構成
    必要な環境変数、公開するポート、アプリケーションのエントリーポイントを設定します。
Dockerfile
# Set environment variables
ENV NODE_ENV=production

# Expose the application port
EXPOSE 3000

# Define the entry point
CMD ["npm", "start"]

--targetオプションを使用して開発ステージと本番ステージをビルド

この章では、--targetオプションを使用して、単一のDockerfileから開発ステージと本番ステージをビルドする方法について説明します。これにより、別々のDockerfileを維持することなく、特定の環境に合わせてカスタマイズされたDockerイメージをビルドすることができます。

開発ステージのビルド

マルチステージDockerfileから開発ステージをビルドするには、--targetオプションを使用して、開発ステージのエイリアスを指定して、docker buildコマンドを実行します。

例えば、次のDockerfileがある場合とします。

Dockerfile
# Develop stage
FROM node:14 AS develop
WORKDIR /app
COPY package*.json ./
RUN npm install
ENV NODE_ENV=development
CMD ["npm", "run", "dev"]

# Production stage
FROM node:14-alpine AS production
WORKDIR /app
COPY --from=develop /app/node_modules /app/node_modules
COPY . .
RUN npm run build
ENV NODE_ENV=production
CMD ["npm", "start"]

開発ステージをビルドするには、次のように実行します。

bash
$ docker build -t my-app:develop --target develop .

これにより、開発環境に最適化されたDockerイメージがmy-app:developというタグで作成されます。

本番ステージのビルド

本番ステージをビルドするには、--targetオプションを省略するか、本番ステージのエイリアスを指定します。

例えば、前述のDockerfileを使用した場合を考えます。

本番ステージをビルドするには、次のように実行します。

bash
$ docker build -t my-app:production --target production .

これにより、本番環境に最適化されたDockerイメージがmy-app:productionというタグで作成されます。

マルチステージビルドの実例

この章では、さまざまなプログラミング言語やフレームワークに対してマルチステージビルドの実際の例について説明します。

Node.jsアプリケーションのビルド

この例では、Node.jsアプリケーション用にnode:14ベースイメージをビルドステージに、node:14-alpineイメージを最終ステージに使用したマルチステージビルドを作成します。

Dockerfile
# Build stage
FROM node:14 AS build
WORKDIR /app

# Copy package files and install dependencies
COPY package*.json ./
RUN npm install

# Copy source files and build the application
COPY . .
RUN npm run build

# Final stage
FROM node:14-alpine AS final
WORKDIR /app

# Copy build artifacts from build stage
COPY --from=build /app/dist /app/dist

# Set environment variables
ENV NODE_ENV=production

# Expose the application port
EXPOSE 3000

# Define the entry point
CMD ["npm", "start"]

Golangバイナリのコンパイル

この例では、Golangアプリケーション用に、golang:1.17ベースイメージをビルドステージに、scratchイメージを最終ステージに使用したマルチステージビルドを作成します。

# Build stage
FROM golang:1.17 AS build
WORKDIR /src

# Copy source files and compile the application
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o app

# Final stage
FROM scratch AS final
WORKDIR /app

# Copy the compiled binary from the build stage
COPY --from=build /src/app .

# Expose the application port
EXPOSE 8080

# Define the entry point
CMD ["./app"]

Python Webアプリケーションの作成

この例では、Flaskを使用したPython Webアプリケーション用に、python:3.9ベースイメージをビルドステージに、python:3.9-slimイメージを最終ステージに使用したマルチステージビルドを作成します。

# Build stage
FROM python:3.9 AS build
WORKDIR /app

# Copy requirements file and install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy source files
COPY . .

# Final stage
FROM python:3.9-slim AS final
WORKDIR /app

# Copy installed dependencies and source files from the build stage
COPY --from=build /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages
COPY --from=build /app .

# Expose the application port
EXPOSE 5000

# Define the entry point
CMD ["python", "app.py"]

参考

https://docs.docker.com/build/building/multi-stage/
https://earthly.dev/blog/docker-multistage/
https://dev.to/pavanbelagatti/what-are-multi-stage-docker-builds-1mi9

Ryusei Kakujo

researchgatelinkedingithub

Focusing on data science for mobility

Bench Press 100kg!