Containerizing your Microservice in Quarkus with Jib

Iain Porter
4 min readSep 8, 2020

--

Photo by Guillaume TECHER on Unsplash

Jib is a google project for building optimised docker images for JVM applications without the need for a docker daemon or docker build files. The library has maven and gradle plugins for building docker images and pushing them to a docker repository.

If you are a wiz at writing docker scripts and would rather control every aspect of the docker build you might be tempted to skip Jib. However, it does provide a useful starting point and gets your application built and deployed quickly. There is also a plugin extension for Jib that allows you to customise every aspect of the build plan to suit your requirements.

The typical docker flow looks like this

Docker flow

With Jib the flow it is reduced to

Jib build flow

Simple

Using Jib removes the complexity of writing docker files. There is no need for a docker daemon for building and pushing. It is based on best practices so is highly optimised for building java containers.

Small

Jib builds are based on the distroless base image and for java applications the distroless java base image, so your container will only contain your application and its runtime dependencies. This means it will be a minimal image and won’t even contain any shell access.

Fast

The container is separated into dependencies, resources, and classes and only those parts that change will be rebuilt. Jib organises layers smartly and will reuse cached layers that contain files that have not changed.

To understand how Jib creates the container layers here is a comparative docker file that achieves the same build

FROM gcr.io/distroless/java:latest

COPY dependencyJars /app/libs
COPY snapshotDependencyJars /app/libs
COPY projectDependencyJars /app/libs
COPY resources /app/resources
COPY classFiles /app/classes
COPY src/main/jib /

ENTRYPOINT ["java", jib.container.jvmFlags, "-cp", "/app/resources:/app/classes:/app/libs/*", jib.container.mainClass]
CMD [jib.container.args]

Quarkus and Jib

The Jib plugin is integrated into a Quarkus dependency library and can be configured to build and push images without the need for a docker file. All dependencies are cached in a different layer than the application making rebuilds fast and small.

Converting an existing service to use Jib

Using an existing microservice that we built in Quarkus that used docker files and the maven exec plugin to invoke the docker daemon, let’s convert it to use Jib.

To follow along you can pull the latest branch that uses docker files

$ git clone git@github.com:iainporter/sms-service.git
$ git checkout part_five

This branch uses a docker file for the non-native build

FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1

ARG JAVA_PACKAGE=java-11-openjdk-headless
ARG RUN_JAVA_VERSION=1.3.8

ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en'

# Install java and the run-java script
# Also set up permissions for user `1001`
RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \
&& microdnf update \
&& microdnf clean all \
&& mkdir /deployments \
&& chown 1001 /deployments \
&& chmod "g+rwX" /deployments \
&& chown 1001:root /deployments \
&& curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \
&& chown 1001 /deployments/run-java.sh \
&& chmod 540 /deployments/run-java.sh \
&& echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security

# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size.
ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"

COPY target/lib/* /deployments/lib/
COPY target/*-runner.jar /deployments/sms-app.jar

EXPOSE 8080
USER 1001

ENTRYPOINT [ "/deployments/run-java.sh" ]

If we build the application from the root directory

$ mvn clean install

We can use a tool called dive to inspect the image

To use Jib we can remove the docker file and the maven exec plugin to build and push the image. Next add the Quarkus Jib dependency

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-container-image-jib</artifactId>
</dependency>

Before we can build it we need to add a few configuration properties to define the image

quarkus.container-image.build=true
quarkus.container-image.group=iainporter
quarkus.container-image.name=sms-service

It should be noted that if the property quarkus.container-image.build is not present or the value is false then the resultant build image will be registered with the docker daemon.

Building it again now with Jib

mvn clean install

When we inspect it with dive we can see the differences

Pushing the image

Typically, as part of your CI build you would use a docker credentials helper to store the registry credentials. For ease of testing we can pass them as arguments to the maven plugin.

mvn clean package \
-Dquarkus.container-image.push=true \
-Dquarkus.container-image.registry=registry.hub.docker.com \
-Dquarkus.jib.base-registry-username=<your registry username> \
-Dquarkus.jib.base-registry-password=<your registry password> \

Google plans to add a lot more features to the Jib project. See also the jib-extensions project.

The source code for this article can be found here

The other parts in this series are:

--

--

Iain Porter

Technology pathfinder who likes to code when not on his bike