Implementa una app de Java de Spring Boot en Kubernetes en Google Kubernetes Engine

1. Antes de comenzar

Kubernetes es un proyecto de código abierto que se puede ejecutar en muchos entornos distintos, desde laptops hasta clústeres multinodos de alta disponibilidad, desde nubes públicas hasta implementaciones locales y desde instancias de máquinas virtuales (VM) hasta equipos físicos.

En este codelab, implementarás una app web simple de Spring Boot para Java en Kubernetes en GKE. El objetivo es que puedas ejecutar tu app web como una app replicada en Kubernetes. Tomarás el código que desarrollaste en tu máquina, lo convertirás en una imagen de contenedor de Docker y la ejecutarás en GKE.

Usarás GKE, un servicio de Kubernetes completamente administrado en Google Cloud, para que puedas enfocarte más en experimentar con Kubernetes, en lugar de configurar la infraestructura subyacente.

Si te interesa ejecutar Kubernetes en tu máquina local, como una laptop de desarrollo, consulta Minikube, que ofrece una configuración sencilla de un clúster de Kubernetes de un solo nodo para fines de desarrollo y prueba. Si lo deseas, puedes usar Minikube para realizar el codelab.

En el codelab, se usará el código de muestra de la guía sobre Cómo compilar una app con Spring Boot.

Requisitos previos

  • Estar familiarizado con el lenguaje de programación y las herramientas de Java
  • Conocimientos sobre editores de texto estándares de Linux, como Vim, Emacs y nano

Actividades

  • Empaquetar una app de Java simple como un contenedor de Docker
  • Crea tu clúster de Kubernetes en GKE.
  • Implementa tu app de Java en Kubernetes en GKE.
  • Escala verticalmente tu servicio y lanza una actualización.
  • Panel de acceso, una interfaz de usuario de Kubernetes basada en la Web.

Requisitos

2. Configuración y requisitos

Configuración del entorno de autoaprendizaje

  1. Accede a Google Cloud Console y crea un proyecto nuevo o reutiliza uno existente. Si aún no tienes una cuenta de Gmail o de Google Workspace, debes crear una.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • El Nombre del proyecto es el nombre visible de los participantes de este proyecto. Es una cadena de caracteres que no se utiliza en las APIs de Google. Puedes actualizarla cuando quieras.
  • El ID del proyecto es único en todos los proyectos de Google Cloud y es inmutable (no se puede cambiar después de configurarlo). La consola de Cloud genera automáticamente una cadena única. Por lo general, no importa cuál sea. En la mayoría de los codelabs, deberás hacer referencia al ID de tu proyecto (suele identificarse como PROJECT_ID). Si no te gusta el ID que se generó, podrías generar otro aleatorio. También puedes probar uno propio y ver si está disponible. No se puede cambiar después de este paso y se usa el mismo durante todo el proyecto.
  • Recuerda que hay un tercer valor, un número de proyecto, que usan algunas APIs. Obtén más información sobre estos tres valores en la documentación.
  1. A continuación, deberás habilitar la facturación en la consola de Cloud para usar las APIs o los recursos de Cloud. Ejecutar este codelab no costará mucho, tal vez nada. Para cerrar recursos y evitar que se generen cobros más allá de este instructivo, puedes borrar los recursos que creaste o borrar el proyecto. Los usuarios nuevos de Google Cloud son aptos para participar en el programa Prueba gratuita de $300.

Activar Cloud Shell

  1. En la consola de Cloud, haz clic en Activar Cloud Shell853e55310c205094.png.

55efc1aaa7a4d3ad.png

Si es la primera vez que inicias Cloud Shell, verás una pantalla intermedia que describe en qué consiste. Si apareció una pantalla intermedia, haz clic en Continuar.

9c92662c6a846a5c.png

El aprovisionamiento y la conexión a Cloud Shell solo tomará unos minutos.

9f0e51b578fecce5.png

