Build containers for Maven and Gradle applications

Past articles of this series have demonstrated how easy it is to produce application container images for Maven and Gradle applications. The containers are built on a chiseled Ubuntu base, making them suitable for deployment. The one last bit that I’d like to cover in this series is about build and test containers, referred to as build containers.
It is difficult to think about rapid and iterative application development without continuous integration, which encompasses continuous testing. GitHub Actions and Worflows have added immense value to this widespread practice, providing the needed automation framework and infrastructure. However, some organizations might want to run continuous integration in an air-gapped environment, ensuring that dependencies are only fetched locally.
Build containers bundle all possible build dependencies, direct and transitive. Such containers may be used to build and test the application, as long as the build dependencies are not updated. They offer a customized build-system that can build applications and run unit/regression tests in an air-gapped environment.
Let us dive straight into how we may use the Rockcraft plugins to create build containers for Maven and Gradle applications. I will also demonstrate how these containers may be used. Though the user experience is quite uniform, I will include examples for Maven and Gradle.
Creating and running build containers for Maven applications
Step 1: Clone your Maven project
I am using the gs-spring-boot project for this demo.
git clone https://github.com/spring-guides/gs-spring-boot && \
cd gs-spring-boot/initial
Step 2: Add the Rockcraft Maven plugin dependency
Add the Rockcraft Maven plugin to the pom.xml.
<plugin>
<groupId>io.github.rockcrafters</groupId>
<artifactId>rockcraft-maven-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<configuration>
<createService>false</createService>
<allowLocal>true</allowLocal>
</configuration>
<goals>
<goal>create-build-rock</goal>
<goal>build-build-rock</goal>
<goal>push-build-rock</goal>
</goals>
</execution>
</executions>
</plugin>
Please note the three new goals we’re using here - create-build-rock, build-build-rock and push-build-rock. The create-build-rock goal creates the Rockcraft YAML for the build rock, the build-build-rock packs the rock, and the push-build-rock goal uses skopeo to convert the rock to a docker image pushed to the local daemon.
Step 3: Run the Maven build
This can take a few minutes.
mvn install
On successful completion of the above command, you may list the docker images to find the build container images pushed recently.
$ docker image ls | more -3
REPOSITORY TAG IMAGE ID CREATED SIZE
build-spring-boot-initial 0.0.1-SNAPSHOT bc01a7c8b36d 10 minutes ago 304MB
build-spring-boot-initial latest bc01a7c8b36d 10 minutes ago 304MB
Step 4: Build the application using this container
You may now build the local clone of your application using this container. Please note the following in this context:
You are building the local clone of the application source.
You need to
cdinto the application directory to run the following command.Though the build happens inside the container, the artifacts are produced on the host.
Optionally, you may delete target directory, the local .m2 cache and disconnect your work-environment from the internet!
rm -rf ./target && rm -rf ~/.m2
Run the build-container.
docker run -v `pwd`:`pwd` \
--user $(id -u):$(id -g) \
--env PEBBLE=/tmp build-spring-boot-initial:latest \
exec build `pwd`
On successful completion, you must find the target directory recreated with the JAR file. You may try launching the application using:
java -jar target/spring-boot-initial-0.0.1-SNAPSHOT.jar
You may now make changes to the application and repeat step 4. This is demonstrated in the screen-cast captured below.
Creating and running build containers for Gradle applications
The experience with Gradle is quite similar. I’ll use the sample Helidon application to demonstrate this.
Step 1: Clone the Helidon project.
git clone git@github.com:pushkarnk/helidon-hello.git && \
cd helidon-hello
Step 2: Add the Rockcraft plugin dependency
It is a very compact change to the build.gradle. See the third plugin-entry below:
plugins {
id 'java'
id 'application'
id 'io.github.rockcrafters.rockcraft' version '1.2.1'
}
Step 3: Run the Gradle build
I use the Gradle wrapper and specifiy the final push-build-rock goal.
./gradlew push-build-rock -i
This takes a few minutes to complete. After successful completion, you must find new images pushed to the local docker daemon.
$ docker image ls | more -3
REPOSITORY TAG IMAGE ID CREATED SIZE
build-helidon-hello 1.0.0 413a14c99997 10 minutes ago 390MB
build-helidon-hello latest 413a14c99997 10 minutes ago 390MB
Step 4: Build the application using this container
This step is, of course, very much similar to the same step in the Maven workflow above.
Optionally, you may delete the local build directory, the local .m2 cache and disconnect your environment from the internet.
rm -rf ./build && rm -rf ~/.m2
Run the build-container.
docker run -v `pwd`:`pwd` \
--user $(id -u):$(id -g) \
--env PEBBLE=/tmp build-helidon-hello:latest \
exec build `pwd`
On successful completion, you must find the build directory recreated with the fat JAR. You may try launching the application using:
java -jar build/libs/helidon-hello-1.0.0.jar
You may now make changes to the application and repeat step 4. This is demonstrated in the screen-cast captured below.
To summarize, build containers are a novel approach to build an application in a controlled build environment. They have all the build dependencies pre-fetched. As a result, builds run faster and also within an air-gapped environment. You may use build container images in your GitHub Actions too. Just keep in mind that a change in the build-dependencies warrants a regeneration of the build container!
With this, I end the Java Application Containers on Ubuntu series. Thanks for staying on!




