【初心者向け】爆速&安全!Spring Bootアプリを Docker コンテナ 最適化! マルチステージビルド と Jib 徹底解説

こんな課題、ありませんか?

「Dockerイメージが大きすぎて、デプロイに時間がかかり、CI/CDパイプラインが遅延している」
「本番環境でコンテナがrootユーザーで動いていて、セキュリティが心配」
「Dockerfileの書き方が複雑で、もっと簡単に最適化したい」

現代のクラウドネイティブ(※1)開発において、アプリケーションのコンテナ化は重要な要素です。

Spring Bootのような高機能なフレームワークを使用している場合、Dockerイメージが肥大化し、デプロイのボトルネックやセキュリティリスクにつながることがあります。

※1 クラウドネイティブ (Cloud Native): クラウド環境のメリットを最大限に活用するために、アプリケーションを設計・開発・運用するアプローチ。コンテナ、マイクロサービス、CI/CDなどを特徴とします。

この記事で得られること

本記事では、Spring Boot 3.x系アプリケーションをDockerコンテナとして効率的にビルドし、イメージサイズとセキュリティを最適化するための手法を、具体的なコード例を交えながら解説します。

最新のSpring Bootの特性を活かし、Jibのようなツールを活用することで、開発ワークフローの改善が期待できます。

この記事を読めば、あなたのSpring Bootアプリは「爆速&安全」なコンテナとして生まれ変わるでしょう。


目次


対象読者

  • Spring BootアプリケーションのDockerコンテナ化に興味がある開発者
  • Dockerイメージのサイズ削減やセキュリティ向上を目指している方
  • Jibを使った効率的なコンテナイメージビルドに挑戦したい方
  • コンテナ化されたSpring Bootアプリケーションの運用・監視について学びたい方

動作検証環境

この記事は、以下の環境で検証しています。

  • OS : macOS Tahoe Version 26.1
  • ハードウェア : MacBook Air 2024 M3 24GB
  • VS Code: 最新版 (記事執筆時点)
  • Java Extension Pack: 最新版 (記事執筆時点)
  • Spring Boot Extension Pack: 最新版 (記事執筆時点)
  • Java: OpenJDK 25.0.1 LTS (Temurin)
  • Spring Boot: 3.5.7
  • Maven: 3.9.6 (Spring Initializrで生成されるデフォルトバージョンを想定)
  • Docker Desktop:4.50

なぜSpring Bootアプリのコンテナ最適化が重要なのか?

DockerとSpring Bootの強力な相性

Spring Bootは、その「設定より規約」の思想と豊富な自動構成機能により、素早く堅牢なアプリケーションを開発できる強力なフレームワークです。

一方、Dockerはアプリケーションとその実行環境をまとめてパッケージ化し、どこでも同じように動作させることを可能にするコンテナ技術のデファクトスタンダード(※2)です。

※2 デファクトスタンダード (De Facto Standard): 公式な標準ではないが、事実上広く使われている標準のこと。

図1: DockerとSpring Bootの連携概念図

DockerとSpring Bootの連携概念

この二つの技術は、相性が良いと言えます。

コンテナ化されたアプリケーションの設計原則として、グローバルスタンダードである「Twelve-Factor App」の考え方を取り入れることで、より堅牢でスケーラブルなシステムを構築できます。

以下に、Twelve-Factor Appを参考に相性が良い部分をいくつか抜粋します。

  • 環境の統一(開発/本番一致):
    • 開発、テスト、本番環境で全く同じ実行環境を提供できるため、「私の環境では動くのに…」といった問題を解消します。
  • スケーラビリティ(並行性):
    • 必要に応じてアプリケーションのインスタンスを簡単に増減でき、クラウド環境での運用に最適です。
  • ポータビリティ(コードベース):
    • どのクラウドプロバイダーやオンプレミス環境でも、同じコンテナイメージをデプロイできます。
  • 依存関係の管理(依存関係):
    • アプリケーションが必要とするライブラリやランタイムをコンテナ内に閉じ込めることで、ホスト環境への影響を最小限に抑えます。

最適化しないとどうなる?(イメージサイズ、デプロイ時間、セキュリティリスク)

コンテナの恩恵を十分に得るためには、単にコンテナ化するだけでなく、イメージの最適化が重要です。

