Migra de una app de Java de Google App Engine a Cloud Run con Docker

1. Descripción general

Esta serie de codelabs (instructivos prácticos y de autoaprendizaje) tiene como objetivo ayudar a los desarrolladores de Java de Google App Engine (estándar) a modernizar sus apps con una serie de migraciones. Si sigues estos pasos, podrás actualizar tu app para que sea más portátil y decidir alojarla en contenedores para Cloud Run, el servicio de alojamiento de contenedores de Google Cloud en App Engine, y otros servicios de alojamiento de contenedores.

En este instructivo, aprenderás a organizar en contenedores una app de App Engine para implementarla en el servicio completamente administrado de Cloud Run con un Dockerfile. Los Dockerfiles son el método de implementación más práctico para esta migración, pero también ofrecen la mayor cantidad de opciones para personalizar tu proceso de compilación.

Además de enseñarte los pasos necesarios para migrar de App Engine a Cloud Run, también aprenderás a actualizar una app de App Engine de Java 8 a Java 17.

Si la aplicación que te interesa migrar usa mucho los servicios agrupados en paquetes heredados de App Engine o cualquier otra función específica de App Engine, es posible que la guía Accede a los servicios agrupados en paquetes de App Engine para Java 11/17 sea un mejor punto de partida que este codelab.

En un próximo lab,

  • Usa Cloud Shell
  • Habilita las APIs de Cloud Run, Artifact Registry y Cloud Build
  • Crea contenedores para tu app con Docker y Cloud Build
  • Implementa tus imágenes de contenedor en Cloud Run

Requisitos

Encuesta

¿Cómo usarás este instructivo?

Ler Leer y completar los ejercicios

¿Cómo calificarías tu experiencia con Java?

Principiante Intermedio Avanzado

¿Cómo calificarías tu experiencia en el uso de los servicios de Google Cloud?

Principiante Intermedio Avanzado

2. Fondo

Los sistemas de PaaS, como App Engine y Cloud Functions, ofrecen muchos beneficios para tu equipo y aplicación, como permitir que los administradores de sistemas y los desarrolladores se enfoquen en la compilación de soluciones. Con las plataformas sin servidores, tu app puede realizar un ajuste de escala automático según sea necesario, reducir la escala verticalmente a cero con la facturación de pago por uso para ayudar a controlar los costos y usar una variedad de lenguajes de desarrollo comunes.

Sin embargo, la flexibilidad de los contenedores también es atractiva. Con la capacidad de elegir cualquier lenguaje, cualquier biblioteca y cualquier objeto binario, los contenedores te ofrecen lo mejor de ambos mundos: la comodidad de la computación sin servidores junto con la flexibilidad de los contenedores. De eso se trata Google Cloud Run.

Aprender a usar Cloud Run no está dentro del alcance de este codelab. Consulta la documentación de Cloud Run. El objetivo aquí es que te familiarices con el proceso de alojar en contenedores tu app de App Engine para Cloud Run (o cualquier otro servicio alojado en contenedores). Existen algunos aspectos que debes saber antes de continuar, principalmente que tu experiencia de usuario será un poco diferente.

En este codelab, aprenderás a compilar e implementar contenedores. Aprenderás a alojar tu app en un contenedor con un Dockerfile, migrar desde la configuración de App Engine y, de forma opcional, definir pasos de compilación para Cloud Build. Esto implicará dejar de usar ciertas funciones específicas de App Engine. Si prefieres no seguir esta ruta de acceso, puedes actualizar a un entorno de ejecución de Java 11/17 y mantener tus apps en App Engine.

3. Configurar/trabajo previo

1. Configura el proyecto

En este instructivo, usarás una app de muestra del repositorio appengine-java-migration-samples en un proyecto nuevo. Asegúrate de que el proyecto tenga una cuenta de facturación activa.

Si planeas migrar una app de App Engine existente a Cloud Run, puedes usar esa app para seguir los pasos.

Ejecuta el siguiente comando para habilitar las APIs necesarias para tu proyecto:

gcloud services enable artifactregistry.googleapis.com cloudbuild.googleapis.com run.googleapis.com

2. Obtén la app de ejemplo del modelo de referencia

Clona la app de ejemplo en tu propia máquina o en Cloud Shell y, luego, navega a la carpeta baseline.

El ejemplo es una app de Datastore basada en servlets y Java 8 que se diseñó para implementarse en App Engine. Sigue las instrucciones del archivo README para preparar esta app para la implementación en App Engine.

3. (Opcional) Implementa la app de referencia

Lo siguiente solo es necesario si deseas confirmar que la app funciona en App Engine antes de migrar a Cloud Run.

