Skip to main content

Command Palette

Search for a command to run...

Creating container images with self-contained Java application images

Updated
5 min read
Creating container images with self-contained Java application images

At the end of the previous post, I promised to talk about container image size reduction by packaging only those Java modules that the application actually depends on. For this we would need a modular Java application that uses the Java Platform Module System. But before we get there, let us see how you could create self-contained Java application images for non-modular applications, which are still the majority.

Background

The jpackage tool was introduced for production-use in Java 16. The primary goal of jpackage is to provide packaging support native to the underlying operating system. For example, jpackage can let you create a .DEB package for your Java application on Debian/Ubuntu or a .RPM package on RHEL.

Why are we discussing jpackage in the context of Java container images? Because jpackage creates a self-contained application runtime image as an intermediate artifact, for non-modular Java applications. It packages the application, all its dependencies and the Java runtime into a launcher executable. For projects that are made up of multiple JAR files with one of them containing an application entry-point, a single application runtime image makes it easier to ship a container image.

💡
Like jlink, jpackage also lets you selectively add packages to create an optimized runtime image.

The Badass Runtime plugin

The Badass Runtime Plugin allows you to create custom runtime images for non-modular Java applications compiled with Gradle. For example, you could add this plugin to the build.gradle of a Spring Boot application and produce a single binary image that bundles the application, the dependent libraries and the Java runtime (which should be JDK 11 or above).

Adding this plugin to the build.gradle file is as simple as:

plugins {
    ...
    ...
    id 'org.beryx.runtime' version '1.12.5'
}

There are various configuration options that this plugin supports. Configuring the application name and the main class (the entry-point) is mandatory:

application {
    mainClass = 'com.my.app.ApplicationMain'
    applicationName = 'my-app'
}

There are several optional configuration options detailed here. For example, you may want to use the runtime option to define a set of Java runtime modules that your non-modular application depends on.

runtime {
    modules = ['java.base', 'java.desktop', 'java.management']
}
💡
Figuring out this list for a non-modular application could be done by using the suggestModules task offered by the plugin.

Using Badass Runtime along with the Rockcraft Gradle plugin

The Badass Runtime plugin could be used in conjunction with the Rockcraft Gradle plugin to generate container images with a Java application runtime image. I demonstrate this using the Spring Petclinic sample application.

💡
Please refer to the initial setup instructions from one of the initial posts.

Step 1: Clone the Spring Petclinic project

git clone https://github.com/spring-projects/spring-petclinic && \
cd spring-petclinic

Step 2: Add the Badass runtime plugin to build.gradle

Let’s also configure the main class. This is mandatory.

plugins {
    ...
    ...
    id 'org.beryx.runtime' version '1.12.5'
}

application {
    mainClass = 'org.springframework.samples.petclinic.PetClinicApplication'
    applicationName = 'spring-petclinic'
}
💡
I picked up the application name from the settings.gradle file.

Step 3: Add the Rockcraft Gradle plugin

plugins {
    ...
    ...
    'org.beryx.runtime' version '1.12.5'
    id 'io.github.rockcrafters.rockcraft' version '1.1.3'
}

Step 4: Build the application using the push-rock task

./gradlew push-rock -i

If this command completes successfully, you should find a container image named spring-petclinic in the local docker daemon.

$ docker image ls
REPOSITORY         TAG              IMAGE ID       CREATED              SIZE
spring-petclinic   3.5.0            cf0d81414f8d   About a minute ago   191MB
spring-petclinic   latest           cf0d81414f8d   About a minute ago   191MB

Step 5: Launching the application container

Let’s inspect the contents of the container using pebble. The custom Java application runtime image is located under /image/bin.

$ docker run spring-petclinic:latest exec ls /image/bin
java
jrunscript
keytool
spring-petclinic
spring-petclinic.bat

Next, execute this image.

$ docker run --network=host spring-petclinic:latest exec /image/bin/spring-petclinic


              |\      _,,,--,,_
             /,`.-'`'   ._  \-;;,_
  _______ __|,4-  ) )_   .;.(__`'-'__     ___ __    _ ___ _______
 |       | '---''(_/._)-'(_\_)   |   |   |   |  |  | |   |       |
 |    _  |    ___|_     _|       |   |   |   |   |_| |   |       | __ _ _
 |   |_| |   |___  |   | |       |   |   |   |       |   |       | \ \ \ \
 |    ___|    ___| |   | |      _|   |___|   |  _    |   |      _|  \ \ \ \
 |   |   |   |___  |   | |     |_|       |   | | |   |   |     |_    ) ) ) )
 |___|   |_______| |___| |_______|_______|___|_|  |__|___|_______|  / / / /
 ==================================================================/_/_/_/

:: Built with Spring Boot :: 3.5.0
...
...

The Spring Petclinic application must now be accessible at http://localhost:8080.

Image size considerations

Unlike jlink and a modular application, it isn’t very straightforward to strip down unnecessary Java system modules while building an image for a non-modular application. But if you can deduce a minimal list of modules that the application depends on, the size of the image could be trimmed down using the runtime configuration mentioned previously. However, I don’t think it is a common pattern to use jpackage runtime images to achieve this.

The Rockcraft Maven/Gradle plugins adopt the minimal-size strategy by using chiseled base images. A modular Java application and a custom runtime image created using the Badass Jlink plugin may help in further size reductions. This is something we should explore next.

Finally, here is the screen-cast that captures the above steps. Thank you for reading on!

Java application containers on Ubuntu

Part 4 of 6

The Maven and Gradle plugins for Rockcraft let you build Ubuntu container images for your Java application through a maven goal or a gradle task. They let you build and test apps in the very same container they would be eventually deployed in.

Up next

Java application containers with Gradle and Rockcraft

Introduction In the previous post of this series, I introduced the Rockcraft Maven plugin. I demonstrated how application container images could be easily created as a part of a Java developer’s typical Maven workflow. This post introduces the Gradle...

More from this blog

Java for Ubuntu

14 posts