普段はバックエンドエンジニアとしてJavaを中心に開発をしています。
今回は勉強ついでに個人で利用する家計簿アプリを作成しており、その際にMavenプロジェクトのDockerイメージを作成する手順を整理しました。
プロジェクト構成
Spring Bootの一般的なプロジェクト構成をベースにしています。プロジェクトのルートディレクトリに、Dockerfileとdocker-compose.ymlを配置するシンプルな構成です。
プロジェクトルート/
├─ src/
├─ .mvn/
├─ mvnw
├─ pom.xml
├─ Dockerfile
└─ docker-compose.yml
マルチステージビルド
1つのDockerfileの中で複数のFROM命令を使用して、コンテナの役割を分割する。
-
ビルド環境(Builder)
maven:3.9-eclipse-temurin-21を使用。
ソースコードをコンパイルし、.jarファイルを作成する役割をします。 -
実行環境(Runner)
eclipse-temurin:21-jre-alpineを使用。
Builder環境で作成された成果物だけをCOPY --rom=builderコマンドで受け取り、実際にアプリを動かす役割をします。
マルチステージビルドの強み
- イメージサイズの削減
最終的なイメージには、数百MBもあるMavenやJDK、ソースコードが含まれない。動かすためだけの軽量な「JRE(Java実行環境)」と最小減のOSのみになるため、イメージのダウンロード時間やストレージ容量を節約できる。
ビルドの高速化
毎回のビルド時間を短縮するために、Dockerのキャッシュマウント機能と、Maven Wrappeer(mvnw)を使用します。これによりローカル環境で事前にビルドしておく必要がなくなります。
# Maven Wrapperをコピー
COPY .mvn/ .mvn
COPY mvnw pom.xml ./
# キャッシュをマウントして依存関係をダウンロード
RUN --mount=type=cache,target=/root/.m2 \
./mvnw dependency:go-offline -B
--mount=type=cache,target=/root/.m2を指定することで、Docker内部に消えない保管庫が作成されます。これにより、2回目以降のビルドでは変更されたライブラリのみをダウンロードするようになり、高速になります。
セキュリティ対策
コンテナをデフォルトの管理者(root)権限で動かすにはセキュリティリスクが高いため、必要最低限の権限を持つ「システム専用の一般ユーザ」を作成してアプリを実行させる。
# Alpine Linux用のユーザー作成コマンド(ID: 1001)
RUN addgroup -g 1001 appgroup && \
adduser -u 1001 -G appgroup -s /bin/sh -D appuser
-
1001:一般ユーザに割り当てられるID。 -
-D:パスワードなし設定(システム自動実行用のため)。 -
-s /bin/sh:Alpine標準の軽量シェルを指定。
最終的なDockerfileの完成形
# ==========================================
# ステージ1: ビルド環境 (Builder)
# ==========================================
FROM maven:3.9-eclipse-temurin-21 AS builder
WORKDIR /app
# Maven Wrapperとpom.xmlをコピーし、依存関係をキャッシュ
COPY .mvn/ .mvn
COPY mvnw pom.xml ./
RUN --mount=type=cache,target=/root/.m2 \
./mvnw dependency:go-offline -B
# ソースコードをコピーしてビルド
COPY src ./src
RUN --mount=type=cache,target=/root/.m2 \
./mvnw clean package -DskipTests
# ==========================================
# ステージ2: 実行環境 (Runner)
# ==========================================
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
# セキュリティ対策: 非rootユーザーの作成
RUN addgroup -g 1001 appgroup && \
adduser -u 1001 -G appgroup -s /bin/sh -D appuser
# ビルドしたJARファイルをコピーし、所有権をappuserに変更
COPY --from=builder --chown=appuser:appgroup /app/target/*.jar app.jar
# 作成したユーザーに切り替え
USER appuser
EXPOSE 8080
# コンテナ起動時に上書き可能な環境変数のデフォルト値
ENV SPRING_PROFILES_ACTIVE=prod \
JAVA_OPTS="-XX:MaxRAMPercentage=75.0"
# アプリケーションの起動
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
Docker Composeの設定とコンテナ起動
作成したDockerfileを使ってコンテナを起動・管理しやすくするために、docker-compose.ymlを作成します。
services:
backend-app:
build:
context: . # カレントディレクトリのDockerfile等を使ってビルドする
image: my-spring-boot:1.0
container_name: my-spring-boot
ports:
- "8080:8080"
restart: unless-stopped
コンテナの起動と動作確認
設定ファイルがそろったら、ターミナルでプロジェクトルートに移動し、以下コマンドを実行する。
docker compose up -d --build
-
-d:バックグラウンドでコンテナを起動します。 -
--build:Dockerfileの変更を反映して、イメージをビルドします。
起動後、ブラウザでhttp://localhost:8080にアクセスします。アプリの画面が表示されたら、無事にDocker化が完了しました。
コンテナを停止・削除して作業を終了する時は、以下のコマンドを実行します。
docker compose down
以上です。何かしらの参考になれば幸いです。