A logo showing the text blog.marcnuri.com
English
Inicio»Java»Quarkus + Fabric8 Maven Plugin + GraalVM

Entradas Recientes

  • Fabric8 Kubernetes Client 7.2.0 está disponible!
  • Conectarse a un servidor MCP con JavaScript y AI SDK
  • Conectarse a un servidor MCP con JavaScript y LangChain.js
  • El Futuro de las Herramientas para Desarrolladores en la era de la IA
  • Conectarse a un servidor Model Context Protocol (MCP) con Java y LangChain4j

Categorías

  • Antiguo
  • Front-end
  • Go
  • Herramientas
  • Industria y negocios
  • Inteligencia Artificial
  • Java
  • JavaScript
  • Operaciones
  • Personal
  • Proyectos personales

Archivos

  • mayo 2025
  • abril 2025
  • marzo 2025
  • febrero 2025
  • enero 2025
  • diciembre 2024
  • noviembre 2024
  • agosto 2024
  • junio 2024
  • mayo 2024
  • abril 2024
  • marzo 2024
  • febrero 2024
  • enero 2024
  • diciembre 2023
  • noviembre 2023
  • octubre 2023
  • septiembre 2023
  • agosto 2023
  • julio 2023
  • junio 2023
  • mayo 2023
  • abril 2023
  • marzo 2023
  • febrero 2023
  • enero 2023
  • diciembre 2022
  • noviembre 2022
  • octubre 2022
  • agosto 2022
  • julio 2022
  • mayo 2022
  • marzo 2022
  • febrero 2022
  • enero 2022
  • diciembre 2021
  • noviembre 2021
  • octubre 2021
  • septiembre 2021
  • agosto 2021
  • julio 2021
  • diciembre 2020
  • octubre 2020
  • agosto 2020
  • junio 2020
  • mayo 2020
  • marzo 2020
  • febrero 2020
  • enero 2020
  • noviembre 2019
  • octubre 2019
  • julio 2019
  • diciembre 2018
  • agosto 2018
  • julio 2018
  • junio 2018
  • mayo 2018
  • marzo 2018
  • febrero 2018
  • noviembre 2017
  • octubre 2017
  • agosto 2017
  • julio 2017
  • enero 2017
  • julio 2016
  • enero 2016
  • diciembre 2015
  • noviembre 2015
  • diciembre 2014
  • marzo 2014
  • febrero 2011
  • junio 2008
  • mayo 2008
  • abril 2008
  • enero 2008
  • junio 2007
  • mayo 2007
  • abril 2007
  • marzo 2007

Quarkus + Fabric8 Maven Plugin + GraalVM

2019-11-26 en Java / Antiguo etiquetado Docker / Fabric8 / GitHub Actions / GraalVM / Java / Maven / Nativo / Quarkus por Marc Nuri | Última actualización: 2025-01-25
English version

Nota

El plugin Fabric8 Maven ha sido deprecado y migrado a Eclipse JKube.

Puedes encontrar una versión actualizada de este artículo utilizando Eclipse JKube aquí.

Introducción

En este tutorial veremos como desarrollar una aplicación muy sencilla con Quarkus e integrarla con Fabric8 Maven Plugin para publicar una imagen nativa con GraalVM en Docker Hub.

La primera parte de la publicación describe como desarrollar una simple aplicación con Quarkus. La siguiente parte describe como construir un ejecutable nativo con GraalVM. La última sección muestra como integrar el proyecto con Fabric8 Maven Plugin y cómo desplegar las diferentes imágenes de la aplicación en Docker Hub.

Quarkus, aplicación de muestra

En esta sección se muestra cómo desarrollar una aplicación muy sencilla que devolverá una cita al azar cada vez que se haga una petición al endpoint /quotes/random.

Proyecto inicial

Si Maven está instalado, la forma más sencilla de iniciar el proyecto es lanzando el siguiente comando:

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

Esto creará un nuevo directorio fmp-quarkus con un proyecto inicial de Maven con soporte para Maven Wrapper.