最適化を怠ると、以下のような問題が発生する可能性があります。

  • イメージサイズの肥大化:
    • 不要なファイルやツールがイメージに含まれることで、イメージサイズが大きくなり、レジストリへのプッシュやプルに時間がかかります。
    • これはデプロイ時間の増加やストレージコストの増大につながります。
  • デプロイ時間の増加:
    • イメージサイズが大きいと、CI/CDパイプラインでのビルドやデプロイに時間がかかり、開発効率が落ち、緊急時の対応も遅れてしまいます。
  • セキュリティリスクの増大:
    • 不要なソフトウェアやライブラリがあると脆弱性のリスクが増え、rootユーザーで実行すると、もしコンテナが乗っ取られた場合にシステム全体に大きな被害が及ぶ可能性があります。
  • リソース消費の増加:
    • 最適化されていないコンテナは、不要なプロセスやファイルがコンテナ内に存在することで、必要以上にメモリやCPUを消費し、クラウド利用料金の増加や、限られたリソースでのアプリケーション稼働数減少につながります。

基本的なDockerfileから学ぶ最適化の第一歩

Spring BootアプリケーションをDockerコンテナにする最も基本的な方法は、Fat JAR(実行可能な単一のJARファイル)をコンテナイメージに含めることです。

このセクションでは、まず基本的なDockerfileの作成方法を学び、その後に潜む課題を理解することで、なぜ最適化が必要なのかを明確にします。


Fat JARのコンテナ化:シンプルだけど課題も

まずは、シンプルなSpring Bootアプリケーションをビルドし、そのFat JARをDockerイメージに含めるDockerfileを見てみましょう。

# ベースイメージとしてOpenJDKのJREを使用
FROM eclipse-temurin:25-jre-alpine-3.22

# アプリケーションのJARファイルをコンテナ内にコピー
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar

# アプリケーションを実行
ENTRYPOINT ["java","-jar","/app.jar"]

このDockerfileは非常にシンプルですが、いくつかの課題があります。例えるなら、不要な荷物でパンパンになったスーツケースのように、イメージサイズが大きくなりがちです。

  • イメージサイズ:
    • eclipse-temurin:25-jre-alpine-3.22 は軽量版とはいえ、それでも数百MBのサイズがあります。さらに、アプリケーションのJARファイル自体も依存ライブラリを全て含んでいるため、大きくなりがちです。
  • ビルド時間:
    • アプリケーションのコード変更があるたびに、全ての依存関係を含んだFat JARを再ビルドし、それをイメージにコピーするため、ビルド時間が長くなる傾向があります。
  • セキュリティ:
    • デフォルトではrootユーザーで実行されるため、セキュリティリスクが高まります。

これらの課題を解決するために、次のセクションで最適化テクニックを見ていきましょう。


ENTRYPOINTCMD:コンテナ起動の仕組みを理解する

Dockerfileでコンテナが起動する際に実行されるコマンドを指定する方法として、ENTRYPOINTCMDがあります。

  • ENTRYPOINT:
    • コンテナが実行される際のメインコマンドを指定します。通常、アプリケーションの実行ファイルなどを指定し、コンテナの「実行形式」を定義します。
  • CMD:
    • ENTRYPOINTで指定されたコマンドの引数、またはENTRYPOINTが指定されていない場合のデフォルトコマンドを指定します。

上記の例では、ENTRYPOINT ["java","-jar","/app.jar"] とすることで、コンテナが起動すると java -jar /app.jar が実行され、Spring Bootアプリケーションが起動します。


Dockerイメージの軽量化とセキュリティ強化テクニック

ここからは、Spring BootアプリケーションのDockerイメージをより効率的かつ安全にするためのテクニックを詳しく見ていきます。

これらのテクニックを適用することで、イメージサイズの削減、ビルド時間の短縮、そしてセキュリティの向上を実現できます。


マルチステージビルドでイメージサイズを劇的に削減

マルチステージビルドは、Dockerイメージのサイズを大幅に削減するための効果的な手法です。

例えるなら、料理の準備(下ごしらえ)と盛り付けを完全に分けるようなものです。

ビルドプロセスを複数のステージに分け、最終的な実行イメージには必要なものだけを含めることで、中間ビルドの成果物や開発ツールなどを排除できます。

Spring Bootアプリケーションの場合、通常は以下の2つのステージに分けます。

  1. ビルドステージ: ソースコードからFat JARをビルドします。このステージではJDKやMaven/Gradleなどのビルドツールが必要です。
  2. 実行ステージ: ビルドされたFat JARとJREのみを含んだ軽量なベースイメージを使用し、アプリケーションを実行します。

