Java application containers with Maven and Rockcraft

Introduction

You are developing a Maven-based Spring Boot application. This Java application will be eventually production-deployed, as a micro-service, in a container. Well, let us assume an Ubuntu-based chiseled container! During your development iterations you want to test the application in a container that is the same as the production container. Consequently, your development iterations have an additional step following the maven build - creating the container image. Can we simply add this step to the Maven workflow?

This Rockcraft Build Plugins can let you create application container images as a Maven build artifact. In this post, I demonstrate the use of the Maven Rockcraft plugin.

Setup

I assume you have the Java development environment set up. For this demo, I used a Java 21 on the host, because the generated Ubuntu ROCK has the same version installed.

Ubuntu ROCKs are OCI-compliant container images that can be stored in an OCI-compliant registry like DockerHub and used by any OCI-compliant tool like Docker. Rockcraft is a command-line tool used to create ROCK images from a YAML-based declarative syntax.

Rockcraft is available as a snap and should be installed as a prerequisite.

snap install rockcraft --classic

If not already present, install docker.

snap install docker --classic
💡
If you installed the docker snap it is mandatory to configure it to be run by a normal user. You also need to manually connect the necessary plugs and slots - please refer to the group of snap connect commands in this section of the docker-snap README.

Building application container images

I use this simple Spring Boot application as an example for this demo.

Step 1: Clone the application repository

git clone https://github.com/spring-guides/gs-spring-boot/
cd gs-spring-boot/initial

Step 2: Add the Maven Rockcraft plugin to the pom.xml

Add the following XML to the pom.xml file as a build plugin:

            <plugin>
                <groupId>io.github.rockcrafters</groupId>
                <artifactId>rockcraft-maven-plugin</artifactId>
                <version>1.1.2</version>
                <executions>
                    <execution>
                        <configuration>
                            <createService>false</createService>
                        </configuration>
                        <goals>
                            <goal>create-rock</goal>
                            <goal>build-rock</goal>
                            <goal>push-rock</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

Step 3: Run mvn install

In the XML above, we see three new goals added by the rockcraft-maven-plugin:
1. The create-rock goal generates the rockcraft.yaml for this application in the target directory
2. The build-rock goal builds an OCI-compliant Ubuntu ROCK image, again in the target directory
3. The push-rock goal copies this image to the local Docker daemon

💡
The generated Ubuntu ROCK is chiseled container image. You may read about chiseled Java images here.

Step 4: Run the application container

Assuming that the previous step completed successfully, you should now find a new image in your local docker daemon.

$ docker image ls
REPOSITORY                        TAG              IMAGE ID       CREATED              SIZE
spring-boot-initial               0.0.1-SNAPSHOT   e49f7a343c87   About a minute ago   145MB
spring-boot-initial               latest           e49f7a343c87   About a minute ago   145MB
...

By default, Ubuntu ROCK images package the Pebble service manager which also acts as the default entrypoint into the image. From a usability point-of-view this only means that the docker run command to launch the container might appear a little different, if you are a regular docker user. We use Pebble’s exec service to launch the Java application:

$ docker run --network=host spring-boot-initial:latest exec \
    /usr/bin/java -jar /jars/spring-boot-initial-0.0.1-SNAPSHOT.jar
💡
A Spring Boot application will be compiled into a single JAR with the name <artifact-id>-<version>.jar. In the container image, this JAR is placed under the /jars directory.

The last command should start up a Spring Boot application listening at port 8080. You may now access http://localhost:8080 from a browser to see a Greetings for Spring Boot! message.

Step 5: Iterate

During local development, you could repeat steps 3 and 4 over and over again. Every time a new image would be created and pushed to the local docker daemon.

Here is a screen-cast that captures steps 1 through 5.

Thanks for reading along! In the next article in this series, I plan to write about the Gradle plugin for creating container images.

0
Subscribe to my newsletter

Read articles from Pushkar Kulkarni directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Pushkar Kulkarni
Pushkar Kulkarni