Posts

What is Quarkus

Feb 8, 2024
Catherine Edelveis
9.3

There are multiple Java frameworks for developing Java applications: Spring Boot, Micronaut, MicroProfile, Javalin, etc., each with their own strengths. Quarkus is another Java framework tailored to developing cloud-native Java microservices and promising to optimize memory footprint and the startup time of application.

In this article, I won’t delve into Quarkus startup study, but rather evaluate the footprint of Quarkus containers created with the default settings and via buildpacks, and see if I can optimize the size of a resulting container image even more.

Overview of the Quarkus framework

Quarkus is an open-source stack of technologies with MicroProfile support aimed at optimizing Jakarta EE for building microservices. It is oriented to Kubernetes and adapted to OpenJDK HotSpot and GraalVM.

To create microservices, Quarkus implements Eclipse MicroProfile APIs along with other valuable tools, such as Apache Kafka, Camel, dependency injection, Hibernate ORM (JPA and JTA annotations), RESTEasy (JAX-RS), etc. It also provides Maven and Gradle plugins so that you can run your application in development mode on a local or remote machine.

In addition, Quarkus supports GraalVM, which lets the developers use ahead-of-time (AOT) compilation to convert the bytecode into native machine code. Another Java framework with built-in support for GraalVM Native Image is Spring Boot. With GraalVM, apps can be compiled to native executables with no need for dynamic scanning and loading all classes into a JVM. By default, all classes are initialized at build time. As a result, the application starts much faster and consumes significantly less resources.

Сompliance with Java EE standards enables developers to use existing APIs and run their applications in an optimized runtime without rewriting them. Basically, you can use enterprise APIs and extend them according to your requirements (for example, use Quarkus extensions for reactive messaging, Vert.x, or Camel).

Quarkus tutorial

Create your first Quarkus application

If you are familiar with Spring Boot, you’ve already used the Spring Initializr to quickly whip up a bare Spring Boot application with the required dependencies. Quarkus offers a similar starting point, code.quarkus.io. Let’s go there and adjust some basic settings. Choose

  • Choose Java version 17 and build tool maven,
  • Change the group and artifact ID to your liking or keep the defaults,
  • Select the extensions: RESTEasy Classic and RESTEasy Classic's REST Client.

There are dozens of additional extensions, including those for cloud services, security, messagings, data handling and so on. But we’ll keep it simple for demo purposes.

Press Generate your application and open the project in your favorite IDE.

The application contains only one class, GreetingResource, with the following content:

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/hello")
public class GreetingResource {

   @GET
   @Produces(MediaType.TEXT_PLAIN)
   public String hello() {
       return "Hello RESTEasy";
   }
}

Open the Terminal and run this command to build your application skipping the tests:

./mvnw package quarkus:dev -Dmaven.test.skip=true -Dquarkus.test.continuous-testing=disabled

The REST endpoint is exposed at localhost:8080/hello, so open the page in your browser to see your app’s “Hello RESTEasy” message.

Without stopping the app, you can now introduce changes to your project (for instance, substitute the default message with ”Hello from Quarkus”). Refresh the browser page, and you’ll see a new greeting. This is made possible by the in-build live coding functionality, thanks to which you don’t have to recompile the app and wait for several seconds for it to start every time you make changes.

How to build Quarkus container images

Containerize the Quarkus app with a Dockerfile

As Quarkus offers the “containers-first” approach, let’s see how we can containerize our application.

You can use the traditional approach and configure the container manually with a Dockerfile.

Run

./mvnw -Dmaven.test.skip package

After that, go to the docker directory of your Quarkus project. There are several ready Dockerfiles, we need the Dockerfile.jvm one. It uses Red Hat Ubi 8 and OpenJDK 17 as a base image.

Place the file to the root directory and run 

docker build -f Dockerfile.jvm -t quarkus-red-hat .

Now, check the image with:

docker images
quarkus-red-hat                       latest           04aaf8eb792b   About a minute ago   485MB

The resulting container seems to be too heavy. Can we do better?

Red Hat offers OpenJDK 17 runtime images on UBI8 without JDK tools, the compiler, or Maven. Let’s take this image and see what we get.

Substitute the FROM line in the Dockerfile with:

FROM registry.access.redhat.com/ubi8/openjdk-17-runtime:1.18

After that, run

docker build -f Dockerfile.jvm -t quarkus-red-hat-runtime .

Check the images:

docker images
quarkus-red-hat-runtime              latest     daf6550b5f74   14 seconds ago   444MB

That’s better. But can we optimize the footprint even more?

Return to the Dockerfile and substitute the FROM line with:

FROM bellsoft/liberica-runtime-container:jre-17-stream-musl 

Here, we take advantage of Liberica Runtime Container with Liberica JRE Lite and Alpaquita Linux to create a lightweight container image with your application.

Run

docker build -f Dockerfile.jvm -t quarkus-liberica-runtime-container .

And verify the image with

docker images
quarkus-liberica-runtime-container    latest           0f6b58dc07f0   4 seconds ago        135MB

As you can see, the resulting image is 3.5 times smaller compared to the first one we made!

Containerize the Quarkus app with buildpacks

There’s another approach to building container images — the buildpacks. If you are unfamiliar with the technology, refer to our previous guide. In short, buildpacks facilitate the development with automatic containerization. There’s no need to write a Dockerfile: run one command, and the buildpack will scan the project and turn it into a production-ready image.

The Quarkus project offers its own buildpack for building Java and Native Image containers. Under the hood, it uses Paketo buildpacks that utilize Liberica JDK by default, so the resulting image will be based on this OpenJDK distribution.

To containerize our app this way, we need to add the buildpack extension.

Run

./mvnw quarkus:add-extension -Dextensions='container-image-buildpack'

And then

./mvnw install -Dquarkus.container-image.build=true -Dmaven.test.skip
. . .
Adding cache layer 'paketo-buildpacks/bellsoft-liberica:jdk'
Adding cache layer 'paketo-buildpacks/syft:syft'
Adding cache layer 'paketo-buildpacks/maven:application'
Adding cache layer 'paketo-buildpacks/maven:cache'
[INFO] Buildpack build complete, with exit code 0
[INFO] [io.quarkus.container.image.buildpack.deployment.BuildpackProcessor] Buildpack build complete
[INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 198821ms

This will successfully build a container image of your Quarkus project.

Verify that the image was created (the name of your image will be different depending on your Docker ID):

docker images
catherineedelveis//code-with-quarkus  1.0.0-SNAPSHOT   c6a436460a70   3 minutes ago   255MB

As you can see, buildpacks help to automate the containerization process, but you don’t have the control over the image insides, so it may be bigger than the one created manually.

Conclusion

Quarkus is steadily gaining popularity among developers used to working with Jakarta EE standards. For others, the learning curve may be longer.

As far as container images are concerned, there are two alternatives:

  • Buildpacks that automate the containerization process but take away control over the image layers;

Subcribe to our newsletter

figure

Read the industry news, receive solutions to your problems, and find the ways to save money.

Further reading