図2: マルチステージビルドの概念図

マルチステージビルドの概念

このDockerfileでは、buildステージでJDKとMavenを使ってアプリケーションをビルドし、packageコマンドでFat JARを生成しています。

その後の実行ステージでは、openjdk:25-jre-slimというより軽量なJREのみのイメージをベースとし、COPY --from=build を使ってビルドステージで生成されたJARファイルだけをコピーしています。

# --- ビルドステージ ---
FROM eclipse-temurin:25-jdk-alpine-3.22 AS build
WORKDIR /app
COPY . .
RUN ./mvnw clean package -DskipTests

# --- 実行ステージ ---
FROM eclipse-temurin:25-jre-alpine-3.22
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

これにより、最終イメージには不要なビルドツールなどが含まれず、大幅なサイズ削減が期待できます。


軽量なベースイメージの選び方:Alpine, Noble

ここでは、軽量化されたAlpine、そして最新のUbuntu LTSをベースとするNobleの2つのイメージについて、それぞれの特徴をJava 25の視点から解説します。

【重要】公式openjdkイメージの非推奨化について
公式のopenjdk Dockerイメージは2022年7月以降非推奨となっており、JREイメージの提供も終了しています。現在は、Eclipse TemurinやAmazon Correttoなどのディストリビューションが推奨されています。本記事では、推奨されるEclipse Temurinイメージを例に解説を進めます。

Alpine

  • 特徴: Alpine Linuxをベースとした非常にコンパクトなイメージです。glibcの代わりにmusl libcを使用している点が最大の特徴で、ベースイメージのサイズはわずか数MBに抑えられています。
  • 利点: 最小限のイメージサイズは、デプロイ時間の短縮、起動速度の向上、そして攻撃対象領域の縮小によるセキュリティリスクの低減に大きく貢献します。jlinkツールと組み合わせることで、アプリケーションに必要なJavaモジュールのみを含むカスタムJREを作成し、さらにイメージサイズを最適化できます。
  • 考慮事項: musl libcの使用により、一部のJavaアプリケーションやネイティブライブラリで互換性の問題が発生する可能性があります。そのため、導入前にはアプリケーションとその依存関係について徹底的なテストが不可欠です。
  • 関連イメージ: Eclipse Temurin Docker Hub (例: eclipse-temurin:25-jre-alpineのようなタグ)

Noble (Ubuntuベース)

  • 特徴: 「Noble」はUbuntu Noble Numbat (24.04 LTS)を指すことが多く、最新のUbuntu LTSリリースをベースとしたイメージです。paketobuildpacks/builder-noble-java-tinyのようなビルドパックと連携するイメージとして提供されることもあります。
  • 利点: 最新のUbuntu環境を基盤とするため、良好な互換性と最新のパッケージが利用可能です。特に、ビルドパックを利用した自動化されたコンテナイメージ構築ワークフローを採用している場合に、その恩恵を最大限に受けられます。
  • 考慮事項: Eclipse TemurinJRE-slimJRE-alpineと比較すると、イメージサイズは大きくなる傾向があります。最新の環境とビルドパックとの統合を重視する場合に検討すべき選択肢です。
  • 関連イメージ: Eclipse Temurin Docker Hub (例: eclipse-temurin:25-jre-nobleのようなタグ)

これらの情報を参考に、あなたのSpring Bootアプリケーションに最適なベースイメージを選択し、効率的でセキュアなコンテナイメージを構築してください。


非rootユーザー実行でセキュリティを盤石に

Dockerコンテナはデフォルトでrootユーザーとして実行されますが、これはセキュリティ上のリスクとなります。

例えるなら、常に最高権限の鍵を持ったまま作業するようなものです。コンテナ内でroot権限が必要な操作は最小限に抑え、可能な限り非rootユーザーで実行するように設定すべきです。

図3: 非rootユーザーとrootユーザー実行権限比較イメージ

非rootユーザーとrootユーザー実行権限比較

RUN adduser --system springboot でシステムユーザー springboot を作成し、USER springboot でそのユーザーに切り替えています。

# --- ビルドステージ ---
FROM eclipse-temurin:25-jdk-alpine-3.22 AS build
WORKDIR /app
COPY . .
RUN ./mvnw clean package -DskipTests

