A logo showing the text blog.marcnuri.com
Español
Home»Java»Quarkus + Fabric8 Maven Plugin + GraalVM integration

Recent Posts

  • Fabric8 Kubernetes Client 7.2 is now available!
  • Connecting to an MCP Server from JavaScript using AI SDK
  • Connecting to an MCP Server from JavaScript using LangChain.js
  • The Future of Developer Tools: Adapting to Machine-Based Developers
  • Connecting to a Model Context Protocol (MCP) Server from Java using LangChain4j

Categories

  • Artificial Intelligence
  • Front-end
  • Go
  • Industry and business
  • Java
  • JavaScript
  • Legacy
  • Operations
  • Personal
  • Pet projects
  • Tools

Archives

  • May 2025
  • April 2025
  • March 2025
  • February 2025
  • January 2025
  • December 2024
  • November 2024
  • August 2024
  • June 2024
  • May 2024
  • April 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • November 2023
  • October 2023
  • September 2023
  • August 2023
  • July 2023
  • June 2023
  • May 2023
  • April 2023
  • March 2023
  • February 2023
  • January 2023
  • December 2022
  • November 2022
  • October 2022
  • September 2022
  • August 2022
  • July 2022
  • June 2022
  • May 2022
  • March 2022
  • February 2022
  • January 2022
  • December 2021
  • November 2021
  • October 2021
  • September 2021
  • August 2021
  • July 2021
  • January 2021
  • December 2020
  • November 2020
  • October 2020
  • September 2020
  • August 2020
  • July 2020
  • June 2020
  • May 2020
  • February 2020
  • January 2020
  • December 2019
  • October 2019
  • September 2019
  • July 2019
  • March 2019
  • November 2018
  • July 2018
  • June 2018
  • May 2018
  • April 2018
  • March 2018
  • February 2018
  • December 2017
  • July 2017
  • January 2017
  • December 2015
  • November 2015
  • December 2014
  • March 2014
  • February 2011
  • November 2008
  • June 2008
  • May 2008
  • April 2008
  • January 2008
  • November 2007
  • September 2007
  • August 2007
  • July 2007
  • June 2007
  • May 2007
  • April 2007
  • March 2007

Quarkus + Fabric8 Maven Plugin + GraalVM integration

2019-10-27 in Java / Legacy tagged Docker / Fabric8 / GitHub Actions / GraalVM / Java / Maven / Native / Quarkus by Marc Nuri | Last updated: 2025-01-25
Versión en Español

Note

Fabric8 Maven Plugin has been deprecated and migrated to Eclipse JKube.

You can find an updated version of this article using Eclipse JKube here.

Introduction

In this tutorial, we’ll see how to develop and integrate a very simple Quarkus application with Fabric8 Maven Plugin in order to publish a native GraalVM image into Docker Hub.

The first part of the tutorial describes how to build a very simple Quarkus application. The next part describes how to build a Quarkus native executable with GraalVM. The last section shows how to integrate the project with Fabric8 Maven Plugin and how to deploy the application images into Docker Hub.

Quarkus example application

This section describes how to build a simple application that will return a random quote each time you perform a request to /quotes/random endpoint.

Project bootstrapping

If you have maven installed, the easiest way to bootstrap the project is to run the following command:

bash
mvn io.quarkus:quarkus-maven-plugin:0.26.1:create \\
    -DprojectGroupId=com.marcnuri.demo \\
    -DprojectArtifactId=fmp-quarkus \\
    -DclassName="com.marcnuri.demo"

This will create a new directory fmp-quarkus with an initial maven project with maven wrapper support.

If you don’t have maven installed, or if you prefer an interactive graphical user interface, you can navigate to code.quarkus.io to download a bootstrapped project with your specific requirements.

Quarkus project bootstrap (code.quarkus.io)
Quarkus project bootstrap (code.quarkus.io)

Project resources