Consulta los pasos en el archivo README.md:

  1. Instala la CLI de gcloud o vuelve a familiarizarte con ella
  2. Inicializa la CLI de gcloud para tu proyecto con gcloud init.
  3. Crea el proyecto de App Engine con gcloud app create
  4. Implementa la app de muestra en App Engine
./mvnw package appengine:deploy -Dapp.projectId=$PROJECT_ID
  1. Confirma que la app se ejecuta en App Engine sin problemas

4. Crea un repositorio de Artifact Registry

Después de contenerizar tu app, necesitarás un lugar para enviar y almacenar tus imágenes. La forma recomendada de hacerlo en Google Cloud es con Artifact Registry.

Crea el repositorio llamado migration con gcloud de la siguiente manera:

gcloud artifacts repositories create migration --repository-format=docker \
--description="Docker repository for the migrated app" \
--location="northamerica-northeast1"

Ten en cuenta que este repositorio usa el tipo de formato docker, pero hay varios tipos de repositorios disponibles.

En este punto, tienes tu app de App Engine de referencia y tu proyecto de Google Cloud está preparado para migrarla a Cloud Run.

4. Modifica los archivos de la aplicación

En los casos en que tu app use en gran medida los servicios agrupados en paquetes heredados, la configuración o cualquier otra función exclusiva de App Engine, te recomendamos que sigas accediendo a esos servicios mientras actualizas al nuevo entorno de ejecución. En este codelab, se muestra una ruta de migración para las aplicaciones que ya usan servicios independientes o que se pueden refactorizar de manera factible para hacerlo.

1. Actualización a Java 17

Si tu app usa Java 8, considera actualizarla a una versión candidata de LTS posterior, como la 11 o la 17, para mantenerte al día con las actualizaciones de seguridad y acceder a las nuevas funciones del lenguaje.

Comienza por actualizar las propiedades en tu pom.xml para incluir lo siguiente:

<properties>
    <java.version>17</java.version>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
</properties>

Esto establecerá la versión del proyecto en 17, informará al complemento del compilador que deseas acceder a las funciones del lenguaje Java 17 y que deseas que las clases compiladas sean compatibles con la JVM de Java 17.

2. Incluir un servidor web

Existen varias diferencias entre App Engine y Cloud Run que vale la pena tener en cuenta cuando se migra de uno a otro. Una diferencia es que, mientras que el entorno de ejecución de Java 8 de App Engine proporcionaba y administraba un servidor Jetty para las apps que alojaba, Cloud Run no lo hace. Usaremos Spring Boot para que nos proporcione un servidor web y un contenedor de servlets.

Agrega las siguientes dependencias:

<dependencies>
<!-- ... -->
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
       <version>2.6.6</version>
       <exclusions>
           <!-- Exclude the Tomcat dependency -->
           <exclusion>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-starter-tomcat</artifactId>
           </exclusion>
       </exclusions>
   </dependency>
   <!-- Use Jetty instead -->
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-jetty</artifactId>
       <version>2.6.6</version>
   </dependency>
<!-- ... -->
</dependencies>

Spring Boot incorpora un servidor Tomcat de forma predeterminada, pero este ejemplo excluirá ese artefacto y se quedará con Jetty para minimizar las diferencias en el comportamiento predeterminado después de la migración.

3. Configuración de Spring Boot

Si bien Spring Boot podrá reutilizar tus servlets sin modificaciones, requerirá cierta configuración para asegurarse de que se puedan detectar.

Crea la siguiente clase MigratedServletApplication.java en el paquete com.example.appengine:

package com.example.appengine;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@ServletComponentScan
@SpringBootApplication
@EnableAutoConfiguration
public class MigratedServletApplication {
    public static void main(String[] args) {
        SpringApplication.run(MigratedServletApplication.class, args);
    }
}

Ten en cuenta que esto incluye la anotación @ServletComponentScan, que buscará (en el paquete actual de forma predeterminada) cualquier @WebServlets y los pondrá a disposición según lo esperado.

4. Cómo empaquetar la app como un archivo JAR

Si bien es posible colocar tu app en un contenedor a partir de un archivo WAR, resulta más fácil si la empaquetas como un archivo JAR ejecutable. Esto no requerirá mucha configuración, en especial para los proyectos que usan Maven como herramienta de compilación, ya que el empaquetado de JAR es el comportamiento predeterminado.

Quita la etiqueta packaging en el archivopom.xml:

<packaging>war</packaging>

A continuación, agrega el spring-boot-maven-plugin:

<plugins>
<!-- ... -->
  <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <version>2.6.6</version>
  </plugin>
<!-- ... -->
</plugins>

5. Migra la configuración, los servicios y las dependencias de App Engine

