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

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 2 + Kubernetes Maven Plugin + GraalVM integration

2021-08-14 en Java etiquetado Docker / Eclipse / GraalVM / Java / Eclipse JKube / Kubernetes / Maven / Nativo / Quarkus por Marc Nuri | Última actualización: 2022-02-03
English version

Introducción

En este tutorial os enseñaré como desarrollar e integrar una aplicación Quarkus 2 muy sencilla con Kubernetes Maven Plugin (Eclipse JKube) para publicar una imagen nativa GraalVM en Docker Hub y cómo desplegarla en Kubernetes.

Esta es una actualización de mi artículo Quarkus + Fabric8 Maven Plugin + GraalVM, y que Fabric8 Maven Plugin está obsoleto y debe de reemplazarse por JKube.

En la primera parte describo como desarrollar una aplicación muy sencilla utilizando Quarkus. A continuación, muestro como compilar un ejecutable nativo empleando GraalVM. Por último, muestro cómo integrar el proyecto con Kubernetes Maven Plugin para publicar las imágenes de contenedor de la aplicación en Docker Hub y desplegarlas en Kubernetes.

Aplicación de ejemplo Quarkus 2

En esta sección os enseñaré a construir una aplicación muy sencilla que devolverá una cita aleatoria cada vez que se haga un a petición al endpoint /quotes/random.

Arrancando el proyecto

Probablemente ya dispongas de Maven en tu entorno, en este caso, la forma más sencilla de iniciar el proyecto es ejecutando el siguiente comando:

mvn io.quarkus:quarkus-maven-plugin:2.1.1.Final:create \
    -DprojectGroupId=com.marcnuri.demo \
    -DprojectArtifactId=kubernetes-maven-plugin-quarkus \
    -DclassName="com.marcnuri.demo.kmp.quote.QuoteResource" \
    -Dextensions='quarkus-resteasy-jackson'

Si el comando se completa con éxito, podrás ver un nuevo directorio kubernetes-maven-plugin-quarkus con un proyecto Maven inicial y soporte para Maven wrapper.

No obstante, si no dispones de Maven en tu entorno, o prefieres una interfaz gráfica interactive, puedes navegar a code.quarkus.io para customizar y descargar un proyecto Quarkus con tus requisitos.

Generando un proyecto Quarkus (code.quarkus.io)
Generando un proyecto Quarkus (code.quarkus.io)

Recursos del proyecto

La aplicación debe de devolver una cita aleatoria cada vez que un usuario haga una petición a un endpoint. Para ello necesitamos que la aplicación cargue las citas desde un fichero JSON situado en el directorio de recursos del proyecto. Para ello, debes de añadir el fichero quotes.json al directorio src/main/resources/quotes/.

Endpoint

Una vez tenemos los recursos de la aplicación, podemos comenzar a implementar el código. El primer paso es crear un POJO Quote que utilizaremos para mapear la citas definidas en el fichero JSON cuando se deserialice.

1public class Quote implements Serializable {
2  /** ... **/
3  private String content;
4  private String author;
5  /** ... **/
6}

A continuación, debemos de crear una clase QuoteService que proporcionará el servicio para leer las citas desde el fichero y seleccionará una cita al azar.

1@Singleton
2public class QuoteService {
3
4  private static final Logger log = LoggerFactory.getLogger(QuoteService.class);
5
6  private static final String QUOTES_RESOURCE= "/quotes/quotes.json";
7
8  private final List<Quote> quotes;
9
10  public QuoteService() {
11    quotes = new ArrayList<>();
12  }
13
14  @PostConstruct
15  protected final void initialize() {
16    final var objectMapper = new ObjectMapper();
17    try (final InputStream quotesStream = QuoteService.class.getResourceAsStream(QUOTES_RESOURCE)) {
18      quotes.addAll(objectMapper.readValue(quotesStream,
19        objectMapper.getTypeFactory().constructCollectionType(List.class, Quote.class)));
20    } catch (IOException e) {
21      log.error("Error loading quotes", e);
22    }
23  }
24
25
26  Quote getRandomQuote() {
27    return quotes.get(ThreadLocalRandom.current().nextInt(quotes.size()));
28  }
29
30}

El método initialize utiliza Jackson para leer y deserializar el fichero quotes.json en una variable de clase ArrayList que se utilizará posteriormente para escoger una cita al azar.