As already explained, the application will serve a random quote each time a user performs a request to an endpoint. These quotes will be loaded from a JSON file located in the project resources folder. For this purpose, we’ll add the file quotes.json into the directory src/main/resources/quotes/.

Random quote endpoint

The first step is to create a Quote Pojo that will be used to map the quotes in the JSON file when it’s deserialized.

Quote.java
public class Quote implements Serializable {
  /* ... */
  private String content;
  private String author;
  /* ... */
}

In order to read the quotes from the resources directory and to expose a random quote, we’ll create a QuoteService class.

QuoteService.java
@Singleton
public class QuoteService {

  private static final Logger log = LoggerFactory.getLogger(QuoteService.class);

  private static final String QUOTES_RESOURCE= "/quotes/quotes.json";

  private final List<Quote> quotes;

  public QuoteService() {
    quotes = new ArrayList<>();
  }

  @PostConstruct
  protected final void initialize() {
    final ObjectMapper objectMapper = new ObjectMapper();
    try (final InputStream quotesStream = QuoteService.class.getResourceAsStream(QUOTES_RESOURCE)) {
      quotes.addAll(objectMapper.readValue(quotesStream,
          objectMapper.getTypeFactory().constructCollectionType(List.class, Quote.class)));
    } catch (IOException e) {
      log.error("Error loading quotes", e);
    }
  }


  Quote getRandomQuote() {
    return quotes.get(ThreadLocalRandom.current().nextInt(quotes.size()));
  }

}

The initialize method will use Jackson to read and deserialize the quotes.json file into an ArrayList that will be used later on to fetch a random quote.

The getRandomQuote method will return a random Quote entry from the ArrayList.

The last step is to actually expose the Quotes through a REST endpoint. For that purpose, we are going to create a QuoteResource class.

QuoteResource.java
@Path("/quotes")
public class QuoteResource {

  private static final String HEADER_QUOTE_AUTHOR = "Quote-Author";

  private QuoteService quoteService;

  @GET
  @Path("/random")
  public Response getRandomQuote() {
    final Quote randomQuote = quoteService.getRandomQuote();
    return Response
        .ok(randomQuote.getContent(), MediaType.TEXT_PLAIN_TYPE)
        .header(HEADER_QUOTE_AUTHOR, randomQuote.getAuthor())
        .build();
  }

  @Inject
  public void setQuoteService(QuoteService quoteService) {
    this.quoteService = quoteService;
  }
}

The class has a method getRandomQuote that will use an instance of the previously described QuoteService to get a random quote and return its content in the HTTP response body. The author of the quote will also be available as a Response header.

Once we complete all the steps, we can start the application in development mode using the following command:

bash
./mvnw clean quarkus:dev
mvnw clean quarkus:dev
mvnw clean quarkus:dev

The endpoint will now be accessible at http://localhost:8080/quotes/random.

curl localhost:8080/quotes/random
curl localhost:8080/quotes/random

Building a native executable with GraalVM

The next step will be to build the application using GraalVM to create a native image.

The first step will be to adapt the application to make it compatible with GraalVM.

Include resources

By default, GraalVM won’t include any of the resources available on the classpath during image creation using native-image. Resources that must be available at runtime must be specifically included during image creation.

For this purpose, we’re going to modify our default pom.xml file and include the following line:

pom.xml
<additionalBuildArgs> -H:IncludeResources=.*\\.json$</additionalBuildArgs>

This line will indicate Quarkus to add the -H:IncludeResources command-line flag to the native-image command. In this case, we want to add any file that ends in .json.

Native image reflection

Jackson JSON deserialization will use reflection to create instances of the target classes when performing reads. Graal native image build requires to know ahead of time which kind of elements are reflectiveley accessed in the program.

Quarkus eases this task by providing a @RegisterForReflection annotation that automates this task. For our example application, we’ll need to annotate the Quote class.

Building the application

Once all modifications have been completed, we can now perform the build of our application in native mode. If Graal VM with native-image support is available in our system, we can simply run the following command:

bash
./mvnw clean package -Pnative
mvn clean package -Pnative
mvn clean package -Pnative

