Build layered Docker images with Spring Boot 2.3.1

This allows to extract and separate dependencies from a fat jar. Dependencies that do not change often are separated code that change often.

Combined with docker layering capabilities, it allows to take advantage of docker caching system. Unchanged layers are loaded from cache and changed layers are recomputed.

First let's generate a spring project with spring Initializr

Let's change the pom.xml to enable layering

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <layers>
                    <!-- Allows better and faster dockerfile dependencies caching -->
                    <enabled>true</enabled>
                </layers>
            </configuration>
        </plugin>
    </plugins>
</build>

Let's write a Dockerfile. We are using an intermediate docker image to extract layers from the fat jar. This way, we don't have to add any step to the build or to write multiple scripts depending on developper's OS.

Then we write the actual docker image. We use a "spring" user to avoid running the application as root user.

Dockerfile

FROM openjdk:11.0.7-jdk as builder
WORKDIR application
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar
RUN java -Djarmode=layertools -jar application.jar extract

FROM openjdk:11.0.7-jdk
RUN adduser --shell /bin/sh --disabled-password --gecos "" spring
USER spring:spring
ARG DEPENDENCY=target/dependency
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"]

Now we can...

Build the application with maven.

mvnw package

Build the docker image.

docker build -t baldir/demo-spring-layered .

Or build directly docker image from maven plugin (does not use Dockerfile) and supports layering out of the box.

mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=baldir/demo-spring-layered

Run the image to test it locally:

docker run -it -p8080:8080 baldir/demo-spring-layered:latest

You can verify in your browser : http://localhost:8080