Si Maven no está disponible, o simplemente porque prefieres una interfaz gráfica, puedes navegar a code.quarkus.io y descargar un proyecto inicial con tus requisitos específicos.

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

Recursos del proyecto

Tal como se ha explicado, el sencillo propósito de la aplicación es devolver una cita al azar cada vez que un usuario haga una petición a un endpoint. Las citas se cargarán desde un fichero JSON situado en la carpeta de recursos del proyecto. Para ello, añadiremos el fichero quotes.json al directorio src/main/resources/quotes/.

Endpoint para obtener una cita al azar

El primer paso es crear un Pojo (Quote) que se empleará para “mapear” las citas del fichero JSON, añadido en el paso anterior, cuando éste se “deserialice”.

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

Para poder leer las citas desde el directorio de recursos y exponer una de ellas al azar, crearemos una clase QuoteService que se encargue de estas tareas.

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()));
  }

}

El método initialize utilizará Jackson para leer y deserializar el fichero quotes.json en un ArrayList que se empleará posteriormente para obtener una cita aleatoria.

El método getRandomQuote será el encargado de devolver una entrada Quote al azar desde el ArrayList inicializado en el método descrito en el párrafo anterior.

El último paso es exponer las citas a través de un endpoint REST. Para ello crearemos la clase QuoteResource.

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;
  }
}

La clase tiene un método getRandomQuote que empleará una instancia de QuoteService para obtener una cita al azar y devolver su contenido en el cuerpo de la respuesta HTTP. El autor de la cita también estará disponible en una de las cabeceras de la respuesta.

Una vez completados todos estos pasos, podemos iniciar la aplicación en modo desarrollo con el siguiente comando:

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

El endpoint estará ahora accesible en http://localhost:8080/quotes/random.

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

Construyendo un ejecutable nativo con GraalVM

El siguiente paso es construir la aplicación empleando GraalVM para crear una imagen nativa.

Lo primero será adaptar la aplicación para que sea compatible con GraalVM.

Incluir recursos

Por defecto, GraalVM no incluirá ninguno de los recursos disponibles en el classpath durante la creación de la imagen nativa. Los recursos que deban de estar disponibles en tiempo de ejecución deben de incluirse específicamente cuando se cree la imagen.

Con este propósito, modificaremos el fichero pom.xml de nuestra aplicación y añadiremos la siguiente línea:

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

Esto hará que Quarkus incluya la opción -H:IncludeResources en la línea de comandos cuando ejecute el comando native-image. En este caso le estamos diciendo que se incluyan todos los ficheros terminados en .json.

Java Reflection en imágenes nativas

La librería de Jackson encargada de la deserialización de JSONs emplea Java Reflection para crear instancias de las clases que se están deserializando durante la lectura del JSON. Para la construcción de imágenes nativas, Graal requiere saber antes de tiempo a qué tipo de elementos se accede mediante Java Reflection durante la ejecución de la aplicación.

Quarkus facilita esta tarea al proveernos de la anotación @RegisterForReflection que automatiza esta tarea. Para nuestra aplicación de ejemplo, anotaremos la clase Quote con ella.

Construyendo la aplicación

Una vez completadas todas las modificaciones, podemos proceder a la construcción de la aplicación en modo nativo. Si GraalVM con soporte para native-image está disponible en nuestro sistema, podemos ejecutar el siguiente comando:

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

Esto creará el ejecutable nativo en el directorio target.

Ahora podemos ejecutar nuestra aplicación de forma nativa:

fmp-quarkus-runner
fmp-quarkus-runner

El endpoint volverá a estar accesible nuevamente en http://localhost:8080/quotes/random.

Si GraalVM no está disponible en nuestro sistema, pero sí Docker, el mismo comando puede ejecutarse por medio de un contenedor de Docker:

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

Fabric8 Maven Plugin

El último paso del tutorial es integrar el proceso de construcción con Fabric8 Maven Plugin.