Como se mencionó al principio del codelab, Cloud Run y App Engine están diseñados para ofrecer diferentes experiencias del usuario. Ciertas funciones que App Engine ofrece de forma predeterminada, como los servicios Cron y Task Queue, deben volver a crearse de forma manual y se abordarán con más detalle en módulos posteriores.

La app de ejemplo no usa los servicios agrupados heredados, pero los usuarios cuyas apps sí los usan pueden consultar las siguientes guías:

Como a partir de ahora realizarás la implementación en Cloud Run, se puede quitar appengine-maven-plugin:

<plugin>
 <groupId>com.google.cloud.tools</groupId>
 <artifactId>appengine-maven-plugin</artifactId>
 <version>2.4.1</version>
 <configuration>
   <!-- can be set w/ -DprojectId=myProjectId on command line -->
   <projectId>${app.projectId}</projectId>
   <!-- set the GAE version or use "GCLOUD_CONFIG" for an autogenerated GAE version -->
   <version>GCLOUD_CONFIG</version>
 </configuration>
</plugin>

5. Organiza la aplicación en contenedores

En este punto, ya puedes indicarle a Cloud Build cómo compilar el contenedor de tu aplicación. Con este método de contenedorización, no se requiere un archivo de configuración de compilación independiente (cloudbuild.yaml). Podemos definir un Dockerfile mínimo como punto de partida:

FROM eclipse-temurin

ARG JAR_FILE=JAR_FILE_MUST_BE_SPECIFIED_AS_BUILD_ARG

COPY ${JAR_FILE} app.jar

ENTRYPOINT ["java", "-jar","/app.jar"]

Este Dockerfile agrupa la versión uber-jar de tu servicio de Spring Boot en una sola capa. Es el enfoque más simple para la contenerización de Dockerfile, pero tiene varios inconvenientes, especialmente cuando se comparan tiempos repetidos en los que las dependencias son relativamente estables. Las preocupaciones como esta son el motivo por el que este método de contenedorización se considera más avanzado. Sin embargo, escribir tu propio Dockerfile te ofrece un control completo sobre la imagen base y acceso a los beneficios de rendimiento de escribir una imagen cuidadosamente estratificada.

2**. Ejecuta el proceso de compilación**.

Ahora que informaste a Cloud Build sobre los pasos de compilación deseados, ya puedes realizar la implementación con un solo clic.

Ejecuta el siguiente comando:

gcloud builds submit --tag LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE_NAME

Reemplaza los valores de marcador de posición en el comando anterior por los siguientes:

  • LOCATION: Es la ubicación regional o multirregional de tu repositorio.
  • PROJECT_ID: Es el ID de tu proyecto de Cloud.
  • REPOSITORY: Es el nombre de tu repositorio de Artifact Registry.
  • IMAGE_NAME: Es el nombre de tu imagen de contenedor.

Una vez que finalice el proceso, se habrá compilado tu imagen de contenedor, se habrá almacenado en Artifact Registry y se habrá implementado en Cloud Run.

Al final de este codelab, tu app debería verse igual que la que se encuentra en la carpeta mod4-migrate-to-cloud-run.

Lo logró. Migraste correctamente una app de App Engine de Java 8 a Java 17 y Cloud Run, y ahora tienes una comprensión más clara del trabajo involucrado al cambiar y elegir entre las opciones de hosting.

6. Resumen/Limpieza

¡Felicitaciones! Actualizaste, alojaste en contenedores y migraste tu app, lo que concluye con este instructivo.

A partir de aquí, el siguiente paso es obtener más información sobre las funciones de CI/CD y seguridad de la cadena de suministro de software que están a tu alcance ahora que puedes realizar implementaciones con Cloud Build:

Opcional: Limpia o inhabilita el servicio

Si implementaste la app de ejemplo en App Engine durante este instructivo, recuerda inhabilitar la app para evitar que se apliquen cargos. Cuando estés listo para pasar al siguiente codelab, puedes volver a habilitarla. Aunque las aplicaciones de App Engine estén inhabilitadas, no recibirán tráfico que genere cargos. Sin embargo, el uso de Datastore puede facturarse si supera su cuota gratuita, así que borra lo suficiente para que quede dentro de ese límite.

Por otro lado, si no vas a continuar con las migraciones y quieres borrar todo el contenido por completo, puedes borrar tu servicio o cerrar el proyecto por completo.

7. Recursos adicionales

Problemas o comentarios de los Codelabs del módulo de migración de App Engine

Si encuentras algún problema con este Codelab, primero busca el problema antes de enviarlo. Vínculos para buscar y crear problemas nuevos:

Recursos de migración

Recursos en línea

A continuación, se incluyen recursos en línea que pueden ser pertinentes para este instructivo:

App Engine

Otra información de la nube

Videos

Licencia

Este trabajo cuenta con una licencia Atribución 2.0 Genérica de Creative Commons.