Building Containers With Spring Boot 2.3

Spring Boot is a great tool for building micro services. It is no surprise that most Spring Boot applications we write are being deployed as Docker containers.

Building our applications as Docker images from Maven or Gradle involves some external plugin like the ones from Spotify or Fabric8 and a definition like a Dockerfile. Not anymore though!

With the release of Spring Boot 2.3, these external plugins are no longer needed. Spring Boot 2.3 brings us native Docker support through the spring-boot-maven plugin.

As with everything Spring Boot, this new feature is opinionated. There are a few things you should consider. More on that later, first let’s see how it works.

Building An Image

In this article, we’ll be using Maven the default demo project generated with Spring Intializr.

Our application is generated with a pom.xml containing the spring-boot-maven-plugin with default configuration:

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
  </plugins>
</build>

Now let’s add a little configuration:

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      <configuration>
        <image />
      </configuration>
    </plugin>
  </plugins>
</build>

By only adding these three lines, we are able to build our Docker image with:

mvn spring-boot:build-image

We’ll end up with a Docker image named ‘demo’ with version ‘0.0.1-SNAPSHOT’.

Naming and versioning

While this is pretty neat, we’d might want to make some changes. For example, we can update the configuration to customize the name of the image:

<configuration>
  <image>
    <name>demo/demo-app</name>
  </image>
</configuration>

As we haven’t specified a version, this configuration will build and image called ‘demo/demo-app’ with version ‘latest’, the Docker equivalent of a snapshot build.

We can easily use the project version or any other version by adding it in the name-element:

<configuration>
  <image>
    <name>demo/demo-app:${project.version}</name>
  </image>
</configuration>

Layering

Because we like our deployments to be fast, we can turn on layers:

<configuration>
  <layers>
    <enabled>true</enabled>
  </layers>
  <image>
    <name>demo/demo-app:${project.version}</name>
  </image>
</configuration>

The plugin will now package our application as a layered jar file with layers ordered from least to most likely to change with a new build:

  1. 1.   Dependencies
  2. 2.   Snapshot-dependencies
  3. 3.   Resources
  4. 4.   Application

Java Version

By default our image will be based on Java 11, which is a reasonable choice being the latest LTS release. We can easily change the version to Java 14 for example with the BP_JVM_VERSION environment variable:

<configuration>
  <layers>
    <enabled>true</enabled>
  </layers>
  <image>
    <name>demo/demo-app:${project.version}</name>
    <env>
      <BP_JVM_VERSION>14</BP_JVM_VERSION>
    </env>
  </image>
</configuration>

Packaging

Last but not least, it would be preferable to build the image automatically during mvn package. As package is the default phase for building an image, it is as simple as adding the build-image goal to the plugins executions.

This is all that is required for us to build our Docker images with Spring Boot from now on. Our final plugin configuration looks like this:

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <configuration>
    <layers>
      <enabled>true</enabled>
    </layers>
    <image>
      <name>demo/demo-app:${project.version}</name>
      <env>
        <BP_JVM_VERSION>14</BP_JVM_VERSION>
      </env>
    </image>
  </configuration>
  <executions>
    <execution>
      <goals>
        <goal>build-image</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Things to Consider

There are some things to consider though. As said, Spring Boot is an opinionated take on web development and this new feature is no exception.

Under the hood, the plugin uses buildpacks. Buildpacks are an alternative way to building images as opposed to Dockerfiles. One might want to get familiar with this technology to know what’s going on here.

More interesting are the choice of base image and JVM.

By default, Spring Boot uses the BellSoft Liberica Buildpack. This buildpack is based on Cloud Foundry’s cflinuxfs3 stack. This means that our image will be based on Ubuntu 18.04 and our application will run in the BellSoft Liberica implementation of the Java runtime.

Should this be a problem, you’d might want to change buildpacks. It is customizable, as is everything in Spring Boot.

 

The example code used in this project can be found in GitHub.

 

Rens Verhage

Rens Verhage is sinds maart 2020 in dienst als Senior Software Engineer bij Profit4Cloud. Rens heeft ruim 14 jaar ervaring met Java en is OCA / OCP gecertificeerd.