Quarkus - Building a Native Executable

This guide covers:

  • Compiling the application to a native executable

  • The packaging of the application in a Docker container

This guide takes as input the application developed in the Getting Started Guide.

Prerequisites

To complete this guide, you need:

  • less than 15 minutes

  • an IDE

  • JDK 1.8+ installed with JAVA_HOME configured appropriately

  • GraalVM installed from the GraalVM web site. Using the community edition is enough. Version 1.0.0-rc13 is required.

  • The GRAALVM_HOME environment variable configured appropriately

  • Apache Maven 3.5.3+

  • A running Docker

  • The code of the application developed in the Getting Started Guide.

Note

Once you have downloaded GraalVM, expand the archive and set the GRAALVM_HOME variable to this location:

export GRAALVM_HOME=$HOME/Development/graalvm/

On MacOS, point the variable to the Home sub-directory:

export GRAALVM_HOME=$HOME/Development/graalvm/Contents/Home/

Solution

We recommend that you follow the instructions in the next sections and package the application step by step. However, you can go right to the completed example.

Clone the Git repository: git clone https://github.com/quarkusio/quarkus-quickstarts.git, or download an archive.

The solution is located in the getting-started directory.

Producing a native executable

Let’s now produce a native executable for our application. It improves the startup time of the application, and produces a minimal disk footprint. The executable would have everything to run the application including the "JVM" (shrunk to be just enough to run the application), and the application.

Creating a native executable

Before going further, be sure that the GRAALVM_HOME environment variable is configured appropriately.

If you have generated the application from the previous tutorial, you can find in the pom.xml the following profile:

<profiles>
    <profile>
        <id>native</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>io.quarkus</groupId>
                    <artifactId>quarkus-maven-plugin</artifactId>
                    <version>${quarkus.version}</version>
                    <executions>
                        <execution>
                            <goals>
                                <goal>native-image</goal>
                            </goals>
                            <configuration>
                                <enableHttpUrlHandler>true</enableHttpUrlHandler>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

We use a profile because, you will see very soon, packaging the native executable takes a few seconds.

Create a native executable using: ./mvnw package -Pnative.

In addition to the regular files, the build also produces target/quarkus-quickstart-runner. You can run it using: ./target/quarkus-quickstart-runner.

Testing the native executable

Producing a native executable can lead to a few issues, and so it’s also a good idea to run some tests against the application running in the native file.

In the pom.xml file, the native profile contains:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-failsafe-plugin</artifactId>
    <version>${surefire.version}</version>
    <executions>
        <execution>
            <goals>
                <goal>integration-test</goal>
                <goal>verify</goal>
            </goals>
            <configuration>
                <systemProperties>
                    <native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
                </systemProperties>
            </configuration>
        </execution>
    </executions>
</plugin>

This instructs the failsafe-maven-plugin to run integration-test and indicates the location of the produced native executable.

Then, open the src/test/java/org/acme/quickstart/NativeGreetingResourceIT.java. It contains:

package org.acme.quickstart;


import io.quarkus.test.junit.SubstrateTest;

@SubstrateTest (1)
public class NativeGreetingResourceIT extends GreetingResourceTest { (2)

    // Run the same tests

}
  1. Use another test runner that starts the application from the native file before the tests. The executable is retrieved using the native.image.path system property configured in the Failsafe Maven Plugin.

  2. We extend our previous tests, but you can also implement your tests

To see the NativeGreetingResourceIT run against the native executable, runs ./mvnw verify -Pnative:

./mvnw verify -Pnative
...
[quarkus-quickstart-runner:50955]     universe:     391.96 ms
[quarkus-quickstart-runner:50955]      (parse):     904.37 ms
[quarkus-quickstart-runner:50955]     (inline):   1,143.32 ms
[quarkus-quickstart-runner:50955]    (compile):   6,228.44 ms
[quarkus-quickstart-runner:50955]      compile:   9,130.58 ms
[quarkus-quickstart-runner:50955]        image:   2,101.42 ms
[quarkus-quickstart-runner:50955]        write:     803.18 ms
[quarkus-quickstart-runner:50955]      [total]:  33,520.15 ms
[INFO]
[INFO] --- maven-failsafe-plugin:2.22.0:integration-test (default) @ quarkus-quickstart-native ---
[INFO]
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running org.acme.quickstart.GreetingResourceIT
Executing [.../getting-started/target/quarkus-quickstart-runner, -Dquarkus.http.port=8081, -Dtest.url=http://localhost:8081, -Dquarkus.log.file.path=target/quarkus.log]
2019-02-28 16:52:42,020 INFO  [io.quarkus] (main) Quarkus started in 0.007s. Listening on: http://localhost:8080
2019-02-28 16:52:42,021 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.081 s - in org.acme.quickstart.GreetingResourceIT
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0

...

Producing a Docker container

Important
Before going further, be sure to have a working Docker environment.

You can run the application in a Docker container using the JAR produced by the Quarkus Maven Plugin. However, in this guide we focus on creating a Docker image using the produced native executable.

Containerization Process

By default, the native executable is tailored for your operating system (Linux, macOS, Windows etc). Because the Docker container may not use the same executable format as the one produced by your operating system, we will instruct the Maven build to produce an executable from inside a Docker container:

./mvnw package -Pnative -Dnative-image.docker-build=true

The produced executable will be a 64 bit Linux executable, so depending on your operating system it may no longer be runnable. However, it’s not an issue as we are going to copy it to a Docker container. The project generation has provided a Dockerfile.native in the src/main/docker directory with the following content:

FROM registry.fedoraproject.org/fedora-minimal
WORKDIR /work/
COPY target/*-runner /work/application
RUN chmod 775 /work
EXPOSE 8080
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

Then, if you didn’t delete the generated native executable, you can build the docker image with:

docker build -f src/main/docker/Dockerfile.native -t quarkus-quickstart/quickstart .

And finally, run it with:

docker run -i --rm -p 8080:8080 quarkus-quickstart/quickstart
Note
Interested by tiny Docker images, check the distroless version.

What’s next?

This guide covered the creation of a native (binary) executable for your application. It provides an application exhibiting a swift startup time and consuming less memory. However, there is much more. We recommend continuing the journey with the deployment to Kubernetes and OpenShift.