# --- 実行ステージ ---
FROM eclipse-temurin:25-jre-alpine-3.22
WORKDIR /app
# アプリケーション実行用のユーザーを作成
RUN addgroup --system springboot && adduser --system --ingroup springboot springboot
USER springboot
COPY --from=build /app/target/*.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

これにより、アプリケーションはroot権限を持たずに実行され、コンテナが侵害された際のリスクを低減できます。


Dockerfile不要!Jibを使ったコンテナイメージビルドでさらに効率化

手動でDockerfileを記述することも可能ですが、Spring Bootアプリケーションのコンテナ化にはGoogleが開発したJibが有用なツールです。

JibはMavenまたはGradleのプラグインとして動作し、Dockerfileを書かずにコンテナイメージをビルドできます。

このセクションでは、Jibの導入とメリット、そしてMaven/Gradleとの連携方法について解説します。


Jibとは?導入と驚きのメリット

Jibの主なメリットは以下の通りです。例えるなら、自動で最適なコンテナを作ってくれる熟練の職人のようなものです。

  • Dockerfile不要:
    • Dockerfileの記述やメンテナンスが不要になります。
  • 高速ビルド:
    • レイヤリングを最適化し、変更があったレイヤーのみを再ビルドするため、非常に高速です。
    • 特に依存関係の変更が少ない場合、アプリケーションコードの変更のみであれば迅速にビルドが完了します。
  • 再現性:
    • ビルド環境に依存せず、常に同じイメージを生成します。
  • セキュリティ:
    • デフォルトで非rootユーザーでの実行や、軽量なベースイメージの選択など、セキュリティベストプラクティスが適用されます。

図4: Jibによるイメージレイヤリングの仕組み

Jibによるイメージレイヤリングの仕組み

Jibはアプリケーションを構成する要素(ベースイメージ、依存ライブラリ、リソース、クラスファイルなど)を個別のレイヤーとして管理します。これにより、変更があったレイヤーのみを再ビルド・プッシュするため、高速なビルドと効率的なイメージ管理を実現します。


Mavenとの連携:設定は簡単、ビルドは高速

JibをMavenプロジェクトに導入するには、pom.xmlにJibプラグインを追加するだけです。

ビルドされたイメージはローカルのDockerデーモンに直接保存する例:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <mainClass>com.example.demo.DemoApplication</mainClass>
            </configuration>
        </plugin>
        <plugin>
            <groupId>com.google.cloud.tools</groupId>
            <artifactId>jib-maven-plugin</artifactId>
            <version>3.4.3</version>
            <dependencies>
                <!-- Java 25のクラスファイルを処理するためにASMのバージョンを更新します -->
                <dependency>
                    <groupId>org.ow2.asm</groupId>
                    <artifactId>asm</artifactId>
                    <version>9.8</version>
                </dependency>
                <dependency>
                    <groupId>org.ow2.asm</groupId>
                    <artifactId>asm-commons</artifactId>
                    <version>9.8</version>
                </dependency>
            </dependencies>
            <configuration>
                <from>
                    <image>eclipse-temurin:25-jre-alpine</image>
                    <platforms>
                        <platform>
                            <os>linux</os>
                            <architecture>arm64</architecture>
                        </platform>
                    </platforms>
                </from>
                <to>
                    <image>spring-web-sample-jib</image>
                    <tags>
                        <tag>latest</tag>
                    </tags>
                </to>
                <container>
                    <!-- セキュリティ向上のため、非rootユーザーで実行します -->
                    <user>1001:1001</user>
                    <ports>
                        <port>8080</port>
                    </ports>
                </container>
            </configuration>
        </plugin>
    </plugins>
</build>

ビルドは、Mavenであれば ./mvnw compile jib:build コマンドを実行するだけです。

以下のコマンドを実行すると、Jibが pom.xml の設定に基づいてアプリケーションのコンテナイメージをビルドします。
このプロセスでは Dockerfile は使用されず、ビルドされたイメージはローカルのDockerデーモンに直接保存されます。

./mvnw compile jib:dockerBuild
  • イメージ名: spring-web-sample-jib:latest

標準的な docker run コマンドでコンテナを起動します。

docker run -p 8080:8080 spring-web-sample-jib:latest

ヘルスチェックと監視:コンテナ化アプリの安定運用に不可欠

コンテナ化されたアプリケーションを安定して運用するためには、その状態を適切に監視し、異常を検知する仕組みが重要です。

DockerやKubernetesなどのコンテナオーケストレーションツールは、コンテナのヘルスチェック機能を提供しており、これとSpring Boot Actuatorを連携させることで、アプリケーションの健全性を判断できます。

このセクションでは、Actuatorの導入とDockerのHEALTHCHECK命令の活用方法を解説します。


Spring Boot Actuatorの導入:アプリの健全性を可視化

Spring Boot Actuatorは、アプリケーションの稼働状況を監視するためのエンドポイントを提供します。pom.xmlに以下の依存関係を追加するだけで簡単に導入できます。

<!-- Maven -->
    <!-- Spring Boot Actuator: アプリケーションの監視・管理機能を提供します。 -->
    <!-- /actuator/health, /actuator/metrics などのエンドポイントが利用可能になります。 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

application.propertiesまたはapplication.ymlで、ヘルスチェックエンドポイントを公開するように設定します。

# ===============================================================
# ACTUATOR CONFIGURATION (Actuator設定)
# ===============================================================
# 公開するActuatorエンドポイントを指定します。
# "*" はすべてのエンドポイントを公開することを意味します。
# 本番環境では、セキュリティ上の理由から公開するエンドポイントを限定することが推奨されます。
# (例: management.endpoints.web.exposure.include=health,info)
management.endpoints.web.exposure.include=*
# /actuator/health エンドポイントで詳細情報(DB接続状態など)を表示するかどうかを設定します。
# "always" は常に詳細を表示します。
management.endpoint.health.show-details=always

これにより、/actuator/health エンドポイントでアプリケーションのヘルスステータスを確認できるようになります。

アプリケーションが起動したら、ブラウザや curl コマンドで以下のエンドポイントにアクセスし、Actuatorが有効になっていることを確認できます。

  • 利用可能な全エンドポイントの一覧:
curl http://localhost:8080/actuator
  • ヘルスチェック:
    データベースの接続状態などの詳細情報も表示されます。
curl http://localhost:8080/actuator/health
  • メトリクス情報:
curl http://localhost:8080/actuator/metrics
  • 特定のメトリクス(例: http.server.requests):
curl http://localhost:8080/actuator/metrics/http.server.requests

DockerのHEALTHCHECK命令:自動復旧の仕組みを構築

DockerfileのHEALTHCHECK命令を使って、コンテナが正常に動作しているかをDockerエンジンに定期的にチェックさせることができます。

図5: Docker HEALTHCHECKのフロー

Docker HEALTHCHECKのフロー

この例では、30秒ごとに/actuator/healthエンドポイントにcurlでアクセスし、HTTPステータスコードが200番台でなければヘルスチェック失敗と判断します。

これにより、アプリケーションがフリーズしたり、応答しなくなったりした場合に、DockerやKubernetesが自動的にコンテナを再起動するなどの対応を取れるようになります。


まとめ:今日から実践!最適化されたコンテナで開発を加速しよう!

本記事では、Spring Boot 3.x系アプリケーションをDockerコンテナとして最適化するための様々なテクニックを解説しました。

  • マルチステージビルドによりイメージサイズを削減し、ビルド時間を短縮。
  • 軽量なベースイメージを選択し、フットプリントを最小化。
  • 非rootユーザーで実行することで、セキュリティを強化。
  • Jibを活用し、Dockerfileなしで効率的かつ高速にイメージをビルド。
  • Spring Boot ActuatorDocker HEALTHCHECKを連携させ、アプリケーションの健全性を監視。

これらのベストプラクティスを開発ワークフローに取り入れることで、より高速で、より安全で、より効率的なSpring Bootアプリケーションのコンテナ開発が実現できるでしょう。

最適化されたコンテナを活用し、開発をさらに加速させましょう。

この記事が役に立ったら、ぜひSNSでシェアしてください!
また、記事に関するご意見やご質問があれば、ぜひコメント欄にお寄せください。編集部が丁寧にお答えします!


免責事項

本記事は、公開時点での情報に基づき執筆されています。技術情報は常に変化するため、内容の正確性、完全性、有用性を保証するものではありません。本記事の内容を実践する際は、ご自身の責任において十分な検証を行ってください。本記事の利用によって生じたいかなる損害についても、筆者および公開元は一切の責任を負いません。


SNSでもご購読できます。

コメントを残す

*