DockerでマルチモジュールのMavenプロジェクトをDocker Multi-stage buildsでビルドする
はじめに
MavenプロジェクトをDockerコンテナ化する際に、Docker Multi-stage buildsを活用して効率的にイメージを構築する方法について理解を深めたいという思いで執筆しています。
Multi-stage builds おさらい
Multi-stage buildsは、一つのDockerfileの中で複数のFROM
ステートメントを使用し、段階的にイメージを構築する手法です。
なぜMulti-stage buildsが必要なのか おさらい
従来の単一ステージでのビルドでは、以下のような問題がありました。
- ビルドツール(Maven、コンパイラなど)が本番イメージに含まれる
- ソースコードや中間ファイルが残る
- イメージサイズが大きくなる
- セキュリティリスクが増大する
Multi-stage buildsを使用することで、これらの問題を解決できます。
ステージ分割の戦略を考える
今回はMavenを使用したJava/Kotlinプロジェクトにおける最適なステージ分割について考えてみます。
Mavenプロジェクトの構成
本記事ではMavenプロジェクトは以下のようにmodule構成で作成しているとします。
/root
├── pom.xml # 親pom
├── rest/ # REST APIレイヤー
│ ├── pom.xml
│ └── src/main/kotlin/
└── usecase/ # ビジネスロジック・ユースケース
├── pom.xml
└── src/main/kotlin/
この要件を踏まえ、ステージ分割戦略を検討します。
3ステージ戦略
baeldung.comの内容を読むと、マルチモジュールであることを考慮すると、実践的な観点からステージを依存関係
,ビルド
,実行
の3つの責務に分けることが最も効果的と理解しています。
※Jib Maven plugin
はここでは取り扱いません。
1. dependency(依存関係)ステージ
ビルドに必要な依存関係の解決とキャッシュ効率の向上
FROM maven:3.9.5-openjdk-21 AS dependency
WORKDIR /app
COPY pom.xml ./
COPY rest/pom.xml rest/
COPY usecase/pom.xml usecase/
RUN mvn dependency:go-offline -B
この分離により、依存関係の変更がない限りこのレイヤーがキャッシュされ、ビルド時間を大幅に短縮できます。
2. build(ビルド)ステージ
アプリケーションのコンパイルとアーティファクトの生成
FROM dependency AS build
WORKDIR /app
COPY /root/.m2 /root/.m2
COPY . .
RUN mvn clean package
ソースコードをコピーしてpackage化していきます。
ソースコードをあえて分離することで、コード変更時のみこのステージが再実行され、ビルド効率の向上が期待できます。
3. runtime(実行)ステージ
最小限の実行環境でのアプリケーション実行
FROM eclipse-temurin:21-jre-alpine AS runtime
WORKDIR /app
COPY /app/rest/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
ビルドステージから必要なアーティファクトのみをコピーし、軽量な実行専用イメージを作成します。
所感
Docker Multi-stage buildsは、現代のコンテナ開発において必須の技術だと思いますが、ステージ分割の戦略を適切に設計することが重要だと改めて感じました。
ここではマルチモジュールMavenプロジェクトに対してフォーカスを当てていましたが、他のビルドツールや言語にも応用できる部分はあると思うので、モジュール化の観点からも考えていけるようにしていきたいところです。
Discussion