Spring Boot アプリケーションは、Dockerfiles を使用してコンテナー化するか、Cloud Native Buildpacks を使用して、どこでも実行できる最適化された docker 互換コンテナーイメージを作成できます。

1. 効率的なコンテナーイメージ

Spring Boot uber jar を docker イメージとしてパッケージ化することは簡単に可能です。ただし、docker イメージにある uber jar をそのままコピーして実行すると、さまざまな欠点があります。uber jar を解凍せずに実行すると、常に一定量のオーバーヘッドが発生します。コンテナー化された環境では、これが顕著になる可能性があります。もう 1 つの課題は、アプリケーションのコードとそのすべての依存関係を Docker イメージの 1 つのレイヤーに配置することが最適ではないことです。おそらく、使用している Spring Boot のバージョンをアップグレードするよりもコードを再コンパイルする頻度が高いため、多くの場合、物事をもう少し分離した方がよいでしょう。アプリケーションクラスの前のレイヤーに jar ファイルを配置すると、Docker は多くの場合、最下位レイヤーを変更するだけで済み、他のレイヤーをキャッシュから取得できます。

1.1. Docker イメージのレイヤー化

最適化された Docker イメージの作成を容易にするために、Spring Boot はレイヤーインデックスファイルを jar に追加することをサポートしています。レイヤーのリストと、レイヤー内に含める必要のある jar のパーツを提供します。インデックス内のレイヤーのリストは、レイヤーを Docker/OCI イメージに追加する順序に基づいて並べられています。すぐに使用できる、次のレイヤーがサポートされています。

  • dependencies (定期的にリリースされる依存関係の場合)

  • spring-boot-loader (org/springframework/boot/loader のすべての)

  • snapshot-dependencies (スナップショットの依存関係)

  • application (アプリケーションのクラスとリソース)

layers.idx ファイルの例を次に示します。

- "dependencies":
  - BOOT-INF/lib/library1.jar
  - BOOT-INF/lib/library2.jar
- "spring-boot-loader":
  - org/springframework/boot/loader/launch/JarLauncher.class
  - ... <other classes>
- "snapshot-dependencies":
  - BOOT-INF/lib/library3-SNAPSHOT.jar
- "application":
  - META-INF/MANIFEST.MF
  - BOOT-INF/classes/a/b/C.class

この階層化は、アプリケーションのビルド間で変更される可能性に基づいてコードを分離するように設計されています。ライブラリコードはビルド間で変更される可能性が低いため、ツールがキャッシュからレイヤーを再利用できるように独自のレイヤーに配置されます。アプリケーションコードはビルド間で変更される可能性が高いため、別のレイヤーに分離されます。

Spring Boot は、layers.idx を使用して war ファイルのレイヤー化もサポートします。

Maven の場合、アーカイブにレイヤーインデックスを追加する方法の詳細については、階層化された jar または war のパッケージ化のセクションを参照してください。 Gradle については、Gradle プラグインのドキュメントのパッケージ化階層化された jar または war セクションを参照してください。

2. Dockerfiles

Dockerfile ではわずか数行で Spring Boot uber jar を docker イメージに変換できますが、ここではレイヤー機能を使用して最適化された docker イメージを作成します。レイヤーインデックスファイルを含む jar を作成すると、spring-boot-jarmode-layertools jar が依存関係として jar に追加されます。クラスパス上にこの jar があると、特別なモードでアプリケーションを起動できます。これにより、ブートストラップコードがアプリケーションとはまったく異なるもの (たとえば、レイヤーを抽出するもの) を実行できるようになります。

layertools モードは、起動スクリプトを含む完全に実行可能な Spring Boot アーカイブでは使用できません。layertools で使用することを目的とした jar ファイルをビルドするときは、起動スクリプト構成を無効にします。

layertools jar モードで jar を起動する方法は次のとおりです。

$ java -Djarmode=layertools -jar my-app.jar

これにより、次の出力が提供されます。

Usage:
  java -Djarmode=layertools -jar my-app.jar

Available commands:
  list     List layers from the jar that can be extracted
  extract  Extracts layers from the jar for image creation
  help     Help about any command

extract コマンドを使用すると、アプリケーションをレイヤーに簡単に分割して、dockerfile に追加できます。これは、jarmode を使用した Dockerfile の例です。

FROM eclipse-temurin:17-jre as builder
WORKDIR application
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar
RUN java -Djarmode=layertools -jar application.jar extract

FROM eclipse-temurin:17-jre
WORKDIR application
COPY --from=builder application/dependencies/ ./
COPY --from=builder application/spring-boot-loader/ ./
COPY --from=builder application/snapshot-dependencies/ ./
COPY --from=builder application/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]

上記の Dockerfile が現在のディレクトリにあると仮定すると、docker build . を使用して docker イメージを構築できます。または、次の例に示すように、アプリケーション jar へのパスをオプションで指定できます。

$ docker build --build-arg JAR_FILE=path/to/myapp.jar .

これはマルチステージの dockerfile です。ビルダーステージは、後で必要になるディレクトリを抽出します。各 COPY コマンドは、jarmode によって抽出されたレイヤーに関連しています。

もちろん、Dockerfile は jarmode を使用せずに作成できます。unzip と mv の組み合わせを使用して、適切なレイヤーに移動できますが、jarmode はそれを単純化します。

3. Cloud Native Buildpacks

Dockerfile は、docker イメージを構築するための 1 つの方法にすぎません。docker イメージを作成する別の方法は、buildpacks を使用して、Maven または Gradle プラグインから直接行うことです。Cloud Foundry や Heroku などのアプリケーションプラットフォームを使用したことがある場合は、おそらく buildpack を使用したことがあります。Buildpacks は、アプリケーションを取得して、プラットフォームが実際に実行できるものに変換するプラットフォームの一部です。例: Cloud Foundry の Java buildpack は、.jar ファイルをプッシュしていることを認識し、関連する JRE を自動的に追加します。

Cloud Native Buildpacks を使用すると、どこでも実行できる Docker 互換のイメージを作成できます。Spring Boot には、Maven と Gradle の両方を直接サポートする buildpack が含まれています。つまり、1 つのコマンドを入力するだけで、ローカルで実行されている Docker デーモンに適切なイメージをすばやく取得できます。

Maven および Gradle で buildpacks を使用する方法については、個々のプラグインのドキュメントを参照してください。

Paketo Spring Boot buildpack [GitHub] (英語) は layers.idx ファイルをサポートしているため、それに適用されるカスタマイズは buildpack によって作成されるイメージに反映されます。
再現可能なビルドとコンテナーイメージのキャッシュを実現するために、Buildpacks はアプリケーションリソースのメタデータ (ファイルの「最終変更」情報など) を操作できます。アプリケーションが実行時にそのメタデータに依存しないようにする必要があります。Spring Boot は静的リソースを提供するときにその情報を使用できますが、これは spring.web.resources.cache.use-last-modified で無効にすることができます。

4. 次のステップ

効率的なコンテナーイメージを構築する方法を学習したら、Kubernetes などのクラウドプラットフォームへのアプリケーションのデプロイについて読むことができます。