Here is our second tutorial about QuarkusIO. Today we will learn how to create native executable applications with amazingly fast start up time. Next we will create a Container image that will be eventually deployed on the top of Openshift
If you have already had a look at our first tutorial Getting started with Quarkus, you could see that Quarkus's maven plugin creates for you a native profile which can be used to build native applications:
<dependencyManagement> <dependencies> <dependency> <groupId>org.hibernate.ogm</groupId> <artifactId>hibernate-ogm-bom</artifactId> <version>5.4.1.Final</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>jakarta.platform</groupId> <artifactId>jakarta.jakartaee-api</artifactId> <scope>provided</scope> <version>${jakartaee.version}</version> </dependency> <dependency> <groupId>org.hibernate.ogm</groupId> <artifactId>hibernate-ogm-mongodb</artifactId> </dependency> </dependencies>
Then, you can use the persistence.xml file just like any JPA application to point to your MongoDB Database:
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<systemPropertyVariables>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<quarkus.package.type>native</quarkus.package.type>
</properties>
</profile>
Installing GRAALVM
In order to build native images we need to install GraalVM, 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.9.2.Final it is recommend installing GraalVM 20.2 with JDK 11 support (graalvm-ce-java11-20.2.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
Building a Quarkus native application
We will now build the HelloWorld Quarkus application discussed in the first tutorial Getting started with Quarkus. Move to the root folder and build the native executable use the 'mvn verify -Pnative' command:
$ mvn verify -Pnative
You should see at the end of the process the following output:
__ ____ __ _____ ___ __ ____ ______
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2020-11-19 12:39:31,369 INFO [io.quarkus] (main) code-with-quarkus 1.0.0-SNAPSHOT native (powered by Quarkus 1.9.2.Final) started in 0.011s. Listening on: http://0.0.0.0:8081
2020-11-19 12:39:31,369 INFO [io.quarkus] (main) Profile prod activated.
2020-11-19 12:39:31,369 INFO [io.quarkus] (main) Installed features: [cdi, resteasy]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.918 s - in org.acme.NativeExampleResourceIT
Note: if you are getting this error "Basic header file missing ()" then it means that headers are not available on your system. You can install them using:
sudo apt-get install libz-dev
If you are using a Fedora/RHEL system, use instead:
sudo dnf install zlib-devel
Once that the native generation has been completed, check the target folder:
$ ls -al target
drwxrwxr-x. 15 francesco francesco 4096 Mar 25 18:37 .
drwxrwxr-x. 4 francesco francesco 4096 Mar 25 18:29 ..
drwxrwxr-x. 4 francesco francesco 4096 Mar 24 20:13 classes
drwxrwxr-x. 2 francesco francesco 4096 Mar 24 20:20 failsafe-reports
drwxrwxr-x. 3 francesco francesco 4096 Mar 18 09:47 generated-sources
drwxrwxr-x. 3 francesco francesco 4096 Mar 24 20:13 generated-test-sources
-rw-rw-r--. 1 francesco francesco 5209 Mar 25 18:36 hello-quarkus-1.0-SNAPSHOT.jar
-rwxrwxr-x. 1 francesco francesco 19740232 Mar 25 18:37 hello-quarkus-1.0-SNAPSHOT-runner
-rw-r--r--. 1 francesco francesco 36337 Mar 25 18:36 hello-quarkus-1.0-SNAPSHOT-runner.jar
The native executable hello-quarkus-1.0-SNAPSHOT-runner, which is a minimal REST Service, is just 19MB in size. Let's run it:
./hello-quarkus-1.0-SNAPSHOT-runner 2019-03-25 18:39:51,473 INFO [io.quarkus] (main) Quarkus 0.11.0 started in 0.007s. Listening on: http://127.0.0.1:8080 2019-03-25 18:39:51,474 INFO [io.quarkus] (main) Installed features: [cdi, resteasy]
As you can see, in a few milliseconds the application will boot. Test it with:
$ curl http://localhost:8080/hello
Building Docker images for Quarkus applications
It is amazing how simple is to build a Docker image out of your native executable application. Quarkus's Maven plugin already arranged for you two Dockerfile in the project folder src/main/docker:
src
├── main
│ ├── docker
│ │ ├── Dockerfile.jvm
│ │ └── Dockerfile.native
- The file Dockerfile.jvm can be used to build a Container image of Quarkus Java application.
- The file Dockerfile.native can be used to build a native Container image of Quarkus application.
Both Dockerfile can be used to build a Container image, starting from the following base image:
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1
On the top of that, the Dockerfile.jvm will install the JAR file containing the Java application, while the Dockerfile.native will add the native executable.
Building the JVM Docker image for a Quarkus application
In order to build a container image of a Quarkus application, first build the application as follows:
mvn package -Dquarkus.native.container-build=true
Then build the Container image from the Dockerfile.jvm:
docker build -f src/main/docker/Dockerfile.jvm -t quarkus/hello-quarkus .
Finally, run the application as follows:
docker run -i --rm -p 8080:8080 quarkus/hello-quarkus
Building the native Docker image for a Quarkus application
In order to build the Quarkus native container image, you will need to use the native profile, when packaging your application:
mvn package -Pnative -Dquarkus.native.container-build=true
Then, build the Quarkus Docker image "quarkus/hello-quarkus" as follows:
docker build -f src/main/docker/Dockerfile.native -t quarkus/hello-quarkus .
Now check that the image is available:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
quarkus/hello-quarkus latest 761947bb9d9d 25 seconds ago 125 MB
It's time to run it, with:
docker run -i --rm -p 8080:8080 quarkus/hello-quarkus
__ ____ __ _____ ___ __ ____ ______
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2020-11-19 11:41:31,908 INFO [io.quarkus] (main) code-with-quarkus 1.0.0-SNAPSHOT on JVM (powered by Quarkus 1.9.2.Final) started in 0.765s. Listening on: http://0.0.0.0:8080
2020-11-19 11:41:31,931 INFO [io.quarkus] (main) Profile prod activated.
2020-11-19 11:41:31,931 INFO [io.quarkus] (main) Installed features: [cdi, resteasy]
You can test the Quarkus Docker image with:
$ curl http://localhost:8080/resteasy/hello
Deploying Quarkus applications on Openshift
Once that you have the Container image of your application, it's very simple to deploy it on Openshift or Kubernetes. To keep it as simple as possible, we will use Code Ready Container as Openshift cluster.
Check the following tutorial to learn how to install Code Ready Containers on your laptop: Getting started with Code Ready Containers
Start crc with:
$ crc start
Create a sample project named "quarkus":
$ oc new-project quarkus
Now using project "quarkus" on server "https://192.168.42.204:8443".
Next, create a new Binary build named "quarkus-quickstart":
$ oc new-build --binary --name=quarkus-quickstart -l app=quarkus-quickstart
* A Docker build using binary input will be created
* The resulting image will be pushed to image stream tag "quarkus-quickstart:latest"
* A binary build was created, use 'start-build --from-dir' to trigger a new build
--> Creating resources with label app=quarkus-quickstart ...
imagestream.image.openshift.io "quarkus-quickstart" created
buildconfig.build.openshift.io "quarkus-quickstart" created
--> Success
We need to set the dockerfilePath parameter in our build, with the location where the Dockerfile is:
$ oc patch bc/quarkus-quickstart -p '{"spec":{"strategy":{"dockerStrategy":{"dockerfilePath":"src/main/docker/Dockerfile"}}}}'
buildconfig.build.openshift.io/quarkus-quickstart patched
Now we can start the build operation, that will push the Quarkus application on Openshift:
$ oc start-build quarkus-quickstart --from-dir=. --follow
Uploading directory "." as binary input for the build ...
>build.build.openshift.io/quarkus-quickstart-1 started
Receiving source from STDIN as archive ...
Pulling image registry.fedoraproject.org/fedora-minimal ...
Pulled 0/1 layers, 20% complete
Pulled 1/1 layers, 100% complete
Extracting
Step 1/8 : FROM registry.fedoraproject.org/fedora-minimal
---> f0c38118c459
Step 2/8 : WORKDIR /work/
---> 69b5e60840bc
Removing intermediate container 86df47c2a9c2
Step 3/8 : COPY target/*-runner /work/application
---> 75695835b4fe
Removing intermediate container 7cbd0029ae38
Step 4/8 : RUN chmod 775 /work
---> Running in edd7a212de68
---> 0856df0e56c8
Removing intermediate container edd7a212de68
Step 5/8 : EXPOSE 8080
---> Running in fde34fea6934
---> eb6112ea782c
Removing intermediate container fde34fea6934
Step 6/8 : CMD ./application -Dquarkus.http.host=0.0.0.0
---> Running in a7c2b6d751f3
---> 8140539071be
Removing intermediate container a7c2b6d751f3
Step 7/8 : ENV "OPENSHIFT_BUILD_NAME" "quarkus-quickstart-1" "OPENSHIFT_BUILD_NAMESPACE" "quarkus"
---> Running in 06a6f92a98e5
---> 1daf960db23d
Removing intermediate container 06a6f92a98e5
Step 8/8 : LABEL "io.openshift.build.name" "quarkus-quickstart-1" "io.openshift.build.namespace" "quarkus"
---> Running in 1a4f712b5df5
---> 07bd2b01cc12
Removing intermediate container 1a4f712b5df5
Successfully built 07bd2b01cc12
Pushing image 172.30.1.1:5000/quarkus/quarkus-quickstart:latest ...
Pushed 2/4 layers, 52% complete
Pushed 3/4 layers, 81% complete
Pushed 4/4 layers, 100% complete
Push successful
Now that the image "quarkus-quickstart:latest" is available in our local registry, you can add it as application with:
$ oc new-app --image-stream=quarkus-quickstart:latest
--> Found image 07bd2b0 (26 seconds old) in image stream "quarkus/quarkus-quickstart" under tag "latest" for "quarkus-quickstart:latest"
* This image will be deployed in deployment config "quarkus-quickstart"
* Port 8080/tcp will be load balanced by service "quarkus-quickstart"
* Other containers can access this service through the hostname "quarkus-quickstart"
* WARNING: Image "quarkus/quarkus-quickstart:latest" runs as the 'root' user which may not be permitted by your cluster administrator
--> Creating resources ...
deploymentconfig.apps.openshift.io "quarkus-quickstart" created
service "quarkus-quickstart" created
--> Success
Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:
'oc expose svc/quarkus-quickstart'
Run 'oc status' to view your app.
Great! the application has been created. Finish the work, by exposing the Service through a Route:
$ oc expose service quarkus-quickstart
route.route.openshift.io/quarkus-quickstart exposed
Testing the Openshift application
Last effort will be testing our application which now should be visible in the Web console:
Let's copy the route address from the shell:
$ oc get route
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
quarkus-quickstart quarkus-quickstart-quarkus.192.168.42.204.nip.io quarkus-quickstart 8080-tcp None
Now test it with:
$ curl http://quarkus-quickstart-quarkus.192.168.42.204.nip.io/hello
UPDATE:
If you want to learn more options for deploying Quarkus applications on OpenShift using Red Hat Code Ready Containers then check this tutorial: Deploying Quarkus applications on OpenShift
If you want to learn how to use Mandrel distribution to buiild native executables, then check this tutorial: Building Quarkus native applications with Mandrel
Conclusion
At the end of this brief journey through Quarkus applications, we have discovered how to build a simple application which can be compiled easily into native code. One major feature of Quarkus is the ability to create instantly Container images of your applications which can be deployed on Openshift or Kubernates.