Bootstrapping a Quarkus App
Overview
Microservices is now a common term with every software development workspace. Let me remind you, in the simplest definition of what microservice is:
Microservices are a way of breaking large software projects into loosely coupled modules, which communicate with each other through simple Application Programming Interfaces (APIs).
The Java ecosystem is always an amazing example of how technology can reinvent itself in new ways and frameworks.
For example, it is interesting to reflect on how Spring Boot brought the Java community features that made the language use among the most widely used in the world for most of the new projects.
But now, we have so many innovative projects that are designed from the ground up to run on the cloud and with microservice architectures.
Java is proven to be a fantastic technology with an active community but has lost ground in many projects where Python or NodeJS made cloud costs cheaper due to their lower CPU, startup time, and memory consumption.
Thinking of making Java relevant and stronger in Cloud Native application, the Quarkus project was launched.
Quarkus
Quarkus is a Red Hat Initiative that features Cloud Native, Container first, Microservice ready framework for writing Java applications based on the standards and frameworks you are already using today (Hibernate, RESTEasy, Camel, Vert.X etc).
What does it mean?
- Container First: Minimal footprint Java applications optimal for running in containers.
- Cloud Native: Embraces 12 factor architecture in environments like Kubernetes.
- Microservice First: Brings lightning-fast startup time and code turn around to Java apps.
I think now we have a general understanding of why we are experimenting with Quarkus. In this blog, we would create a simple quarkus app and run it using the maven tool.
1. Our First Application
Requirements to build a Quarkus Java Applications:
- JDK 8 or 11+ installed with
JAVA_HOME
configured appropriately - Apache Maven 3.6.2+
- GraalVM (if native apps needed)
Quarkus applications can be quickly bootstrapped using Maven or Gradle plugin. The plugin will generate a minimal project structure with a sample REST Endpoint and the Quarkus’s Maven dependencies included in the configuration file.
If you prefer an interactive graphical user interface, you can navigate to code.quarkus.io to download a bootstrapped project with your specific requirements.
Lets assume you have maven installed, run the below command on the terminal :
mvn io.quarkus:quarkus-maven-plugin:1.12.2.Final:create \
-DprojectGroupId=com.praveen.samples.quarkus \
-DprojectArtifactId=sample-quarkus-app \
-DclassName="com.praveen.samples.quarkus.HelloResource" \
-Dpath="helloworld"
This will generate the project skeleton, a HelloResource with a /helloworld endpoint exposed, configuration, Maven project, and Dockerfiles.
Once imported into an IDE, we’ll have a structure similar to that shown in the image below:
Once imported successfully examine the contents of our resource file HelloResource
:
@Path("/helloworld")
public class HelloResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "Hello RESTEasy";
}
}
Looks good so far. Now we have a simple qurakus app with a single RESTEasy JAX-RS endpoint. You can find more details on the generated README file in the root. Let’s go ahead and test it by opening a terminal and running the command:
mvn compile quarkus:dev
Our REST endpoint should be exposed at http://localhost:8080/helloworld. you can hit the url with the browser or curl command, & you will see:
Hello RESTEasy
You would see Quarkus now ships with a Dev UI, which is available in dev mode only at http://localhost:8080/q/dev/. It allows you to quickly visualize all the extensions currently loaded, see their status and go directly to their documentation. For more details visit Dev UI.
2. Dependency Injection
Quarkus provides a hot-reload capability when running in development mode (mvn compile quarkus:dev). We don’t need to restart or do save changes explicitly. Let us create a service and inject into our sample application and see how hot reload and DI works.
First, we’ll create a HelloService class:
@ApplicationScoped
public class HelloService{
public String sayHello(String name){
return "Hello " + name;
}
}
Now, we’ll modify the HelloResource class, injecting the HelloService and adding a new method:
@Inject
HelloService helloService; @GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/hello/{name}")
public String greet(@PathParam("name") String name) {
return helloService.sayHello(name);
}
Next, let’s test our new endpoint: http://localhost:8080/helloworld/hello/jasmine
Hello jasmine
We’ll make one more change to demonstrate that the same can be applied to property files. Let’s edit the application.properties file and add one more key:
greet-message=Good day
After that, we’ll modify the HelloService to use our new property:
@ConfigProperty(name = "greet-message")
private String greetMessage;public String sayHello(String name) {
return greetMessage + " " + name;
}
If we hit the same url again, we should now see:
Good day jasmine
3. Package Application
Now that we have tried some features, it is time to package the application using JVM. You could run the below command to package the app.
mvn package
This will generate 2 jar files inside the target directory:
- /quarkus-app/quarkus-run.jar — an executable jar with the dependencies copied to target/lib.
- /sample-quarkus-app-1.0-SNAPSHOT.jar — contains classes and resource files.
Run the packaged application:
java -jar target/quarkus-app/quarkus-run.jar
Wohoo !! we just started a Quarkus app in JVM mode.
4. GraalVM and Native App
Next, we’ll produce a native image of our application. A native image will improve start-up time and time to first response. In other words, it contains everything it needs to run, including the minimal JVM necessary to run the application.
Installing GraalVM
To start with, we need to have GraalVM installed which is a high-performance, embeddable, polyglot virtual machine for running applications written in JavaScript, Python, Ruby, R, JVM-based languages like Java, Scala, Kotlin, and LLVM-based languages such as C and C++.
GraalVM releases are available at: https://github.com/graalvm/graalvm-ce-builds/releases
With the Quarkus version 1.12.2.Final it is recommend installing GraalVM 21 with JDK 11 support (graalvm-ce-java11–21.0.0).
Download GraalVM and unzip it in a folder of your likes. Within the GraalVM package you will find:
- A JVM
- A JavaScript engine & node.js runtime
- A LLVM engine
Now export the GRAALVM_HOME with the path where GraalVM was installed:
export GRAALVM_HOME=/path/to/graal
Next, we need the native-image tool which allows you to ahead-of-time compile Java code to a standalone executable. You can install the native-image tool as follows:
${GRAALVM_HOME}/bin/gu install native-image
For more details on GraalVM installation visit install-graalvm.
Build Native Executable
Now that we have configured graalvm, we can do native package of our app. We have native
profile already defined in pom.xml. We’ll now stop the application (Ctrl + C), if not stopped already, and run the command:
mvn package -Pnative
This can take a few seconds to complete. Because native images try to create all code AOT(Ahead of Time) to boot faster, as a result, we’ll have a bit longer build times.
Run the below command to verify that our native artifact is properly constructed.
mvn verify -Pnative
If the result is success then your artifact is all good. After the package and verify command you would see a native executable runner file in your target folder. To run a native executable directly just execute below in terminal.
./sample-quarkus-app-1.0.0-SNAPSHOT-runner
Bingo !! Our Quarkus Native App just started. Now see the difference between the previous JVM app startup time and native app startup time.
JVM mode Start up : 1.219s
Native mode Start up : 0.015s
~98% reduction in startup time. Isn’t that impressive !!!
5. Docker Build
Now that we have our native artifact ready, we’ll create an image using our native artifact. For that, we must have a container runtime (i.e. Docker) running in our machine.
In order to build a native container image of a Quarkus application, first build the application using native profile and container-build as follows:
mvn package -Pnative -Dquarkus.native.container-build=true
The above command will take some time to complete...
Now If you see when we created the project, it created a docker file for us Dockerfile.native, which has minimal requirements to run the app. if you want to change the base OS you can modify the docker file as required.
The provided
Dockerfiles
use UBI (Universal Base Image) as parent image. This base image has been tailored to work perfectly in containers. TheDockerfiles
use the minimal version of the base image to reduce the size of the produced image. For more details visit here.
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3
WORKDIR /work/
RUN chown 1001 /work \
&& chmod "g+rwX" /work \
&& chown 1001:root /work
COPY --chown=1001:root target/*-runner /work/application
EXPOSE 8080
USER 1001
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]
Now run the below cmd to build a docker image:
docker build -f src/main/docker/Dockerfile.native -t quarkus/sample-quarkus-app .
This would have created the image. Run docker images to confirm. Now run the container using below:
docker run -i --rm -p 8080:8080 quarkus/sample-quarkus-app
The container started at an incredibly low time of 0.041s.
Now we know that's one of the strengths of Quarkus. Now our image can be shipped anywhere Openshift, Kubernetes etc.
Finally, we can test our REST endpoint to validate our application. Hit http://localhost:8080/helloworld/hello/jasmine
Good day jasmine
Summary
In this blog, we have seen that Quarkus is a great addition that can bring Java more effectively to the cloud, and most importantly we don’t need to learn anything new, after all it's all Java !! There are a lot of new features getting added daily in Quarkus so you can try and keep watch on them at the Quarkus GitHub repository.
I have shared the code for the above sample app we tried here. Happy learning !!
References: