【Spring Boot】Dockerでコンテナ化する基本

Java

こんにちは、かつコーチです。

「自分のPCでは動くのに、サーバーだとJavaのバージョンが違って動かない」という経験はないでしょうか。

筆者も過去、ローカルはJava 21なのに本番サーバーはJava 17のままで、起動時に原因不明のエラーに何時間も悩まされたことがあります。

Docker(アプリケーションと実行環境をまとめて「コンテナ」として配布・実行する技術)を使えば、実行環境の差異そのものをなくせます。

この記事では、Spring BootアプリケーションをDockerでコンテナ化する基本の手順を解説します。

Dockerでコンテナ化するとは?

コンテナとイメージの関係

イメージとは、アプリケーションの実行に必要なファイル一式(OS環境・Javaランタイム・jarファイルなど)をひとまとめにしたテンプレートです。

コンテナは、そのイメージを実際に起動した実体で、独立した軽量な実行環境として動作します。

同じイメージからは何度でも同じ状態のコンテナを起動できるため、「開発環境では動くのに本番では動かない」という環境差異の問題そのものが起こりにくくなります。

なぜSpring Bootアプリをコンテナ化するのか

Fat JARだけでもJavaさえあれば動く、と前回の記事で紹介しましたが、Javaのバージョン差異や、OSごとのライブラリの有無までは吸収できません。

Dockerイメージには、Javaランタイムそのものも含めてパッケージングするため、「サーバーにどのJavaが入っているか」を気にする必要がなくなります。

さらに、KubernetesやAWS ECSのようなコンテナオーケストレーション基盤を使う前提では、Docker化がほぼ必須の要件になります。

基本の書き方・実装手順

手順1:Dockerfileを作成する

プロジェクトのルートにDockerfileという名前のファイルを作成します。

# Dockerfile
FROM eclipse-temurin:17-jre-jammy

WORKDIR /app

COPY build/libs/blog-0.0.1-SNAPSHOT.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]

FROMでベースとなるイメージ(ここではJava 17のJRE入りイメージ)を指定し、COPYでビルド済みのjarファイルをコンテナ内にコピーします。

手順2:イメージをビルドする

Dockerfileがあるディレクトリで、以下のコマンドを実行してイメージをビルドします。

# 事前にjarファイルをビルドしておく
./gradlew bootJar

# Dockerイメージをビルド
docker build -t blog-app:1.0 .

-tでイメージに名前とタグを付けておくと、後で管理しやすくなります。

手順3:コンテナを起動する

ビルドしたイメージからコンテナを起動します。

docker run -d -p 8080:8080 --name blog-container blog-app:1.0

-p 8080:8080は「ホストの8080番ポートをコンテナの8080番ポートにつなぐ」という意味で、これがないとコンテナ内で起動しているアプリに外部からアクセスできません。

# 起動確認
docker ps
curl http://localhost:8080

つまずきやすい設定・注意点

コンテナに環境変数を渡す場合は、docker run-eオプションを付けます。

docker run -d -p 8080:8080 \
  -e SPRING_PROFILES_ACTIVE=prod \
  -e DB_PASSWORD=super-secret-password \
  blog-app:1.0

前回の記事で扱ったプロファイル切り替えと組み合わせることで、同じイメージを環境変数だけで使い分けられます。

よくあるつまずきポイント・エラー対処

❌ Before:ビルドとコンテナ化を分けずイメージが巨大化する

筆者が初めてDockerfileを書いたとき、Gradleのビルド処理までDockerfile内に含めてしまい、ビルドツールやソースコードまでイメージに残ってしまうという失敗をしました。

# 良くない例:ビルドと実行が同じステージに混在
FROM eclipse-temurin:17-jdk-jammy
WORKDIR /app
COPY . .
RUN ./gradlew bootJar
ENTRYPOINT ["java", "-jar", "build/libs/blog-0.0.1-SNAPSHOT.jar"]

このイメージはGradleキャッシュやソースコードまで含んでしまうため、サイズが1GBを超えることも珍しくなく、デプロイに時間がかかるようになります。

✅ After:マルチステージビルドでイメージを軽量化する

マルチステージビルド(ビルド用と実行用でステージを分け、最終イメージには実行に必要なものだけを残す手法)を使うと、この問題を解決できます。

# ビルドステージ
FROM eclipse-temurin:17-jdk-jammy AS builder
WORKDIR /app
COPY . .
RUN ./gradlew bootJar

# 実行ステージ
FROM eclipse-temurin:17-jre-jammy
WORKDIR /app
COPY --from=builder /app/build/libs/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]

最終的なイメージにはJREとjarファイルだけが残り、ソースコードやビルドツール一式は破棄されます。

同じ環境でビルドから実行までを検証したところ、イメージサイズが3分の1近くまで縮小しました。

応用・一歩先の使い方

docker-composeでDBとまとめて起動する

開発環境では、アプリとDBをまとめて起動できると便利です。

# docker-compose.yml
services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      SPRING_PROFILES_ACTIVE: dev
      DB_HOST: db
    depends_on:
      - db
  db:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: blog_dev
      MYSQL_ROOT_PASSWORD: root_pass
    ports:
      - "3306:3306"
docker compose up -d

depends_onでDBコンテナの起動を待ってからアプリを起動する順序を指定できますが、DBの起動完了とMySQLの接続受付開始にはタイムラグがあるため、アプリ側にリトライ処理を入れておくとより安定します。

.dockerignoreでビルドコンテキストを絞る

.gitignoreと同様に、.dockerignoreを用意しておくと、不要なファイルをビルドコンテキストから除外でき、ビルド速度の向上にもつながります。

# .dockerignore
build/
.gradle/
.git/
*.md

まとめ

この記事のポイント

  • Dockerはアプリと実行環境をまとめてコンテナとして配布・実行できる技術
  • Dockerfileでベースイメージ・jarのコピー・起動コマンドを定義する
  • docker buildでイメージを作り、docker run -pでポートを公開して起動する
  • マルチステージビルドで、ビルドツールやソースコードを含まない軽量なイメージにする
  • docker-composeを使うとアプリとDBをまとめて開発環境として起動できる

次に読むべき記事

デプロイ・インフラ連携編はここまでです。設計面をより深く学びたい方は「レイヤードアーキテクチャの考え方」もご覧ください。

タグ: Spring Boot, 中級者向け, デプロイ

タイトルとURLをコピーしました