Quarkus + Fabric8 Maven Plugin + GraalVM


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 desrrollar 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:

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)

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”.

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.

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.

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:

mvnw clean quarkus:dev

El endpoint estará ahora accesible en http://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:

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:

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

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:

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:

La primera parte (groupId, artifactId & version) indica que queremos emplear Fabric8 Maven Plugin. En michos 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:

mvnw clean package fabric8:build

docker images "marcnuri/fmp-quarkus"

Imagen Docker ejecutando la aplicación ne 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:

En este caso, vamos a modificar la configuración de la imagen. En primer lugar 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:

mvn clean package fabric8:build -Pnative

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:

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

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

Dejar un Comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *