142 lines
7.5 KiB
Plaintext
142 lines
7.5 KiB
Plaintext
[[features.container-images]]
|
||
== Container Images
|
||
It is easily possible to package a Spring Boot fat jar as a docker image.
|
||
However, there are various downsides to copying and running the fat jar as is in the docker image.
|
||
There’s always a certain amount of overhead when running a fat jar without unpacking it, and in a containerized environment this can be noticeable.
|
||
The other issue is that putting your application's code and all its dependencies in one layer in the Docker image is sub-optimal.
|
||
Since you probably recompile your code more often than you upgrade the version of Spring Boot you use, it’s often better to separate things a bit more.
|
||
If you put jar files in the layer before your application classes, Docker often only needs to change the very bottom layer and can pick others up from its cache.
|
||
|
||
|
||
|
||
[[features.container-images.layering]]
|
||
=== Layering Docker Images
|
||
To make it easier to create optimized Docker images, Spring Boot supports adding a layer index file to the jar.
|
||
It provides a list of layers and the parts of the jar that should be contained within them.
|
||
The list of layers in the index is ordered based on the order in which the layers should be added to the Docker/OCI image.
|
||
Out-of-the-box, the following layers are supported:
|
||
|
||
* `dependencies` (for regular released dependencies)
|
||
* `spring-boot-loader` (for everything under `org/springframework/boot/loader`)
|
||
* `snapshot-dependencies` (for snapshot dependencies)
|
||
* `application` (for application classes and resources)
|
||
|
||
The following shows an example of a `layers.idx` file:
|
||
|
||
[source,yaml,indent=0,subs="verbatim"]
|
||
----
|
||
- "dependencies":
|
||
- BOOT-INF/lib/library1.jar
|
||
- BOOT-INF/lib/library2.jar
|
||
- "spring-boot-loader":
|
||
- org/springframework/boot/loader/JarLauncher.class
|
||
- org/springframework/boot/loader/jar/JarEntry.class
|
||
- "snapshot-dependencies":
|
||
- BOOT-INF/lib/library3-SNAPSHOT.jar
|
||
- "application":
|
||
- META-INF/MANIFEST.MF
|
||
- BOOT-INF/classes/a/b/C.class
|
||
----
|
||
|
||
This layering is designed to separate code based on how likely it is to change between application builds.
|
||
Library code is less likely to change between builds, so it is placed in its own layers to allow tooling to re-use the layers from cache.
|
||
Application code is more likely to change between builds so it is isolated in a separate layer.
|
||
|
||
Spring Boot also supports layering for war files with the help of a `layers.idx`.
|
||
|
||
For Maven, refer to the {spring-boot-maven-plugin-docs}#repackage-layers[packaging layered jar or war section] for more details on adding a layer index to the archive.
|
||
For Gradle, refer to the {spring-boot-gradle-plugin-docs}#packaging-layered-archives[packaging layered jar or war section] of the Gradle plugin documentation.
|
||
|
||
|
||
|
||
[[features.container-images.building]]
|
||
=== Building Container Images
|
||
Spring Boot applications can be containerized <<features#features.container-images.building.dockerfiles,using Dockerfiles>>, or by <<features#features.container-images.building.buildpacks,using Cloud Native Buildpacks to create docker compatible container images that you can run anywhere>>.
|
||
|
||
|
||
|
||
[[features.container-images.building.dockerfiles]]
|
||
==== Dockerfiles
|
||
While it is possible to convert a Spring Boot fat jar into a docker image with just a few lines in the Dockerfile, we will use the <<features#features.container-images.layering,layering feature>> to create an optimized docker image.
|
||
When you create a jar containing the layers index file, the `spring-boot-jarmode-layertools` jar will be added as a dependency to your jar.
|
||
With this jar on the classpath, you can launch your application in a special mode which allows the bootstrap code to run something entirely different from your application, for example, something that extracts the layers.
|
||
|
||
CAUTION: The `layertools` mode can not be used with a <<deployment#deployment.installing, fully executable Spring Boot archive>> that includes a launch script.
|
||
Disable launch script configuration when building a jar file that is intended to be used with `layertools`.
|
||
|
||
Here’s how you can launch your jar with a `layertools` jar mode:
|
||
|
||
[source,shell,indent=0,subs="verbatim"]
|
||
----
|
||
$ java -Djarmode=layertools -jar my-app.jar
|
||
----
|
||
|
||
This will provide the following output:
|
||
|
||
[subs="verbatim"]
|
||
----
|
||
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
|
||
----
|
||
|
||
The `extract` command can be used to easily split the application into layers to be added to the dockerfile.
|
||
Here's an example of a Dockerfile using `jarmode`.
|
||
|
||
[source,dockerfile,indent=0,subs="verbatim"]
|
||
----
|
||
FROM adoptopenjdk:11-jre-hotspot as builder
|
||
WORKDIR application
|
||
ARG JAR_FILE=target/*.jar
|
||
COPY ${JAR_FILE} application.jar
|
||
RUN java -Djarmode=layertools -jar application.jar extract
|
||
|
||
FROM adoptopenjdk:11-jre-hotspot
|
||
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.JarLauncher"]
|
||
----
|
||
|
||
Assuming the above `Dockerfile` is in the current directory, your docker image can be built with `docker build .`, or optionally specifying the path to your application jar, as shown in the following example:
|
||
|
||
[source,shell,indent=0,subs="verbatim"]
|
||
----
|
||
$ docker build --build-arg JAR_FILE=path/to/myapp.jar .
|
||
----
|
||
|
||
This is a multi-stage dockerfile.
|
||
The builder stage extracts the directories that are needed later.
|
||
Each of the `COPY` commands relates to the layers extracted by the jarmode.
|
||
|
||
Of course, a Dockerfile can be written without using the jarmode.
|
||
You can use some combination of `unzip` and `mv` to move things to the right layer but jarmode simplifies that.
|
||
|
||
|
||
|
||
[[features.container-images.building.buildpacks]]
|
||
==== Cloud Native Buildpacks
|
||
Dockerfiles are just one way to build docker images.
|
||
Another way to build docker images is directly from your Maven or Gradle plugin, using buildpacks.
|
||
If you’ve ever used an application platform such as Cloud Foundry or Heroku then you’ve probably used a buildpack.
|
||
Buildpacks are the part of the platform that takes your application and converts it into something that the platform can actually run.
|
||
For example, Cloud Foundry’s Java buildpack will notice that you’re pushing a `.jar` file and automatically add a relevant JRE.
|
||
|
||
With Cloud Native Buildpacks, you can create Docker compatible images that you can run anywhere.
|
||
Spring Boot includes buildpack support directly for both Maven and Gradle.
|
||
This means you can just type a single command and quickly get a sensible image into your locally running Docker daemon.
|
||
|
||
Refer to the individual plugin documentation on how to use buildpacks with {spring-boot-maven-plugin-docs}#build-image[Maven] and {spring-boot-gradle-plugin-docs}#build-image[Gradle].
|
||
|
||
NOTE: The https://github.com/paketo-buildpacks/spring-boot[Paketo Spring Boot buildpack] has also been updated to support the `layers.idx` file so any customization that is applied to it will be reflected in the image created by the buildpack.
|
||
|
||
NOTE: In order to achieve reproducible builds and container image caching, Buildpacks can manipulate the application resources metadata (such as the file "last modified" information).
|
||
You should ensure that your application does not rely on that metadata at runtime.
|
||
Spring Boot can use that information when serving static resources, but this can be disabled with configprop:spring.web.resources.cache.use-last-modified[]
|