Spring Bootアプリをいざコンテナ化しようとすると、「Dockerfileの書き方がよくわからない」「Docker ComposeでPostgreSQLと繋がらない」という壁に当たりますよね。

この記事では、動く Dockerfile の作り方から、マルチステージビルドでのイメージ最適化、Docker ComposeでのDB連携まで、実践的なポイントを解説します。


最小構成のDockerfileを書く

まずは動く Dockerfile を最短距離で作りましょう。Spring Boot アプリは JAR ファイルを実行するだけなので、Dockerfile はとてもシンプルです。

ベースイメージには eclipse-temurin:21-jre-alpine を使います。ランタイムではビルドツールが不要なので、JDK ではなく JRE を選ぶとイメージサイズを抑えられます。

FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY target/app.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

ビルドと起動は次のコマンドで確認できます。

./mvnw package -DskipTests
docker build -t myapp:latest .
docker run -p 8080:8080 myapp:latest

マルチステージビルドでイメージサイズを削減する

上記の Dockerfile では事前に mvn package を実行する必要がありますが、マルチステージビルドを使えば Dockerfile 内でビルドから実行まで完結できます。

Maven や JDK を含んだイメージをそのまま本番イメージにすると 500MB 以上になってしまいますが、ビルドステージとランタイムステージを分離すれば 80MB 程度に抑えられます。

# ビルドステージ
FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /workspace

# pom.xml だけを先にコピーして依存関係を解決(キャッシュ最適化)
COPY pom.xml .
RUN mvn dependency:go-offline -B

COPY src ./src
RUN mvn package -DskipTests -B

# ランタイムステージ
FROM eclipse-temurin:21-jre-alpine AS runtime
WORKDIR /app
COPY --from=build /workspace/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

COPY --from=build がポイントです。ビルドステージの JAR だけをランタイムイメージにコピーし、Maven や JDK は最終イメージに含まれません。

また、pom.xml を先にコピーして依存関係を解決することで、コード変更時にも依存ライブラリのダウンロードをスキップできます。


Layered JAR でさらに高速化する

Spring Boot 3.x では Layered JAR という仕組みが使えます。JAR の中身を dependencies・spring-boot-loader・application の3層に分割してコピーすることで、アプリコードだけが変わった場合でも依存ライブラリのレイヤーをキャッシュから再利用できます。

FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /workspace
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn package -DskipTests -B
RUN java -Djarmode=layertools -jar target/*.jar extract --destination target/extracted

FROM eclipse-temurin:21-jre-alpine AS runtime
WORKDIR /app
COPY --from=build /workspace/target/extracted/dependencies/ ./
COPY --from=build /workspace/target/extracted/spring-boot-loader/ ./
COPY --from=build /workspace/target/extracted/snapshot-dependencies/ ./
COPY --from=build /workspace/target/extracted/application/ ./
EXPOSE 8080
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]

Docker ComposeでSpring Boot + PostgreSQLを連携起動する

ローカル開発でアプリとDBを別々に起動するのは手間がかかりますよね。Docker Compose を使えば、1コマンドで複数サービスをまとめて起動できます。

services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: myapp_db
      POSTGRES_USER: myapp_user
      POSTGRES_PASSWORD: myapp_password
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U myapp_user -d myapp_db"]
      interval: 10s
      timeout: 5s
      retries: 5

  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/myapp_db
      SPRING_DATASOURCE_USERNAME: myapp_user
      SPRING_DATASOURCE_PASSWORD: myapp_password
    depends_on:
      db:
        condition: service_healthy

volumes:
  postgres_data:

depends_on だけでは PostgreSQL コンテナが起動した時点で次のサービスが開始されてしまいますが、healthcheckcondition: service_healthy を組み合わせることで PostgreSQL が接続を受け付けられる状態になってから Spring Boot が起動します。

起動は次のコマンドで行います。

docker compose up -d
docker compose logs -f app
docker compose down

環境変数で設定を外出しする

コンテナ環境では DB 接続情報などをコードにハードコードせず、環境変数で注入するのがベストプラクティスです。

spring.datasource.url=${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/myapp_db}
spring.datasource.username=${SPRING_DATASOURCE_USERNAME:myapp_user}
spring.datasource.password=${SPRING_DATASOURCE_PASSWORD:myapp_password}

${変数名:デフォルト値} の形式を使うと、環境変数が設定されていない場合のフォールバック値を指定できます。

機密情報は .env ファイルに分離して Git にコミットしないようにしましょう。Docker Compose は同じディレクトリの .env ファイルを自動で読み込みます。

プロファイルとの組み合わせについては Spring BootのProfileを使って環境によって違う設定を安全に切り替える方法 も参照してください。


本番移行前のセキュリティチェック

ローカルで動かせるようになったら、本番環境への移行前にセキュリティのポイントを確認しましょう。

非 root ユーザーでの実行を推奨します。デフォルトでは Docker コンテナ内のプロセスは root で実行されるため、専用ユーザーを作成して切り替えることでリスクを低減できます。

FROM eclipse-temurin:21-jre-alpine AS runtime
WORKDIR /app
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
COPY --chown=appuser:appgroup --from=build /workspace/target/*.jar app.jar
USER appuser
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

また、本番環境では環境変数ではなく Docker Secrets や AWS Secrets Manager などのシークレット管理サービスの利用を検討してください。

イメージの脆弱性スキャンも習慣づけましょう。

docker scout cves myapp:latest

まとめ

この記事では、Spring Boot アプリの Docker コンテナ化について解説しました。

まずは最小構成の Dockerfile で動かし、マルチステージビルドでイメージサイズを削減、さらに Layered JAR でビルドを高速化する流れを押さえておきましょう。

Docker Compose を使えばローカル開発がぐっと楽になりますし、本番移行時も非 root ユーザー実行や脆弱性スキャンなどのセキュリティチェックを忘れずに行いたいですね。

コンテナ化はクラウドデプロイや CI/CD パイプラインへの入口でもあります。まずはローカルで Docker Compose 環境を動かして、CI でのイメージビルドへとステップアップしていきましょう。