Quarkus qualifies itself as SUPERSONIC/SUBATOMIC/JAVA partly thanks to its support for GraalVM. This post shows how to build a GraalVM native image for Linux from using a container, allowing to perform this task from Windows or macOS.
Motivation
GraalVM is a high performance JDK distribution which brings many interesting features such as supporting non JVM languages (such as Python). Among the features of this JDK, the one that we're interested in here is called native image, which compiles Java code into a native binary. This means that the resulting binary will contain machine code instead of the traditional bytecode. Theoretically, we would gain in performance and startup speed.
Quarkus allows to generate a native image for the current platform quite easily. However, since most servers run on Linux, we would like to find the simplest way to generate a native Linux binary. Unfortunately, GraalVM does not seem to support cross-compilation yet. Thus, we need to find other alternatives. Hopefully, Quarkus provide a very accessible technique, described here, which requires to have a container engine installed and does not require to install GraalVM.
In this post, we'll try out to use a container to build the native image and we'll use podman as the container engine.
Prerequisites
Please read my previous post which describes how to install the prerequisites which are summarized as follows:
- Java JDK 17
- podman
- On Windows, WSL needs to be installed and enabled
You can verity that you're good to go by running these commands.
java --version
podman run hello-world
We don't need to install GraalVM nor any native compilation toolchain as they will be handled by the container which will build the native image.
That's what makes this technique so easy !.
Sample project
We're going to use the sample project developed during a previous post. It consists of a Quarkus Rest API that uses a PostgreSQL database for development and a H2 in-memory database for production. For the sake of simplification, H2 is chosen in because it makes the production app autonomous and easier to test. The application.yaml file is shown below:
greeting:
message: "hello"
quarkus:
datasource:
db-kind: PostgreSQL
"%prod":
quarkus:
datasource:
db-kind: H2
jdbc:
url: jdbc:h2:mem:default
After cloning the project, please run the following commands to verify that we're ready to go:
- Run tests:
.\gradlew test
or in Windows.\gradlew.bat test
- Run the server in production mode:
.\gradlew quarkusDev -Dquarkus.profile=prod
The backtick character is required on PowerShell for arguments that have a dot .. So, -Dquarkus.profile=prod
becomes (backtick)-Dquarkus.profile=prod
in PowerShell, and so on.
Checking the production profile locally is recommended because the native image will use that profile.
Once you're ready, let's create the native image.
Creating the native image using a container
Open a terminal on the project folder and run this command:
.\gradlew build -Dquarkus.package.type=native -Dquarkus.native.container-build=true -Dquarkus.native.container-runtime=podman
This argument -Dquarkus.native.container-runtime=podman
allows to use podman as a container tool.
The command should finish with an output similar to this one:
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:
/project/testcontainers-demo-1.0.0-SNAPSHOT-runner (executable)
/project/testcontainers-demo-1.0.0-SNAPSHOT-runner-build-output-stats.json (json)
/project/testcontainers-demo-1.0.0-SNAPSHOT-runner-timing-stats.json (raw)
/project/testcontainers-demo-1.0.0-SNAPSHOT-runner.build_artifacts.txt (txt)
========================================================================================================================
Finished generating 'testcontainers-demo-1.0.0-SNAPSHOT-runner' in 3m 34s.
The native app (also called native image) is /project/testcontainers-demo-1.0.0-SNAPSHOT-runner
Running the Linux app from other OSes
We'll be exploring two techniques even if there may be more, such using a virtual machine.
Using WSL on Windows
On windows, it's quite easy to test thanks to WSL:
wsl # open a WSL session
./build/testcontainers-demo-1.0.0-SNAPSHOT-runner
The output should display this:
__ ____ __ _____ ___ __ ____ ______
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2022-12-06 22:42:05,809 INFO [io.quarkus] (main) testcontainers-demo 1.0.0-SNAPSHOT native (powered by Quarkus 2.14.2.Final) started in 0.942s. Listening on: http://0.0.0.0:8
080
2022-12-06 22:42:05,828 INFO [io.quarkus] (main) Profile prod activated.
2022-12-06 22:42:05,828 INFO [io.quarkus] (main) Installed features: [agroal, cdi, config-yaml, hibernate-orm, hibernate-orm-panache, hibernate-orm-panache-kotlin, jdbc-h2, j
dbc-postgresql, kotlin, narayana-jta, resteasy-reactive, resteasy-reactive-jackson, resteasy-reactive-kotlin-serialization, smallrye-context-propagation, vertx]
^C2022-12-06 22:42:12,136 INFO [io.quarkus] (Shutdown thread) testcontainers-demo stopped in 0.012s
This means that the server is up and running.
Using a container
We can also test the native Linux app by running it from a container. This allows to run the app from any OS as long as there is a tool that runs containers. But, we need to first create the image.
Hopefully, projects generated using Quarkus starter already provides the configuration file, located in src/main/docker/Dockerfile.native-micro that is used by the container tool in order to generate the image that hosts and runs the native Linux app. It just requires that the native Linux app is already generated, which we already did. So, let's run this command:
podman build -f src/main/docker/Dockerfile.native-micro -t IMAGE_NAME .
Please replace IMAGE_NAME with the desired name of the image, such as: myapp/native-server
.
You can test the image by running podman run IMAGE_NAME_OR_ID
and upload it to a container registry by running podman push IMAGE_NAME_OR_ID REGISTRY_NAME
. IMAGE_NAME_OR_ID REGISTRY_NAME corresponds to either the name of the image that you used earlier, or its ID that you can get by running podman images
.
If running the image fails, it may be due to a missing execution permission for the native app. Please add a RUN chmod +x /work/application
in the dockerfile src/main/docker/Dockerfile.native-micro to fix this. The file that I used is shown below:
FROM quay.io/quarkus/quarkus-micro-image:1.0
WORKDIR /work/
RUN chown 1001 /work \
&& chmod "g+rwX" /work \
&& chown 1001:root /work
COPY --chown=1001:root build/*-runner /work/application
RUN chmod +x /work/application
EXPOSE 8080
USER 1001
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]
You can run my generated image hosted on quay.io as an example: podman run quay.io/yostane/quarkus-jvm-demo/quarkus-jvm-demo-micro
.
podman run quay.io/yostane/quarkus-jvm-demo/quarkus-jvm-demo-micro
__ ____ __ _____ ___ __ ____ ______
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2022-12-07 20:19:51,660 INFO [io.quarkus] (main) testcontainers-demo 1.0.0-SNAPSHOT native (powered by Quarkus 2.14.2.Final) started in 0.357s. Listening on: http://0.0.0.0:8080
2022-12-07 20:19:51,669 INFO [io.quarkus] (main) Profile prod activated.
2022-12-07 20:19:51,669 INFO [io.quarkus] (main) Installed features: [agroal, cdi, config-yaml, hibernate-orm, hibernate-orm-panache, hibernate-orm-panache-kotlin, jdbc-h2, jdbc-postgresql, kotlin, narayana-jta, resteasy-reactive, resteasy-reactive-jackson, resteasy-reactive-kotlin-serialization, smallrye-context-propagation, vertx]
Conclusion
This post showed how to generate a native Linux image of a Quarkus application in a very simple and straightforward way. This native app does not a require a JRE and is "theoretically" faster and more optimized than a JVM counterpart.
We used a container to generated the image which handled all the native complication toolchain. It also allowed to cross-compile to Linux. I was really surprised to see how it's easy to generate a native image using this technique. So, why not try it out on your side ?
Top comments (0)