Esta máquina virtual está cargada con todas las herramientas de desarrollo necesarias. Ofrece un directorio principal persistente de 5 GB y se ejecuta en Google Cloud, lo que mejora considerablemente el rendimiento de la red y la autenticación. Gran parte de tu trabajo en este codelab, si no todo, se puede hacer con un navegador.

Una vez que te conectes a Cloud Shell, deberías ver que estás autenticado y que el proyecto está configurado con tu ID del proyecto.

  1. En Cloud Shell, ejecuta el siguiente comando para confirmar que tienes la autenticación:
gcloud auth list

Resultado del comando

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Ejecuta el siguiente comando en Cloud Shell para confirmar que el comando de gcloud conoce tu proyecto:
gcloud config list project

Resultado del comando

[core]
project = <PROJECT_ID>

De lo contrario, puedes configurarlo con el siguiente comando:

gcloud config set project <PROJECT_ID>

Resultado del comando

Updated property [core/project].

3. Obtener código fuente

Después de que se inicie Cloud Shell, podrás usar la línea de comandos para clonar el código fuente de ejemplo en el directorio principal.

$ git clone https://github.com/spring-guides/gs-spring-boot.git
$ cd gs-spring-boot/complete

4. Ejecuta la app de forma local

  1. Asegúrate de que JAVA_HOME esté configurado en la versión correcta:
$ export JAVA_HOME=/usr/lib/jvm/java-1.17.0-openjdk-amd64
  1. Puedes iniciar la app de Spring Boot normalmente con el complemento de Spring Boot.
$ ./mvnw -DskipTests spring-boot:run
  1. Después de iniciar la app, haz clic en Vista previa en la Web 1a94d5bd10bfc072.pngen la barra de herramientas de Cloud Shell y selecciona Vista previa en el puerto 8080.

6252b94905f3f7bd.png

En tu navegador, se abrirá una pestaña y se conectará al servidor que acabas de iniciar.

9b6c29059957bd0.jpeg

5. Empaquetar la app de Java como un contenedor de Docker

A continuación, debes preparar tu app para que se ejecute en Kubernetes. El primer paso es definir el contenedor y su contenido.

  1. Crea el archivo JAR implementable para la app.
$ ./mvnw -DskipTests package
  1. Habilita la API de Artifact Registry para almacenar la imagen de contenedor que crearás.
$ gcloud services enable artifactregistry.googleapis.com
  1. Crea un repositorio de Docker nuevo si no hay uno existente. Debes crear un repositorio antes de enviarle imágenes:
$ gcloud artifacts repositories create codelabrepo     --repository-format=docker --location=us-central1 
  1. La imagen tendrá el siguiente formato:

{LOCATION}-docker.pkg.dev/{PROJECT-ID}/{REPOSITORY}/{IMAGE-NAME}

Por ejemplo, si creaste el repositorio en la ubicación us-central1 con el nombre codelabrepo y quieres asignarle a tu imagen el nombre hello-java:v1, la imagen será la siguiente:

us-central1-docker.pkg.dev/{PROJECT-ID}/codelabrepo/hello-java:v1

  1. Usa Jib para crear la imagen de contenedor y enviarla a Artifact Registry.
$ export GOOGLE_CLOUD_PROJECT=`gcloud config list --format="value(core.project)"`

$ ./mvnw -DskipTests com.google.cloud.tools:jib-maven-plugin:build -Dimage=us-central1-docker.pkg.dev/${GOOGLE_CLOUD_PROJECT}/codelabrepo/hello-java:v1
  1. Deberías poder ver la imagen de contenedor enumerada en la consola navegando a la página Imágenes de Artifact Registry en la consola de Cloud. Ahora tienes disponible una imagen de Docker para todo el proyecto, a la que Kubernetes puede acceder y organizar, como verás en unos minutos.
  2. (Opcional) Después de que se complete el proceso (la descarga y extracción tardará un poco), prueba la imagen con el siguiente comando, que ejecutará un contenedor de Docker como un daemon en el puerto 8080 a partir de la imagen de contenedor recién creada. Si tienes problemas con los permisos, primero ejecuta gcloud auth configure-docker us-central1-docker.pkg.dev:
$ docker run -ti --rm -p 8080:8080 \
  us-central1-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/codelabrepo/hello-java:v1
  1. Nuevamente, aproveche la función de vista previa en la Web de Cloud Shell.

6252b94905f3f7bd.png

  1. Deberías ver la página predeterminada en una pestaña nueva. Después de verificar que la app se ejecuta de forma local en un contenedor de Docker, puedes detener el contenedor en ejecución presionando Control+C.

6. Cree su clúster

Ya puedes crear tu clúster de GKE. Un clúster consta de un servidor de API de Kubernetes administrado por Google y un conjunto de nodos trabajadores. Los nodos trabajadores son VMs de Compute Engine.

  1. Primero, asegúrate de que las funciones de API relacionadas estén habilitadas.
$ gcloud services enable compute.googleapis.com container.googleapis.com
  1. Crea un clúster con dos nodos n1-standard-1 (el proceso tardará unos minutos en completarse).
$ gcloud container clusters create hello-java-cluster \
  --num-nodes 2 \
  --machine-type n1-standard-1 \
  --zone us-central1-c

Cuando el proceso se complete, debería ver que se creó el clúster.

Creating cluster hello-java-cluster...done.
Created [https://container.googleapis.com/v1/projects/...].
kubeconfig entry generated for hello-dotnet-cluster.
NAME                  ZONE            MASTER_VERSION  
hello-java-cluster  us-central1-c  ...

Ahora deberías tener un clúster de Kubernetes completamente funcional con la tecnología de GKE.

8c7c7fca14f70623.png

Es momento de implementar tu app alojada en contenedores en el clúster de Kubernetes. A partir de ahora, usarás la línea de comandos de kubectl (que ya está configurada en tu entorno de Cloud Shell). El resto del codelab requiere que la versión del cliente y del servidor de Kubernetes sean la 1.2 o posterior. kubectl version te mostrará la versión actual del comando.

7. Implementa tu app en Kubernetes

  1. Una implementación de Kubernetes puede crear, administrar y escalar varias instancias de tu app con la imagen de contenedor que creaste. Implementa una instancia de tu app en Kubernetes con el comando kubectl run.
$ kubectl create deployment hello-java --image=us-central1-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/codelabrepo/hello-java:v1
  1. Para ver la implementación que creaste, simplemente ejecuta el siguiente comando:
$ kubectl get deployments

NAME         DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hello-java   1         1         1            1           37s
  1. Para ver las instancias de la app que creó la implementación, ejecuta el siguiente comando:
$ kubectl get pods

NAME                         READY     STATUS    RESTARTS   AGE
hello-java-714049816-ztzrb   1/1       Running   0          57s

En este punto, deberías tener el contenedor ejecutándose bajo el control de Kubernetes, pero aún debes permitir que el mundo exterior pueda acceder a él.

8. Permite el tráfico externo

De forma predeterminada, solo se puede acceder al Pod mediante su IP interna dentro del clúster. Para que el contenedor hello-java sea accesible desde fuera de la red virtual de Kubernetes, debes exponer el Pod como un servicio de Kubernetes.

  1. En Cloud Shell, puedes exponer el Pod a la Internet pública creando un servicio LoadBalancer de Kubernetes.
$ kubectl create service loadbalancer hello-java --tcp=8080:8080

Ten en cuenta que se expone directamente la implementación, no el Pod. Eso hará que el servicio resultante balancee las cargas de tráfico en todos los Pods administrados por la implementación (en este caso, solo un Pod, pero luego agregarás más réplicas).

La instancia principal de Kubernetes crea el balanceador de cargas y las reglas de reenvío de Compute Engine relacionadas, los grupos de destino y las reglas de firewall para que el servicio sea completamente accesible desde fuera de Google Cloud.

  1. Para conocer la dirección IP de acceso público del servicio, solo debes solicitar a kubectl que enumere todos los servicios del clúster.
$ kubectl get services

NAME         CLUSTER-IP     EXTERNAL-IP      PORT(S)    AGE
hello-java   10.3.253.62    aaa.bbb.ccc.ddd  8080/TCP    1m
kubernetes   10.3.240.1     <none>           443/TCP    5m
  1. Ahora, deberías poder acceder al servicio si diriges tu navegador a http://<EXTERNAL_IP>:8080.

9. Escala tu servicio

Una de las funciones potentes que ofrece Kubernetes es la facilidad de escalamiento de tu app. Supongamos que, de repente, necesitas más capacidad para tu app. Puedes indicarle al controlador de replicación que administre una nueva cantidad de réplicas para las instancias de tu app.

$ kubectl scale deployment hello-java --replicas=3

deployment "hello-java" scaled

$ kubectl get deployment
NAME         DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hello-java   3         3         3            3           22m

Observa el enfoque declarativo. En vez de iniciar o detener nuevas instancias, debes declarar cuántas instancias deberían estar en ejecución constantemente. Los bucles de conciliación de Kubernetes simplemente se aseguran de que la realidad coincida con lo que solicitó y, de ser necesario, toman medidas.

10. Implementa una actualización para tu servicio

En algún momento, la app que implementaste en producción requerirá la corrección de errores o funciones adicionales. Kubernetes puede ayudarte a implementar una versión nueva en producción sin afectar a tus usuarios.

  1. Para abrir el editor de código, haz clic en Abrir editor 2109d75686c889a.png en el menú de Cloud Shell.
  2. Navega a src/main/java/com/example/springboot/HelloController.java y actualiza el valor de la respuesta.
package com.example.springboot;

import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;

@RestController
public class HelloController {

    @RequestMapping("/")
    public String index() {
        return "Greetings from Google Kubernetes Engine!";
    }
}
  1. Usa Jib para compilar y enviar una versión nueva de la imagen del contenedor. Compilar y enviar la imagen actualizada debería ser mucho más rápido, ya que se aprovecha al máximo el almacenamiento en caché.
$ ./mvnw -DskipTests package com.google.cloud.tools:jib-maven-plugin:build -Dimage=us-central1-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/codelabrepo/hello-java:v2

Está todo listo para que Kubernetes actualice sin problemas el controlador de replicación a la nueva versión de la app.

  1. Para cambiar la etiqueta de imagen de tu contenedor en ejecución, debes editar la implementación de hello-java existente y cambiar la imagen de us-central1-docker.pkg.dev/PROJECT_ID/codelabrepo/hello-java:v1

a us-central1-docker.pkg.dev/PROJECT_ID/codelabrepo/hello-java:v2

  1. Puedes usar el comando kubectl set image para pedirle a Kubernetes que implemente la versión nueva de tu app en todo el clúster, de a una instancia a la vez con actualizaciones progresivas.
$ kubectl set image deployment/hello-java hello-java=us-central1-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/codelabrepo/hello-java:v2

deployment "hello-java" image updated
  1. Vuelve a revisar http://EXTERNAL_IP:8080 para ver que muestra la respuesta nueva.

11. Revertir

¡Uy! ¿Cometiste un error con la nueva versión de la app? Es posible que la nueva versión haya tenido un error y debas revertirla rápidamente. Con Kubernetes, puedes revertirlo fácilmente al estado anterior. Ejecuta el siguiente comando para revertir la app:

$ kubectl rollout undo deployment/hello-java

Deberías ver la respuesta anterior cuando vuelvas a revisar http://EXTERNAL_IP:8080.

12. Felicitaciones

Aprendiste a compilar e implementar una nueva app web basada en Java en Kubernetes en GKE.

Realiza una limpieza

$ gcloud container clusters delete hello-java-cluster --zone us-central1-c

$ gcloud container images delete us-central1-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/codelabrepo/hello-java:v1 us-central1-docker.pkg.dev/$GOOGLE_CLOUD_PROJECT/codelabrepo/hello-java:v2

Más información