El método getRandomQuote devuelve una entrada Quote aleatoria de la ArrayList para cada llamada.

Para finalizar la aplicación, necesitamos modificar el endpoint de muestra para que utilice el servicio que acabamos de implementar. Para ello, modificaremos la clase QuoteResource.

1@Path("/quotes")
2public class QuoteResource {
3
4  private static final String HEADER_QUOTE_AUTHOR = "Quote-Author";
5
6  private QuoteService quoteService;
7
8  @GET
9  @Path("/random")
10  @Produces(MediaType.TEXT_PLAIN)
11  public Response getRandomQuote() {
12    final var randomQuote = quoteService.getRandomQuote();
13    return Response
14      .ok(randomQuote.getContent(), MediaType.TEXT_PLAIN_TYPE)
15      .header(HEADER_QUOTE_AUTHOR, randomQuote.getAuthor())
16      .build();
17  }
18
19  @Inject
20  public void setQuoteService(QuoteService quoteService) {
21    this.quoteService = quoteService;
22  }
23}

El método getRandomQuote emplea una instancia de la clase QuoteService (descrita anteriormente) para obtener una cita al azar y devolver su contenido en el cuerpo de la respuesta HTTP. Además, el autor de la cita también se devuelve como una cabecera de la respuesta.

Una vez completados todos los pasos, podemos arrancar la aplicación en modo desarrollo empleando el siguiente comando:

./mvnw clean compile quarkus:dev
[INFO] --- quarkus-maven-plugin:2.1.1.Final:dev (default-cli) @ kubernetes-maven-plugin-quarkus ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory D:\00-MN\projects\marcnuri-demo\kubernetes-maven-plugin-quarkus\src\test\resources
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to D:\00-MN\projects\marcnuri-demo\kubernetes-maven-plugin-quarkus\target\test-classes
Listening for transport dt_socket at address: 5005
__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2021-07-04 07:49:33,690 INFO  [io.quarkus] (Quarkus Main Thread) kubernetes-maven-plugin-quarkus 1.0.0-SNAPSHOT on JVM (powered by Quarkus 2.1.1.Final) started in 3.774s. Listening on: http://localhost:8080
2021-07-04 07:49:33,693 INFO  [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2021-07-04 07:49:33,693 INFO  [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, resteasy, resteasy-jackson, smallrye-context-propagation]

--
Tests paused, press [r] to resume, [h] for more options>

Si todo ha ido bien, el endpoint estará accesible en http://localhost:8080/quotes/random.

Captura de pantalla del resultado de ejecutar curl localhost:8080/quotes/random -v
Captura de pantalla del resultado de ejecutar curl localhost:8080/quotes/random -v

Compilar un ejecutable nativo con GraalVM

Ha llegado el momento de hacer que nuestra aplicación sea supersónica. Para ello vamos a utilizar GraalVM para crear un binario nativo de la aplicación.

Lo primero es adaptar el proyecto para que sea completamente compatible con GraalVM.

Incluir recursos

GraalVM no incluirá ninguno de los recursos disponibles en el classpath durante la compilación del binario utilizando native-image. Todos aquellos recursos que deban de estar disponibles en tiempo de ejecución, debemos de incluirlos específicamente cuando se crea la imagen.

Con Quarkus es muy sencillo configurar GraalVM. En este sentido, para que GraalVM tenga en cuenta nuestro fichero quotes.json, tenemos que modificar el fichero application.properties del proyecto e incluir la siguiente línea:

1quarkus.native.additional-build-args=-H:IncludeResources=.*\.json$

Con esta instrucción le indicamos a Quarkus que añada la opción -H:IncludeResources al comando native-image. En este caso, queremos que se añada cualquier fichero que tenga la extensión .json.

Reflection en la imagen nativa

La deserialización de JSON de Jackson utiliza reflection para crear las instancias de las clases de destino cuando realiza lecturas. La compilación mediante Graal native image necesita saber antes de tiempo qué elementos van a ser accedidos mediante reflection en tiempo de ejecución del programa.

Quarkus facilita esta configuración mediante la anotación @RegisterForReflection que automatiza la tarea. En el caso de nuestra aplicación, anotaremos la clase Quote.

Construyendo la aplicación nativa

Una vez hemos adaptado la aplicación para que sea completamente compatible con GraalVM, podemos proceder a compilar la imagen en modo nativo. Si Graal VM con soporte para native-image está disponible en nuestro entorno, será tan sencillo como ejecutar el siguiente comando:

./mvnw clean package -Pnative
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildRunner] D:\00-MN\bin\graalvm-ce-java11-21.1.0\bin\native-image.cmd
  -J-Djava.util.logging.manager=org.jboss.logmanager.LogManager -J-Dsun.nio.ch.maxUpdateArraySize=100
  -J-Dvertx.logger-delegate-factory-class-name=io.quarkus.vertx.core.runtime.VertxLogDelegateFactory
  -J-Dvertx.disableDnsResolver=true -J-Dio.netty.leakDetection.level=DISABLED -J-Dio.netty.allocator.maxOrder=3
  -J-Duser.language=en -J-Duser.country=US -J-Dfile.encoding=UTF-8 -H:IncludeResources=.*.json\$
  --initialize-at-build-time=
  -H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy\$BySpaceAndTime -H:+JNI
  -H:+AllowFoldMethods -jar kubernetes-maven-plugin-quarkus-1.0.0-SNAPSHOT-runner.jar -H:FallbackThreshold=0
  -H:+ReportExceptionStackTraces -H:-AddAllCharsets -H:EnableURLProtocols=http -H:-UseServiceLoaderFeature
  -H:+StackTrace kubernetes-maven-plugin-quarkus-1.0.0-SNAPSHOT-runner
