SpringBootでのDockerレイヤーの再利用
1. 序章
Dockerは、自己完結型アプリケーションを作成するための事実上の標準です。 バージョン2.3.0以降、 Spring Boot には、効率的なDockerイメージの作成に役立ついくつかの拡張機能が含まれています。 したがって、は、アプリケーションを異なるレイヤーに分解することを可能にします。
つまり、ソースコードは独自のレイヤーに存在します。 したがって、独立して再構築でき、効率と起動時間が向上します。 このチュートリアルでは、Spring Bootの新機能を活用してDockerレイヤーを再利用する方法を説明します。
2. Dockerのレイヤードジャー
Dockerコンテナーは、ベースイメージと追加のレイヤーで構成されます。 レイヤーが構築されると、それらはキャッシュされたままになります。 したがって、後続の世代ははるかに高速になります。
下位レベルのレイヤーを変更すると、上位レベルのレイヤーも再構築されます。 したがって、頻繁に変更されないレイヤーは下部に残し、頻繁に変更されるレイヤーは上部に配置する必要があります。
同様に、Spring Bootを使用すると、アーティファクトのコンテンツをレイヤーにマッピングできます。 レイヤーのデフォルトのマッピングを見てみましょう。
ご覧のとおり、アプリケーションには独自のレイヤーがあります。 ソースコードを変更すると、独立したレイヤーのみが再構築されます。
3. SpringBootを使用した効率的なDockerイメージの作成
Dockerイメージを構築する従来の方法では、SpringBootはfatjarアプローチを使用します。 その結果、単一のアーティファクトにすべての依存関係とアプリケーションのソースコードが埋め込まれます。 したがって、ソースコードを変更すると、レイヤー全体が再構築されます。
3.1. SpringBootを使用したレイヤー構成
Spring Bootバージョン2.3.0では、Dockerイメージの生成を改善するための2つの新機能が導入されています。
- ビルドパックのサポートはアプリケーションのJavaランタイムを提供するため、DockerfileをスキップしてDockerイメージを自動的にビルドできるようになりました
- レイヤードjarは、Dockerレイヤー生成を最大限に活用するのに役立ちます
このチュートリアルでは、階層化されたjarアプローチを拡張します。
最初に、Mavenでレイヤードjarをセットアップします。 アーティファクトをパッケージ化するときに、レイヤーを生成します。 jarファイルを調べてみましょう。
jar tf target/spring-boot-docker-0.0.1-SNAPSHOT.jar
ご覧のとおり、fatjar内のBOOT-INFフォルダーに新しいlayers.idxファイルが作成されます。 確かに、依存関係、リソース、およびアプリケーションのソースコードを独立したレイヤーにマップします。
BOOT-INF/layers.idx
同様に、ファイルの内容は、保存されているさまざまなレイヤーを分類します。
- "dependencies":
- "BOOT-INF/lib/"
- "spring-boot-loader":
- "org/"
- "snapshot-dependencies":
- "application":
- "BOOT-INF/classes/"
- "BOOT-INF/classpath.idx"
- "BOOT-INF/layers.idx"
- "META-INF/"
3.2. レイヤーとの相互作用
アーティファクト内のレイヤーをリストしてみましょう。
java -Djarmode=layertools -jar target/docker-spring-boot-0.0.1.jar list
結果は、layers.idxファイルのコンテンツの単純なビューを提供します。
dependencies
spring-boot-loader
snapshot-dependencies
application
レイヤーをフォルダーに抽出することもできます。
java -Djarmode=layertools -jar target/docker-spring-boot-0.0.1.jar extract
次に、次のセクションで説明するように、Dockerfile内のフォルダーを再利用できます。
$ ls
application/
snapshot-dependencies/
dependencies/
spring-boot-loader/
3.3. Dockerfileの構成
Dockerの機能を最大限に活用するには、イメージにレイヤーを追加する必要があります。
まず、fatjarファイルをベースイメージに追加しましょう。
FROM adoptopenjdk:11-jre-hotspot as builder
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar
次に、アーティファクトのレイヤーを抽出しましょう。
RUN java -Djarmode=layertools -jar application.jar extract
最後に、抽出したフォルダーをコピーして、対応するDockerレイヤーを追加しましょう。
FROM adoptopenjdk:11-jre-hotspot
COPY --from=builder dependencies/ ./
COPY --from=builder snapshot-dependencies/ ./
COPY --from=builder spring-boot-loader/ ./
COPY --from=builder application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]
この構成では、ソースコードを変更すると、アプリケーション層のみが再構築されます。 残りはキャッシュされたままになります。
4. カスタムレイヤー
すべてが魅力のように機能しているようです。 しかし、注意深く見ると、依存関係レイヤーはビルド間で共有されていません。 つまり、内部のものも含めて、すべてが単一のレイヤーになります。 したがって、内部ライブラリのクラスを変更すると、すべての依存関係レイヤーが再構築されます。
4.1. SpringBootを使用したカスタムレイヤー構成
Spring Bootでは、別の構成ファイルを使用してカスタムレイヤーを調整できます。
<layers xmlns="http://www.springframework.org/schema/boot/layers"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/boot/layers
https://www.springframework.org/schema/boot/layers/layers-2.3.xsd">
<application>
<into layer="spring-boot-loader">
<include>org/springframework/boot/loader/**</include>
</into>
<into layer="application" />
</application>
<dependencies>
<into layer="snapshot-dependencies">
<include>*:*:*SNAPSHOT</include>
</into>
<into layer="dependencies" />
</dependencies>
<layerOrder>
<layer>dependencies</layer>
<layer>spring-boot-loader</layer>
<layer>snapshot-dependencies</layer>
<layer>application</layer>
</layerOrder>
</layers>
ご覧のとおり、依存関係とリソースをレイヤーにマッピングして順序付けしています。 さらに、必要な数のカスタムレイヤーを追加できます。
ファイルにlayers.xmlという名前を付けましょう。 次に、Mavenで、レイヤーをカスタマイズするようにこのファイルを構成できます。
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
<configuration>${project.basedir}/src/layers.xml</configuration>
</layers>
</configuration>
</plugin>
アーティファクトをパッケージ化すると、結果はデフォルトの動作と同様になります。
4.2. 新しいレイヤーの追加
アプリケーションクラスを追加して、内部依存関係を作成しましょう。
<into layer="internal-dependencies">
<include>com.baeldung.docker:*:*</include>
</into>
さらに、新しいレイヤーを注文します。
<layerOrder>
<layer>internal-dependencies</layer>
</layerOrder>
その結果、fat jar内のレイヤーを一覧表示すると、新しい内部依存関係が表示されます。
dependencies
spring-boot-loader
internal-dependencies
snapshot-dependencies
application
4.3. Dockerfileの構成
抽出したら、Dockerイメージに新しい内部レイヤーを追加できます。
COPY --from=builder internal-dependencies/ ./
したがって、イメージを生成すると、Dockerが内部依存関係を新しいレイヤーとして構築する方法がわかります。
$ mvn package
$ docker build -f src/main/docker/Dockerfile . --tag spring-docker-demo
....
Step 8/11 : COPY --from=builder internal-dependencies/ ./
---> 0e138e074118
.....
その後、Dockerイメージのレイヤーの構成を履歴で確認できます。
$ docker history --format "{{.ID}} {{.CreatedBy}} {{.Size}}" spring-docker-demo
c0d77f6af917 /bin/sh -c #(nop) ENTRYPOINT ["java" "org.s… 0B
762598a32eb7 /bin/sh -c #(nop) COPY dir:a87b8823d5125bcc4… 7.42kB
80a00930350f /bin/sh -c #(nop) COPY dir:3875f37b8a0ed7494… 0B
0e138e074118 /bin/sh -c #(nop) COPY dir:db6f791338cb4f209… 2.35kB
e079ad66e67b /bin/sh -c #(nop) COPY dir:92a8a991992e9a488… 235kB
77a9401bd813 /bin/sh -c #(nop) COPY dir:f0bcb2a510eef53a7… 16.4MB
2eb37d403188 /bin/sh -c #(nop) ENV JAVA_HOME=/opt/java/o… 0B
ご覧のとおり、レイヤーにはプロジェクトの内部依存関係が含まれています。
5. 結論
このチュートリアルでは、効率的なDockerイメージを生成する方法を示しました。 つまり、新しいSpringBoot機能を使用してレイヤードjarを作成しました。 単純なプロジェクトの場合、デフォルト構成を使用できます。 また、レイヤーを再利用するためのより高度な構成についても説明しました。
いつものように、コードはGitHubでから入手できます。