Vamos a construir (fabric8:build) 2 imágenes diferentes dependiendo del perfil de Maven que seleccionemos. Para ello, vamos a reutilizar los ficheros Docker que fueron generados junto al proyecto inicial. El proyecto inicial contiene dos ficheros Dockerfiles distintos en el directorio src/main/docker, uno que crea una imagen que ejecuta la aplicación Quarkus en modo JVM y otro que ejecuta la aplicación en modo nativo.

Imagen Docker ejecutando la aplicación mediante JVM

Vamos a modificar el build por defecto de nuestro pom.xml para construir una imagen Docker que ejecute nuestra aplicación igual que cualquier otra aplicación Java empleando una máquina virtual de Java. En este caso, vamos a utilizar el archivo Dockerfile.jvm.

En la sección de plugins del build por defecto insertaremos las siguientes líneas:

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>

La primera parte (groupId, artifactId y version) indica que queremos emplear Fabric8 Maven Plugin. En muchos casos esto podría ser suficiente ya que el plugin incluye un modo Zero-Config que funciona con valores por defecto.

A continuación añadimos una configuración específica para nuestra imagen. Primero definimos el nombre de la imagen marcnuri/fmp-quarkus:jvm. Esto creará una imagen para el repositorio marcnuri con el nombre fmp-quarkus y el tag jvm. Es muy importante añadir el tag de esta forma, si no la imagen también recibirá el tag latest (ver configuración para imágenes nativas).

También estamos especificando el dockerFile que queremos emplear en combinación con contextDir (directorio raíz del proyecto).

Por último añadimos las credenciales de Docker Hub en la sección authConfig. El goal de Maven para publicar las imágenes en Docker Hub sólo se empleará a través de GitHub Actions CI, las credenciales estarán disponibles en las variables de entorno especificadas.

Podemos lanzar el siguiente comando para iniciar la construcción de la imagen Docker:

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'

Imagen Docker ejecutando la aplicación en modo nativo

Del mismo modo que hemos hecho para construir la imagen en modo JVM, vamos a añadir el plugin al perfil native con algunos ajustes específicos:

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>

En este caso, vamos a cambiar el nombre de la imagen por marcnuri/fmp-quarkus:native para que reciba el tag native. También vamos a utilizar Dockerfile.native como nuestro dockerFile.

También queremos que la imagen reciba el tag latest (será la imagen que se descargará por defecto si no se especifica ningún tag), para ello añadiremos este tag en la sección tags. El mismo resultado se podría haber obtenido si hubiésemos especificado el nombre de la imagen si ningún tag (i.e. marcnuri/fmp-quarkus) y añadido el tag native a la sección tags.

Podemos ejecutar el siguiente comando para construir la imagen Docker nativa:

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 Publicando la imagen en Docker Hub

Si tenemos las variables de entorno adecuadas (authentication), ahora podemos ejecutar el siguiente comando para publicar la imagen en Docker Hub repository:

bash
./mvnw fabric8:push

En nuestro caso, vamos a ejecutar este paso desde un workflow de GitHub Actions:

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

Conclusión

Esta publicación muestra como desarrollar una sencilla aplicación empleando Quarkus y cómo publicar en Docker Hub una imagen Docker que ejecuta un binario nativo creado con GraalVM mediante Fabric8 Maven Plugin. En la primera sección hemos aprendido a construir un endpoint REST que devolverá una cita aleatoria para cada una de las peticiones. El segundo paso muestra como adaptar la aplicación para que pueda ser convertida en un ejecutable nativo con GraalVM. Por último, hemos integrado Fabric8 Maven Plugin en nuestro fichero pom.xml para poder construir y publicar dos imágenes Docker distintas.

El código fuente completo de este artículo puede encontrarse en Github.

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

Navegador de artículos
Cómo usar sets en GoIsotope Mail: Cómo desplegar Isotope+Traefik en Kubernetes
© 2007 - 2025 Marc Nuri