[kubernetes-maven-plugin-quarkus-1.0.0-SNAPSHOT-runner:2772]    classlist:   2,473.21 ms,  0.96 GB
[kubernetes-maven-plugin-quarkus-1.0.0-SNAPSHOT-runner:2772]        (cap):   3,921.54 ms,  0.96 GB
[kubernetes-maven-plugin-quarkus-1.0.0-SNAPSHOT-runner:2772]        setup:   6,147.99 ms,  0.96 GB
08:52:27,268 INFO  [org.jbo.threads] JBoss Threads version 3.4.0.Final
[kubernetes-maven-plugin-quarkus-1.0.0-SNAPSHOT-runner:2772]     (clinit):     664.62 ms,  4.67 GB
[kubernetes-maven-plugin-quarkus-1.0.0-SNAPSHOT-runner:2772]   (typeflow):  16,865.10 ms,  4.67 GB
[kubernetes-maven-plugin-quarkus-1.0.0-SNAPSHOT-runner:2772]    (objects):  20,902.79 ms,  4.67 GB
[kubernetes-maven-plugin-quarkus-1.0.0-SNAPSHOT-runner:2772]   (features):     945.86 ms,  4.67 GB
[kubernetes-maven-plugin-quarkus-1.0.0-SNAPSHOT-runner:2772]     analysis:  40,710.23 ms,  4.67 GB
[kubernetes-maven-plugin-quarkus-1.0.0-SNAPSHOT-runner:2772]     universe:   1,702.71 ms,  4.67 GB
[kubernetes-maven-plugin-quarkus-1.0.0-SNAPSHOT-runner:2772]      (parse):   4,527.99 ms,  4.67 GB
[kubernetes-maven-plugin-quarkus-1.0.0-SNAPSHOT-runner:2772]     (inline):   8,267.27 ms,  5.83 GB
[kubernetes-maven-plugin-quarkus-1.0.0-SNAPSHOT-runner:2772]    (compile):  24,709.00 ms,  5.69 GB
[kubernetes-maven-plugin-quarkus-1.0.0-SNAPSHOT-runner:2772]      compile:  39,765.47 ms,  5.69 GB
[kubernetes-maven-plugin-quarkus-1.0.0-SNAPSHOT-runner:2772]        image:   4,563.42 ms,  5.69 GB
[kubernetes-maven-plugin-quarkus-1.0.0-SNAPSHOT-runner:2772]        write:   2,281.61 ms,  5.69 GB
# Printing build artifacts to: kubernetes-maven-plugin-quarkus-1.0.0-SNAPSHOT-runner.build_artifacts.txt
[kubernetes-maven-plugin-quarkus-1.0.0-SNAPSHOT-runner:2772]      [total]:  98,272.86 ms,  5.69 GB
[WARNING] [io.quarkus.deployment.pkg.steps.NativeImageBuildRunner] objcopy executable not found in PATH. Debug symbols will not be separated from executable.
[WARNING] [io.quarkus.deployment.pkg.steps.NativeImageBuildRunner] That will result in a larger native image with debug symbols embedded in it.
[INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 106997ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  02:04 min
[INFO] Finished at: 2021-07-04T08:53:44+02:00
[INFO] -----------------------------------------------------------------------

Si la ejecución termina con éxito, un nuevo binario kubernetes-maven-plugin-quarkus-1.0.0-SNAPSHOT-runner estará disponible enel directorio target.

Ahora podemos ejecutar la aplicación de forma nativa mediante el siguiente comando:

./target/kubernetes-maven-plugin-quarkus-1.0.0-SNAPSHOT-runner

Del mismo modo que cuando ejecutamos la aplicación en modo JVM, el endpoint está disponible en http://localhost:8080/quotes/random.

Si GraalVM no está disponible en nuestro sistema, pero sí disponemos de Docker, el mismo comando puede ejecutarse dentro de un Contenedor Docker para construir un binario nativo de Linux:

./mvnw clean package -Pnative -Dquarkus.native.container-build=true

Integración con Kubernetes Maven Plugin (Eclipse JKube)

Este es el último paso del tutorial. En esta sección, os enseñaré cómo integrar el proyecto con Kubernetes Maven Plugin (Eclipse JKube).

El proceso es tan sencillo como añadir Kubernetes Maven Plugin al pom.xml de nuestro proyecto.

1<properties>
2    <!-- ... -->
3    <jkube.version>1.18.1</jkube.version>
4  </properties>
5  <!-- ... -->
6  <build>
7    <!-- ... -->
8    <plugins>
9      <!-- ... -->
10      <plugin>
11        <groupId>org.eclipse.jkube</groupId>
12        <artifactId>kubernetes-maven-plugin</artifactId>
13        <version>${jkube.version}</version>
14      </plugin>
15    </plugins>
16  </build>

La configuración se reduce a añadir un elemento <plugin> con groupId, artifactId &version para indicar que queremos usar Kubernetes Maven Plugin. En muchos casos, esto será suficiente, ya que el plugin tiene un modo Zero-Config que se ocupa de definir la mayoría de opciones por nosotros analizando el proyecto e infiriendo los valores recomendados para la configuración.

Construir la imagen de contenedor (Docker) (k8s:build)

EL primero paso es eliminar los ficheros Dockerfile que Quarkus incluye en el directorio src/main/docker. Kubernetes Maven Plugin detecta toda la confiugración de forma automática, así que no es necesario mantener estos ficheros y los podemos eliminar de forma segura.

El modo Zero-Config de Eclipse JKube emplea Generators y Enrichers para ofrecer los valores de configuración inferidos del análisis del proyecto. Para este proyecto, toda la configuración se obtiene de forma automática salvo el ajuste de la autorización para el registro de Docker Hub. En este caso, vamos a configurar JKube para que lea las credenciales push de las variables de entorno DOCKER_HUB_USER andDOCKER_HUB_PASSWORD.

Para ello tienes que añadir los siguiente en la configuración del plugin.

1<plugin>
2  <groupId>org.eclipse.jkube</groupId>
3  <artifactId>kubernetes-maven-plugin</artifactId>
4  <version>${jkube.version}</version>
5  <configuration>
6    <authConfig>
7      <push>
8        <username>${env.DOCKER_HUB_USER}</username>
9        <password>${env.DOCKER_HUB_PASSWORD}</password>
10      </push>
11    </authConfig>
12  </configuration>
13</plugin>

Ya que hemos desarrollado el proyecto con soporte tanto para Quarkus en modo JVM como en modo nativo, vamos a generar 2 imágenes (Docker) de contenedor diferentes dependiendo en el perfil de Maven que seleccionemos.

Imagen Docker ejecutando la aplicación con JVM

fast-jar es el modo por defecto de empaquetado para el modo JVM en Quarkus 2. Esto implica que a menos que se indique otra opción (e.g. Maven Profile), el comando mvn package generará los ficheros y artefactos necesarios para este modo. Como voy a publicar las imágenes en Docker Hub, esta imagen la nombraré marcnuri/kubernetes-maven-plugin-quarkus:jvm.

Esto creará una imagen para el repositorio marcnuri con el nombre kubernetes-maven-plugin-quarkus y el tag jvm.

Para modificar el valor por defecto que JKube infiere para nuestro proyecto Quarkus, tenemos que modificar el valor de la propiedad de maven jkube.generator.name. Esto lo podemos hacer añadiendo una entrada a las propiedades globales del proyecto en el fichero pom.xml:

1<properties>
2    <!-- ... -->
3    <jkube.version>1.18.1</jkube.version>
4    <jkube.generator.name>marcnuri/kubernetes-maven-plugin-quarkus:jvm</jkube.generator.name>
5  </properties>

Ahora podemos ejecutar el siguiente comando para construir la imagen de Docker:

./mvnw clean package k8s:build
[INFO] --- kubernetes-maven-plugin:1.18.1:build (default-cli) @ kubernetes-maven-plugin-quarkus ---
[INFO] k8s: Running in Kubernetes mode
[INFO] k8s: Building Docker image in Kubernetes mode
[INFO] k8s: Running generator quarkus
[INFO] k8s: quarkus: Using Docker image quay.io/jkube/jkube-java-binary-s2i:0.0.9 as base / builder
[INFO] k8s: [marcnuri/kubernetes-maven-plugin-quarkus:jvm] "quarkus": Created docker-build.tar in 7 seconds 
[INFO] k8s: [marcnuri/kubernetes-maven-plugin-quarkus:jvm] "quarkus": Built image sha256:4947d
[INFO] k8s: [marcnuri/kubernetes-maven-plugin-quarkus:jvm] "quarkus": Tag with latest
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  16.113 s
[INFO] Finished at: 2021-07-04T16:22:27+02:00
[INFO] ------------------------------------------------------------------------

Imagen Docker ejecutando la aplicación en modo nativo

El proceso para el modo nativo es muy similar al que seguimos en el paso anterior para el modo JVM. En este caso, lo que quiero es generar una imagen con el siguiente nombre: marcnuri/kubernetes-maven-plugin-quarkus:native.

Como el proyecto ya contiene un perfil de Maven para el modo nativo, conseguir esto será tan sencillo como sobreescribir la propiedad global jkube.generator.name para el perfil native.

1<profiles>
2  <profile>
3    <id>native</id>
4    <!-- ... -->
5    <properties>
6      <quarkus.package.type>native</quarkus.package.type>
7      <jkube.generator.name>marcnuri/kubernetes-maven-plugin-quarkus:native</jkube.generator.name>
8    </properties>
9  </profile>
10</profiles>

Ahora podemos ejecutar el siguiente comando para construir la imagen nativa de Docker:

./mvnw clean package k8s:build -Pnative
[INFO] --- kubernetes-maven-plugin:1.18.1:build (default-cli) @ kubernetes-maven-plugin-quarkus ---
[INFO] k8s: Running in Kubernetes mode
[INFO] k8s: Building Docker image in Kubernetes mode
[INFO] k8s: Running generator quarkus
[INFO] k8s: quarkus: Using Docker image registry.access.redhat.com/ubi8/ubi-minimal:8.1 as base / builder
[INFO] k8s: Pulling from ubi8/ubi-minimal
[INFO] k8s: Digest: sha256:df6f9e5d689e4a0b295ff12abc6e2ae2932a1f3e479ae1124ab76cf40c3a8cdd
[INFO] k8s: Status: Downloaded newer image for registry.access.redhat.com/ubi8/ubi-minimal:8.1
[INFO] k8s: Pulled registry.access.redhat.com/ubi8/ubi-minimal:8.1 in 3 seconds 
[INFO] k8s: [marcnuri/kubernetes-maven-plugin-quarkus:native] "quarkus": Created docker-build.tar in 498 milliseconds
[INFO] k8s: [marcnuri/kubernetes-maven-plugin-quarkus:native] "quarkus": Built image sha256:5a1d5
[INFO] k8s: [marcnuri/kubernetes-maven-plugin-quarkus:native] "quarkus": Tag with latest
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  25.851 s
[INFO] Finished at: 2021-07-04T15:10:31Z
[INFO] ------------------------------------------------------------------------

Publicar la imagen en Docker Hub (k8s:push)

Independientemente del empaquetado que elijamos (JVM o native), publicar la imagen en Docker Hub es tan sencillo como ejecutar el siguiente comando (siempre y cuando las variables de entorno requeridas estén disponibles):

./mvnw k8s:push
# or if a native image was built
./mvnw k8s:push -Pnative

Obviamente, esto tiene sentido desde una pipeline de integración continua. Si estás ejecutando este código desde un entorno local seguro, puedes establecer las credenciales de esta forma:

./mvnw k8s:push -Djkube.docker.push.username=$username -Djkube.docker.push.password=$password
# or if a native image was built
./mvnw k8s:push -Pnative -Djkube.docker.push.username=$username -Djkube.docker.push.password=$password

En mi caso, estoy ejecutando esto desde un GitHub Actions Workflow:

Una captura de pantalla de GitHub Actions Workflow - mvn kubernetes:push
Una captura de pantalla de GitHub Actions Workflow - mvn kubernetes:push

Desplegando la aplicación en Kubernetes (k8s:apply)

Ahora que la imagen se encuentra disponible en Docker Hub, puedo desplegar la aplicación en mi cluster de Kubernetes.

La principal ventaja de JKube es que no hay que lidiar con ficheros de configuración YAML. El plugin se encarga de generar todo lo necesario, así que únicamente hay que ejecutar lo siguiente:

./mvnw k8s:resource k8s:apply
# or if a native image was built
./mvnw k8s:resource k8s:apply -Pnative
[INFO] --- kubernetes-maven-plugin:1.18.1:apply (default-cli) @ kubernetes-maven-plugin-quarkus ---
[INFO] k8s: Using Kubernetes at https://192.168.99.120:8443/ in namespace default with manifest D:\00-MN\projects\marcnuri-demo\kubernetes-maven-plugin-quarkus\target\classes\META-INF\jkube\kubernetes.yml 
[INFO] k8s: Creating a Service from kubernetes.yml namespace default name kubernetes-maven-plugin-quarkus
[INFO] k8s: Created Service: target\jkube\applyJson\default\service-kubernetes-maven-plugin-quarkus-4.json
[INFO] k8s: Creating a Deployment from kubernetes.yml namespace default name kubernetes-maven-plugin-quarkus
[INFO] k8s: Created Deployment: target\jkube\applyJson\default\deployment-kubernetes-maven-plugin-quarkus-4.json
[INFO] k8s: HINT: Use the command `kubectl get pods -w` to watch your pods start up
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  8.678 s
[INFO] Finished at: 2021-07-06T06:13:35+02:00
[INFO] ------------------------------------------------------------------------

Este es un proyecto muy sencillo, así que no he creado ningún Ingress para exponer el servicio. Esto significa que la aplicación se encuentra inaccesible. En un próximo post explicaré como crear dicho Ingress. En todo caso, si estás ejecutando la aplicación en Minikube y quieres acceder a la misma, con JKube sería tan sencillo como ejecutar:

./mvnw k8s:resource k8s:apply -Djkube.enricher.jkube-service.type=NodePort
# or if a native image was built
./mvnw k8s:resource k8s:apply -Djkube.enricher.jkube-service.type=NodePort -Pnative
minikube service kubernetes-maven-plugin-quarkus

Si todo ha ido bien, una ventana de navegador se abrirá y podrás ver una página como esta:

Captura de pantalla mostrando la aplicación corriendo sobre Kubernetes
Captura de pantalla mostrando la aplicación corriendo sobre Kubernetes

Conclusión

En esta publicación os he mostrado como desarrollar una aplicación Quarkus 2 muy sencilla, crear una imagen nativa mediante GraalVM e integrarla con Kubernetes Maven Plugin. En la primera sección, he demostrado como generar la aplicación básica y crear un endpoint REST que devuelve una cita al azar para cada petición. A continuación os he mostrado como configurar la aplicación para que sea compatible con GraalVM y así poder generar un binario nativo. Por último, os he enseñado como configurar Kubernetes Maven Plugin para poder construir la imagen de contenedor y publicarla en el registro de Docker Hub. Además, hemos visto lo sencillo que es desplegar la aplicación en Kubernetes a través de Eclipse JKube.

El código fuente completo de este artículo se encuentra en GitHub.

Quarkus 2 + GraalVM + Kubernetes Maven Plugin
Quarkus 2 + GraalVM + Kubernetes Maven Plugin
Twitter iconFacebook iconLinkedIn iconPinterest iconEmail icon

Navegador de artículos
Cómo deshabilitar wildcard imports en IntelliJ IDEACómo mostrar espacios en blanco en IntelliJ IDEA
© 2007 - 2025 Marc Nuri