This will create a native executable in the target directory.

We can now run the application natively:

fmp-quarkus-runner
fmp-quarkus-runner

The endpoint will again be accessible at http://localhost:8080/quotes/random.

If GraalVM is not available in your system, but Docker is, the same command can be run through a Docker container:

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

Fabric8 Maven Plugin integration

The last step in the tutorial is to integrate the build process with Fabric8 Maven Plugin.

We are going to build (fabric8:build) 2 different Docker images depending on the profile we are running. For this purpose, we are going to reuse de the Docker files that were generated when the application was bootstrapped. The bootstrap application contains two different Docker files in src/main/docker directory one to create a Docker image that runs the Quarkus application in JVM mode and another that runs the application in native mode.

Docker image running application with JVM

We are going to modify the default build of our pom.xml to build a Docker image that runs our application like any other regular Java application using a Java Virtual Machine. In this case, we are going to use the file Dockerfile.jvm.

In the plugin section of the default build we’ll insert the following lines:

pom.xml
<build>
  <plugins>
    <!-- ... -->
    <plugin>
      <groupId>io.fabric8</groupId>
      <artifactId>fabric8-maven-plugin</artifactId>
      <version>\${fmp.version}</version>
      <configuration>
        <images>
          <image>
            <name>marcnuri/fmp-quarkus:jvm</name>
            <build>
              <contextDir>\${project.basedir}</contextDir>
              <dockerFile>src/main/docker/Dockerfile.jvm</dockerFile>
            </build>
          </image>
        </images>
        <authConfig>
          <username>\${env.DOCKER_HUB_USER}</username>
          <password>\${env.DOCKER_HUB_PASSWORD}</password>
        </authConfig>
      </configuration>
    </plugin>
  </plugins>
</build>

The first part (groupId, artifactId & version) indicate that we want to use the Fabric8 Maven Plugin. In many cases, this may just be enough, as the plugin has a Zero-Config setup that works with some opinionated defaults.

Next, we add a specific configuration for our image. First, we define the image name: marcnuri/fmp-quarkus:jvm. This will create an image for the marcnuri repository with the name fmp-quarkus and jvm tag. It’s important to add the tag this way, otherwise, the image will also be tagged with latest (see native image build configuration).

We are also specifying the dockerFile we want to use in combination with the contextDir (project base directory).

Finally, we add the credentials for Docker Hub in the authConfig section. The Maven goal to push the image will only be used through GitHub Actions CI, credentials will be available in the specified environment variables.

We can now run the following command to build the Docker image:

bash
./mvnw clean package fabric8:build
mvnw clean package fabric8:build
mvnw clean package fabric8:build
docker images 'marcnuri/fmp-quarkus'
docker images 'marcnuri/fmp-quarkus'

Docker image running application in native mode

Following the steps defined to build the JVM Docker image, we are going to add the plugin to the native profile with some specific settings:

pom.xml
<profiles>
  <profile>
    <id>native</id>
    <!-- ... -->
    <build>
      <plugins>
        <!-- ... -->
        <plugin>
          <groupId>io.fabric8</groupId>
          <artifactId>fabric8-maven-plugin</artifactId>
          <version>\${fmp.version}</version>
          <configuration>
            <images>
              <image>
                <name>marcnuri/fmp-quarkus:native</name>
                <build>
                  <contextDir>\${project.basedir}</contextDir>
                  <dockerFile>src/main/docker/Dockerfile.native</dockerFile>
                  <tags>
                    <tag>latest</tag>
                  </tags>
                </build>
              </image>
            </images>
            <authConfig>
              <username>\${env.DOCKER_HUB_USER}</username>
              <password>\${env.DOCKER_HUB_PASSWORD}</password>
            </authConfig>
          </configuration>
        </plugin>
      </plugins>
    </build>
  </profile>
</profiles>

In this case, we are going to modify the configuration for the image. Firstly, we are going to replace the image name with marcnuri/fmp-quarkus:native so that it gets tagged as native. We are going to use Dockerfile.native as the dockerFile.

