WebSphere Application Server & Liberty

 View Only

Creating a Minimized Liberty Container Image

By Joe McClure posted Mon November 08, 2021 10:18 AM

  

In this blog post I will show how to create a minimized Liberty container image. The idea is based on an article by Tim Pickett: https://developer.ibm.com/articles/modernize-and-optimize-spring-boot-applications/. Tim’s article focused on Spring Boot applications in JAR files; I will be focusing on more general WAR applications.

I will create a container image with a minimal Liberty package, a minimal Java Runtime Environment (JRE), and an Open J9 Shared Class Cache (SCC) to improve startup time.

To demonstrate this approach, I am using the acmeair-monolithic benchmark, which you can find here: https://github.com/blueperf/acmeair-monolithic-java.

So, why do this? What does it do for you? As you can see below, you end up with a significantly smaller image because you only package what is essential. A smaller image results in faster deployments and push times, and a smaller memory requirement. 

  
Notes: 

 
Here are steps to play along at home. This assumes you have git, docker, maven, and a JDK installed on your machine. These steps will build the base image and application image described below:

git clone https://github.com/blueperf/acmeair-monolithic-java
cd acmeair-monolithic-java
mvn package
./build-minimal.sh

There are two important Dockerfiles. I have tried to make these Dockerfiles generic to work with any WAR application and configuration, but your mileage may vary. If you encounter issues or have suggestions, please contact me.

  • Dockerfile-base-minimal – This Dockerfile will build a base image with the minimized Liberty image, minimized JRE, and the SCC.

  • Dockerfile-app-minimal – This Dockerfile will add the application and configuration files to the base image and add another layer to the SCC.

 

Dockerfile-base-minimal

 https://github.com/blueperf/acmeair-monolithic-java/blob/main/Dockerfile-base-minimal

 

This is a multi-stage Dockerfile with a lot going on. I will attempt to explain it all below. I recommend opening it with the link above and following along below. It takes two parameters:

  • CONFIG_DIRECTORY – The directory with the Liberty config (server.xml, jvm.options, server.env, etc.).
    • For this example: src/main/liberty/config

  • WAR_FILE – The built application.
    • For this example: target/acmeair-java-2.0.0-SNAPSHOT.war

 

The first step is to create a minimized Liberty image.

This section begins here in Dockerfile-base-minimal:
    ### CREATE SMALL LIBERTY IMAGE ### 

  • Start with an official Open Liberty kernel-slim image.
  • Copy in configuration files (so we know what Liberty features to install, via the server.xml).
  • Install the needed Liberty features (with features.sh).
  • Use server package to create a minimized liberty image.

 

Next, we create a minimized JRE. This is a bit messy, as we need to discover the Java modules that are required.

This section begins here in Dockerfile-base-minimal:
    ### CREATE SMALL JRE IMAGE ### 

  • Start with an official IBM Semeru JDK 11 image.
  • Copy over the minimized Liberty image from above.
  • Copy over the application (WAR_FILE).
  • Search for Java modules using jdeps.
    • This will probably find some modules we won’t need and will miss some modules that we do need. So, we exclude any modules listed in jdeps/java_modules_exclude.txt and add any modules found in jdeps/java_modules_append.txt.
    • This process can be slow. If you already know the Java modules required, you can list them in jdeps/java_modules.txt and skip this step (see the comment near line 30 in Dockerfile-base-minimal).
  • Finally, jlink is invoked to create a minimized JRE.

 

Next, we create the Shared Class Cache.

This section begins here in Dockerfile-base-minimal:
    ### CREATE BASE SCC LAYER ### 

  • Start with a ubi8-minimal image.
  • Copy in the minimized Liberty and JRE images created above.
  • Invoke the populate_scc.sh script, which starts and stops the Liberty server to create and populate a SCC. This step will also populate the Liberty feature and configuration caches, which also helps improve startup time.

  

Finally, we assemble our base image.

This section begins here in Dockerfile-base-minimal:
    ### FINAL BASE IMAGE ### 

  • Start with a ubi8-minimal image.
  • Copy over the minimized Liberty, minimized JRE, and the SCC.
  • Copy in the configuration files.
  • Set some Java Options.

Example for how to build the base image with Dockerfile-base-minimal:

docker build -t acmeair-mono-base-minimal -f Dockerfile-base-minimal --build-arg CONFIG_DIRECTORY=src/main/liberty/config --build-arg WAR_FILE=target/acmeair-java-2.0.0-SNAPSHOT.war .

 

If you’re curious, here is what makes up the base image.

Layer

Size (MB)

ubi8-minimal

102

Liberty minimized

66.3

JRE Minimized

45.3

SCC

26.2

Total

239.8

 

The idea behind the base image is to have a bigger layer that you won’t need to change/push very often because it contains the features you need, the java modules you need, and a populated SCC. If you have minor changes in your application layer, those can be changed in the next step. The hope is that you usually only need to push the much smaller application layer.

 

Dockerfile-app-minimal

 https://github.com/blueperf/acmeair-monolithic-java/blob/main/Dockerfile-app-minimal

 

Now we build another image on top of the base image with Dockerfile-app-minimal. This is a much simpler Dockerfile. It takes the same two parameters as above.

  • CONFIG_DIRECTORY – The directory with the Liberty config (server.xml, jvm.options, server.env, etc.).
    • For this example: src/main/liberty/config

  • WAR_FILE – The built application.
    • For this example: target/acmeair-java-2.0.0-SNAPSHOT.war


Dockerfile-app-minimal explained:

  • Start with the base image created above.
  • Copy in the configuration and application (WAR_FILE).
  • Invoke populate_scc.sh again. This will add another layer to the SCC (and re-populate the Liberty feature and configuration caches).
  • Make sure Java Options are set.


Example for how to build the application image with Dockerfile-app-minimal:

docker build -t acmeair-mono-app-minimal -f Dockerfile-app-minimal --build-arg CONFIG_DIRECTORY=src/main/liberty/config --build-arg WAR_FILE=target/acmeair-java-2.0.0-SNAPSHOT.war .

  
As you can see below our application layer is only 25MB.

 


Final Note: The SCC may add a decent amount of size to the images. Is this worth it for a faster startup time? Below is the difference in startup time between including a populated SCC (and populated Liberty feature and configuration caches) versus not caching anything. This was done on my MacBook Pro laptop.

 

 

 If startup time is not a priority, and you want an even smaller image, you can skip building the SCC layers and set -Xshareclasses:none. This will result in a base layer of 214 MB and an application layer of 3 MB.

 See these files for an example.

  • build-minimal-no-scc.sh
  • Dockerfile-base-minimal-no-scc
  • Dockerfile-app-minimal-no-scc

 

 In summary, in this post I demonstrated a way to build a minimized Liberty container image with a small Liberty package, a small JRE, and a SCC.

#websphere-performance

#containers#Liberty

#OpenLiberty

​​​​​​​​​
0 comments
31 views

Permalink