We also want this image to get the latest tag, so we are going to manually add this tag in the tags section. The same result would be achieved if the image name contained no tag (i.e. marcnuri/fmp-quarkus) and added the native tag manually.

We can now run the following command to build the native Docker image:

bash
./mvnw clean package fabric8:build -Pnative
mvn clean package fabric8:build -Pnative
mvn clean package fabric8:build -Pnative
docker images 'marcnuri/fmp-quarkus' (native)
docker images 'marcnuri/fmp-quarkus' (native)

fabric8:push Push image to Docker Hub

If we have the appropriate (authentication) environment variables we can now run the following command to push the image into Docker Hub repository:

bash
./mvnw fabric8:push

In our case we are going to run this from a GitHub Actions Workflow:

GitHub Actions Workflow - mvn fabric8:push
GitHub Actions Workflow - mvn fabric8:push

Conclusion

This post shows how to build a very simple Quarkus application and how to publish a Docker image running a GraalVM native executable into Docker Hub using Fabric8 Maven Plugin. In the first section, we learnt how to build a very simple REST endpoint that will return a random quote for each request. The second step shows how to adapt our application so that it can be built into a native executable using GraalVM. Finally, we integrated the Fabric8 Maven plugin into our pom.xml file in order to build and publish two different Docker images.

The full source code for this post can be found at Github.

Quarkus + GraalVM + Fabric8 Maven Plugin
Quarkus + GraalVM + Fabric8 Maven Plugin
Twitter iconFacebook iconLinkedIn iconPinterest iconEmail icon

Comments in "Quarkus + Fabric8 Maven Plugin + GraalVM integration"

  • Avatar for Peter G
    Peter G
    2020-06-09 15:02
    First mvn command to initiate project fails with lots of errors
    • Avatar for Marc Nuri
      Marc Nuri
      2020-06-12 05:47
      The article is outdated, I need to update it to use Eclipse JKube (Successor to the deprecated Fabric8 Maven Plugin).

      It's probably failing with newer versions.

      Anyway, if you're referring to the maven command to create project from archetype, it does work for me:
      C:\Users\Marc\Downloads\delete>mvn io.quarkus:quarkus-maven-plugin:0.26.1:create  -DprojectGroupId=com.marcnuri.demo  -DprojectArtifactId=fmp-quarkus  -DclassName="com.marcnuri.demo"
      [INFO] Scanning for projects...
      [INFO]
      [INFO] ------------------< org.apache.maven:standalone-pom >-------------------
      [INFO] Building Maven Stub Project (No POM) 1
      [INFO] --------------------------------[ pom ]---------------------------------
      [INFO]
      [INFO] --- quarkus-maven-plugin:0.26.1:create (default-cli) @ standalone-pom ---
      Creating a new project in C:\Users\Marc\Downloads\delete\.
      [INFO]
      [INFO] Maven Wrapper version 0.5.5 has been successfully set up for your project.
      [INFO] Using Apache Maven: 3.6.2
      [INFO] Repo URL in properties file: https://repo.maven.apache.org/maven2
      [INFO]
      [INFO]
      [INFO] ========================================================================================
      [INFO] Your new application has been created in C:\Users\Marc\Downloads\delete\.
      [INFO] Navigate into this directory and launch your application with mvn compile quarkus:dev
      [INFO] Your application will be accessible on http://localhost:8080
      [INFO] ========================================================================================
      [INFO]
      [INFO] ------------------------------------------------------------------------
      [INFO] BUILD SUCCESS
      [INFO] ------------------------------------------------------------------------
      [INFO] Total time:  7.712 s
      [INFO] Finished at: 2020-06-12T07:46:36+02:00
      [INFO] ------------------------------------------------------------------------

Post navigation
Triggering GitHub Actions across different repositoriesIsotope Mail: How to deploy Isotope+Traefik into Kubernetes
© 2007 - 2025 Marc Nuri