Taller de modificación de apps

1. Introducción

Última actualización: 1/11/2024

¿Cómo modernizamos una aplicación PHP antigua para Google Cloud?

(📽️ Mira un video introductorio de 7 minutos sobre este codelab)

Es común tener aplicaciones heredadas que se ejecutan de forma local y que deben modernizarse. Esto significa que deben ser escalables, seguros y aptos para la implementación en diferentes entornos.

En este taller, harás lo siguiente:

  1. Organiza en contenedores la aplicación de PHP.
  2. Migra a un servicio de bases de datos administrado ( Cloud SQL).
  3. Implementa en Cloud Run (es la alternativa sin operaciones a GKE/Kubernetes).
  4. Protege la aplicación con Identity and Access Management (IAM) y Secret Manager.
  5. Define una canalización de CI/CD a través de Cloud Build. Cloud Build se puede conectar con tu repositorio de Git alojado en proveedores populares de Git, como GitHub o GitLab, y se puede activar con cualquier envío a la rama principal, por ejemplo.
  6. Aloja las imágenes de la aplicación en Cloud Storage. Esto se logra a través del montaje, y no se necesita código para cambiar la app.
  7. Presentamos la funcionalidad de IA generativa a través de Gemini, orquestada a través de Cloud Functions (sin servidores).
  8. Familiarízate con los SLO y el funcionamiento de tu app recién actualizada.

Si sigues estos pasos, podrás modernizar gradualmente tu aplicación en PHP y mejorar su escalabilidad, seguridad y flexibilidad de implementación. Además, migrar a Google Cloud te permite aprovechar su potente infraestructura y sus servicios para garantizar que tu aplicación se ejecute sin problemas en un entorno nativo de la nube.

Creemos que lo que aprenderás siguiendo estos sencillos pasos se puede aplicar a tu propia aplicación y organización con diferentes lenguajes o pilas, y diferentes casos de uso.

Acerca de la app

La aplicación ( código, bajo la licencia MIT) que bifurcarás es una aplicación básica de PHP 5.7 con autenticación de MySQL. La idea principal de la app es proporcionar una plataforma en la que los usuarios puedan subir fotos y los administradores puedan etiquetar las imágenes inapropiadas. La aplicación tiene dos tablas:

  • Usuarios. Viene precompilado con administradores. Las personas nuevas se pueden registrar.
  • Images Incluye algunas imágenes de muestra. Los usuarios que accedieron a su cuenta pueden subir fotos nuevas. Agregaremos algo de magia aquí.

Tu objetivo

Queremos modernizar la aplicación anterior para tenerla en Google Cloud. Aprovecharemos sus herramientas y servicios para mejorar la escalabilidad, aumentar la seguridad, automatizar la administración de la infraestructura y, además, integrar funciones avanzadas, como el procesamiento de imágenes, la supervisión y el almacenamiento de datos, con servicios como Cloud SQL, Cloud Run, Cloud Build, Secret Manager y muchos más.

445f7a9ae37e9b4d.png

Lo que es más importante, queremos hacerlo paso a paso para que puedas aprender el proceso de pensamiento detrás de cada paso. Por lo general, cada paso desbloquea nuevas posibilidades para los siguientes (por ejemplo, los módulos 2 -> 3 y 6 -> 7).

¿Aún no te convence? Mira este video de 7 minutos en YouTube.

Requisitos

  • Una computadora con un navegador conectado a Internet
  • Algunos créditos de GCP Consulta el siguiente paso.
  • Usarás Cloud Shell. Incluye todos los comandos preinstalados que necesitarás y un IDE.
  • Cuenta de GitHub Necesitas esto para crear una rama del código original 🧑🏻‍💻 gdgpescara/app-mod-workshop con tu propio repo de Git. Esto es necesario para tener tu propia canalización de CI/CD (confirmación automática -> compilación -> implementación).

Aquí puedes encontrar soluciones de muestra:

Este taller está diseñado para completarse en Cloud Shell (en un navegador).

Sin embargo, también se puede intentar desde tu computadora local.

2. Configuración de crédito y Fork

6dafc658860c0ce5.png

Canjea el crédito de GCP y configura tu entorno de GCP [opcional]

Para realizar este taller, necesitas una cuenta de facturación con algo de crédito. Si ya tienes tu propia facturación, puedes omitir este paso.

Crea una cuenta de Gmail de Google completamente nueva (*) para vincularla a tu crédito de GCP. Pídele a tu instructor el vínculo para canjear el crédito de GCP o usa los créditos aquí: bit.ly/PHP-Amarcord-credits .

Accede con la cuenta recién creada y sigue las instrucciones.

ff739240dbd84a30.png

(

) ¿Por qué necesito una cuenta de Gmail nueva?*

Hemos visto que algunas personas no aprueban el codelab porque su cuenta (en particular, los correos electrónicos laborales o de estudiantes) ya había estado expuesta a GCP y tenía políticas organizacionales que restringían su capacidad para hacerlo. Te recomendamos que crees una cuenta de Gmail nueva o que uses una cuenta de Gmail existente (gmail.com) que no haya estado expuesta a GCP.

Haz clic en el botón para canjear el crédito.

331658dc50213403.png

Completa el siguiente formulario con tu nombre y apellido, y acepta los Términos y Condiciones.

Es posible que debas esperar unos segundos antes de que aparezca la cuenta de facturación aquí: https://console.cloud.google.com/billing

Una vez que termines, abre Google Cloud Console y crea un proyecto nuevo. Para ello, haz clic en el selector de proyectos en el menú desplegable de la esquina superior izquierda, donde se muestra "Sin organización". Ver a continuación

bd7548f78689db0b.png

Crea un proyecto nuevo si no tienes uno, como se muestra en la siguiente captura de pantalla. Hay una opción “PROYECTO NUEVO” en la esquina superior derecha.

6c82aebcb9f5cd47.png

Asegúrate de vincular el proyecto nuevo a la cuenta de facturación de la prueba de GCP de la siguiente manera.

f202527d254893fb.png

Ya puedes usar Google Cloud Platform. Si eres principiante o solo quieres hacer todo en un entorno de Cloud, puedes acceder a Cloud Shell y a su editor a través del siguiente botón en la esquina superior izquierda, como se muestra a continuación.

7d732d7bf0deb12e.png

Asegúrate de que tu proyecto nuevo esté seleccionado en la parte superior izquierda:

No seleccionado (incorrecto):

c2ffd36a781b276a.png

Seleccionada (buena):

594563c158f4f590.png

Bifurca la app desde GitHub

  1. Ve a la app de demostración: https://github.com/gdgpescara/app-mod-workshop
  2. Haz clic en 🍴 fork.
  3. Si no tienes una cuenta de GitHub, deberás crear una nueva.
  4. Edita lo que quieras.

734e51bfc29ee5df.png

  1. Clona el código de la app con
  2. git clone https://github.com/YOUR-GITHUB-USER/YOUR-REPO-NAME
  1. Abre la carpeta del proyecto clonado con tu editor favorito. Si eliges Cloud Shell, puedes hacer clic en "Abrir editor", como se muestra a continuación.

40f5977ea4c1d1cb.png

Tienes todo lo que necesitas con el editor de Google Cloud Shell, como se muestra en la siguiente figura.

a4e5ffb3e9a35e84.png

Para hacerlo visualmente, haz clic en "Abrir carpeta" y selecciona la carpeta, que probablemente sea app-mod-workshop en tu carpeta principal.

3. Módulo 1: Crea una instancia de SQL

645902e511a432a6.png

Crea la instancia de Google Cloud SQL

Nuestra app en PHP se conectará a una base de datos de MySQL, por lo que debemos replicarla en Google Cloud para una migración sin problemas. Cloud SQL es la solución perfecta, ya que te permite ejecutar una base de datos de MySQL completamente administrada en Cloud. Sigue estos pasos:

  1. Ve a la página de Cloud SQL: https://console.cloud.google.com/sql/instances
  2. Haz clic en "Crear instancia".
  3. Habilita la API (si es necesario). Este proceso podría tardar unos segundos.
  4. Elige MySQL.
  5. (Estamos tratando de ofrecerte la versión más económica para que dure más):
  • Edición: Enterprise
  • Ajuste predeterminado: development (probamos Sandbox y no funcionó para nosotros)
  • Versión de MySQL: 5.7 (¡vaya, un viaje al pasado!)
  1. ID de instancia: Elige appmod-phpapp (si cambias esto, recuerda cambiar también los futuros secuencias de comandos y soluciones según corresponda).
  2. Contraseña: La que quieras, pero anótala como CLOUDSQL_INSTANCE_PASSWORD
  3. Región: Mantén la misma que elegiste para el resto de la app (p. ej., Milán = europe-west8).
  4. Disponibilidad zonal: Zona única (ahorramos dinero para la demostración)

Haz clic en el botón Create Instance para implementar la base de datos de Cloud SQL. ⌛ Este proceso tarda alrededor de 10 minutos en completarse⌛. Mientras tanto, sigue leyendo la documentación. También puedes comenzar a resolver el siguiente módulo ("Containerize your PHP App") ya que no tiene dependencias de este módulo en la primera parte (hasta que corrijas la conexión a la base de datos).

Nota. Esta instancia debería costarte alrededor de USD 7 por día. Asegúrate de hacerlo después del taller.

Crea la base de datos y el usuario de image_catalog en Cloud SQL

El proyecto de la app incluye una carpeta db/ que contiene dos archivos SQL:

  1. 01_schema.sql : Contiene código SQL para crear dos tablas que contienen datos de usuarios y de imágenes.
  2. 02_seed.sql: Contiene código SQL para propagar datos en las tablas creadas anteriormente.

Estos archivos se usarán más adelante, una vez que se cree la base de datos image_catalog. Para ello, sigue estos pasos:

  1. Abre tu instancia y haz clic en la pestaña Databases:
  2. Haz clic en "Crear base de datos".
  3. Llama a image_catalog (como en la configuración de la app de PHP).

997ef853e5ebd857.png

Luego, creamos el usuario de la base de datos. Con esto, podemos autenticarnos en la base de datos image_catalog.

  1. Ahora haz clic en la pestaña Usuarios.
  2. Haz clic en "Agregar cuenta de usuario".
  3. Usuario: Creemos uno:
  • Nombre de usuario: appmod-phpapp-user
  • Contraseña: Elige algo que puedas recordar o haz clic en "Generar".
  • Mantén la opción "Permitir cualquier host (%)".
  1. haz clic en AGREGAR.

Abre la BD a IPs conocidas.

Ten en cuenta que todas las bases de datos en Cloud SQL nacen "aisladas". Debes configurar explícitamente una red desde la que se pueda acceder.

  1. Haz clic en tu instancia.
  2. Abre el menú "Conexiones".
  3. Haz clic en la pestaña “Redes”.
  4. Haz clic en "Redes autorizadas". Ahora agrega una red (es decir, una subred).
  • Por ahora, elijamos una configuración rápida pero INSEGURA para que la app funcione. Más adelante, puedes restringirla a las IPs en las que confías:
  • Nombre: "Todos en el mundo: INSEGURA".
  • Red: "0.0.0.0/0" (Nota: Esta es la parte INSEGURA)"
  • Haz clic en LISTO.
  1. Haz clic en Guardar.

Deberías ver algo como esto:

5ccb9062a7071964.png

Nota. Esta solución es un buen compromiso para terminar el taller en O(horas). Sin embargo, consulta el documento de SEGURIDAD para proteger tu solución para la producción.

Es hora de probar la conexión a la base de datos

Veamos si funciona el usuario image_catalog que creamos antes.

Accede a "Cloud SQL Studio" dentro de la instancia y, luego, ingresa la base de datos, el usuario y la contraseña para autenticarte, como se muestra a continuación:

d56765c6154c11a4.png

Ahora que ingresaste, puedes abrir el editor de SQL y continuar con la siguiente sección.

Importa la base de datos desde el código base

Usa el editor de SQL para importar las tablas image_catalog con sus datos. Copia el código SQL de los archivos del repo ( 01_schema.sql y, luego, 02_seed.sql) y ejecútalos uno tras otro en orden secuencial.

Después de esto, deberías obtener dos tablas en image_catalog, que son users y images, como se muestra a continuación:

65ba01e4c6c2dac0.png

Para probarlo, ejecuta lo siguiente en el editor: select * from images;

También asegúrate de anotar la dirección IP pública de la instancia de Cloud SQL, ya que la necesitarás más adelante. Para obtener la IP, ve a la página principal de la instancia de Cloud SQL en la página Descripción general. (Descripción general > Conéctate a esta instancia > Dirección IP pública).

4. Módulo 2: Organiza tu app de PHP en contenedores

e7f0e9979d8805f5.png

Queremos compilar esta app para la nube.

Esto significa empaquetar el código en algún tipo de archivo ZIP que contenga toda la información para ejecutarlo en la nube.

Existen varias formas de empaquetarlo:

  • Docker Es muy popular, pero su configuración correcta es bastante compleja.
  • Paquetes de compilación. Es menos popular, pero tiende a "adivinar automáticamente" qué compilar y qué ejecutar. A menudo, simplemente funciona.

En el contexto de este taller, supondremos que usas Docker.

Si elegiste usar Cloud Shell, es el momento de volver a abrirlo (haz clic en la parte superior derecha de la consola de Cloud).

ec6a6b90b39e03e.png

Esto debería abrir una shell conveniente en la parte inferior de la página, en la que deberías haber bifurcado el código en el paso de configuración.

6999b906c0dedeb7.png

Docker

Si quieres tener control, esta es la solución adecuada para ti. Esto tiene sentido cuando necesitas configurar bibliotecas específicas e insertar ciertos comportamientos no evidentes (un chmod en las cargas, un ejecutable no estándar en tu app, etc.).

Como, en última instancia, queremos implementar nuestra aplicación alojada en contenedores en Cloud Run, consulta la siguiente documentación. ¿Cómo harías una adaptación para versiones anteriores de PHP 8 a PHP 5.7? Quizás puedas usar Gemini para ello. Como alternativa, puedes usar esta versión precompilada:

# Use the official PHP image: https://hub.docker.com/_/php
FROM php:5.6-apache

# Configure PHP for Cloud Run.
# Precompile PHP code with opcache.
# Install PHP's extension for MySQL
RUN docker-php-ext-install -j "$(nproc)" opcache mysqli pdo pdo_mysql && docker-php-ext-enable pdo_mysql

RUN set -ex; \
  { \
    echo "; Cloud Run enforces memory & timeouts"; \
    echo "memory_limit = -1"; \
    echo "max_execution_time = 0"; \
    echo "; File upload at Cloud Run network limit"; \
    echo "upload_max_filesize = 32M"; \
    echo "post_max_size = 32M"; \
    echo "; Configure Opcache for Containers"; \
    echo "opcache.enable = On"; \
    echo "opcache.validate_timestamps = Off"; \
    echo "; Configure Opcache Memory (Application-specific)"; \
    echo "opcache.memory_consumption = 32"; \
  } > "$PHP_INI_DIR/conf.d/cloud-run.ini"

# Copy in custom code from the host machine.
WORKDIR /var/www/html

COPY . .

# Setup the PORT environment variable in Apache configuration files: https://cloud.google.com/run/docs/reference/container-contract#port
ENV PORT=8080

# Tell Apache to use 8080 instead of 80.
RUN sed -i 's/80/${PORT}/g' /etc/apache2/sites-available/000-default.conf /etc/apache2/ports.conf

# Note: This is quite insecure and opens security breaches. See last chapter for hardening ideas.
# Uncomment at your own risk:
#RUN chmod 777 /var/www/html/uploads/

# Configure PHP for development.
# Switch to the production php.ini for production operations.
# RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
# https://github.com/docker-library/docs/blob/master/php/README.md#configuration
RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"

# Expose the port
EXPOSE 8080

La versión más reciente de Dockerfile está disponible aquí.

Para probar nuestra aplicación de forma local, debemos cambiar el archivo config.php de tal manera que nuestra app de PHP se conecte con la base de datos de MySQL disponible en Google Cloud SQL. Según lo que configuraste antes, completa los espacios en blanco:

<?php
// Database configuration
$db_host = '____________';
$db_name = '____________';
$db_user = '____________';
$db_pass = '____________';

try {
    $pdo = new PDO("mysql:host=$db_host;dbname=$db_name", $db_user, $db_pass);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    die("Errore di connessione: " . $e->getMessage());
}

session_start();
?>
  • DB_HOST es la dirección IP pública de Cloud SQL. Puedes encontrarla en la consola de SQL:

bd27071bf450a8d0.png

  • DB_NAME debe permanecer sin cambios: image_catalog
  • DB_USER debe ser appmod-phpapp-user
  • DB_PASS es algo que elegiste. Configúralo entre comillas simples y escapa los caracteres según sea necesario.

Además, no dudes en traducir al inglés las pocas partes en 🇮🇹 italiano con la ayuda de Gemini.

Bien, ahora que tienes Dockerfile y configuraste tu app de PHP para que se conecte a tu base de datos, probemos esto.

Instala Docker si aún no lo tienes ( vínculo). No necesitas esto si usas Cloud Shell (¿no es genial?).

Ahora, intenta compilar y ejecutar tu app de PHP alojada en contenedores con los comandos de compilación y ejecución de Docker adecuados.

# Build command - don't forget the final . This works if Dockerfile is inside the code folder:
$ docker build -t my-php-app-docker .   

# Local Run command: most likely ports will be 8080:8080
$ docker run -it -p <CONTAINER_PORT>:<LOCAL_MACHINE_PORT> my-php-app-docker

Si todo funciona, deberías poder ver la siguiente página web cuando te conectes al host local. Ahora que tu app se ejecuta en el puerto 8080, haz clic en el ícono de "Vista previa en la Web" (un navegador con un ojo) y, luego, en Vista previa en el puerto 8080 (o "Cambiar puerto" para cualquier otro puerto).

33a24673f4550454.png

Cómo probar el resultado en tu navegador

Ahora, tu aplicación debería verse de la siguiente manera:

2718ece96b1f18b6.png

Si accedes con Admin/admin123, deberías ver algo como lo siguiente.

68b62048c2e86aea.png

¡Excelente! Aparte del texto en italiano, funciona. 🎉🎉🎉

Si tu dockerización es correcta, pero las credenciales de la BD son incorrectas, es posible que veas algo como lo siguiente:

e22f45b79bab86e1.png

Vuelve a intentarlo. ¡Estás cerca!

Cómo guardar en Artifact Registry [opcional]

A estas alturas, deberías tener una aplicación de PHP alojada en contenedores que funcione y esté lista para implementarse en la nube. A continuación, necesitamos un lugar en la nube para almacenar nuestra imagen de Docker y hacer que sea accesible para la implementación en los servicios de Google Cloud, como Cloud Run. Esta solución de almacenamiento se llama Artifact Registry, un servicio de Google Cloud completamente administrado diseñado para almacenar artefactos de aplicaciones, incluidas imágenes de contenedores de Docker, paquetes de Maven, módulos de npm y mucho más.

Creemos un repositorio en Artifact Registry de Google Cloud con el botón correspondiente.

e1123f0c924022e6.png

Elige un nombre válido, el formato y la región adecuados para almacenar los artefactos.

4e516ed209c470ee.png

Vuelve a tu entorno de desarrollo local, etiqueta y envía la imagen del contenedor de la app al repositorio de Artifact Registry que acabas de crear. Para ello, completa los siguientes comandos.

  • docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
  • docker push TARGET_IMAGE[:TAG]

El resultado debería verse como la siguiente captura de pantalla.

1e498feb4e88be9f.png

¡Genial! 🎉🎉🎉 Puedes pasar al siguiente nivel. Antes de eso, dedica 2 minutos a intentar subir, acceder y salir de la app, y familiarizarte con los extremos de la app.Los necesitarás más adelante.

Posibles errores

Si recibes errores de contenedorización, intenta usar Gemini para explicar y corregir el error. Para ello, proporciona lo siguiente:

  • Tu Dockerfile actual
  • El error que se recibió
  • [Si es necesario] El código PHP que se está ejecutando.

Permisos de carga También prueba el extremo /upload.php y sube una foto. Es posible que recibas el siguiente error. Si es así, debes realizar algunas correcciones de chmod/chown en el objeto Dockerfile.

Advertencia: move_uploaded_file(uploads/image (3).png): failed to open stream: Permission denied in /var/www/html/upload.php on line 11

PDOException "could not find driver" (o "Errore di connessione: could not find driver"). Asegúrate de que tu Dockerfile tenga las bibliotecas de PDO adecuadas para MySQL (pdo_mysql) para conectarse a la BD. Obtén inspiración de las soluciones que se encuentran aquí.

No se puede reenviar tu solicitud a un backend. No se pudo conectar a un servidor en el puerto 8080. Esto significa que probablemente estás exponiendo el puerto incorrecto. Asegúrate de exponer el puerto desde el que Apache o NGINX realmente entregan contenido. Esto no es trivial. Si es posible, intenta usar el puerto 8080 (esto facilita el uso de Cloud Run). Si deseas conservar el puerto 80 (p. ej., porque Apache lo requiere), usa otro comando para ejecutarlo:

$ docker run -it -p 8080:80 # force 80

# Use the PORT environment variable in Apache configuration files.

# https://cloud.google.com/run/docs/reference/container-contract#port

RUN sed -i 's/80/${PORT}/g' /etc/apache2/sites-available/000-default.conf /etc/apache2/ports.conf

5. Módulo 3: Implementa la app en Cloud Run

9ffca42774f6c5d1.png

¿Por qué elegir Cloud Run?

¡Buena pregunta! Hace años, seguramente habrías elegido Google App Engine.

En pocas palabras, hoy en día, Cloud Run tiene una pila de tecnología más nueva, es más fácil de implementar, más económico y se reduce a 0 cuando no lo usas. Gracias a su flexibilidad para ejecutar cualquier contenedor sin estado y su integración con varios servicios de Google Cloud, es ideal para implementar microservicios y aplicaciones modernas con una sobrecarga mínima y una eficiencia máxima.

Específicamente, Cloud Run es una plataforma completamente administrada de Google Cloud que te permite ejecutar aplicaciones alojadas en contenedores sin estado en un entorno sin servidores. Maneja automáticamente toda la infraestructura, se escala desde cero para satisfacer el tráfico entrante y se reduce cuando está inactivo, lo que lo hace rentable y eficiente. Cloud Run admite cualquier lenguaje o biblioteca, siempre y cuando se empaqueten en un contenedor, lo que permite una gran flexibilidad en el desarrollo. Se integra bien con otros servicios de Google Cloud y es adecuada para compilar microservicios, APIs, sitios web y aplicaciones basadas en eventos sin necesidad de administrar la infraestructura de servidores.

Requisitos previos

Para completar esta tarea, debes tener gcloud instalado en tu máquina local. De lo contrario, consulta las instrucciones aquí. En cambio, si estás en Google Cloud Shell, no es necesario que realices ninguna acción.

Antes de la implementación…

Si trabajas en tu entorno local, autentícate en Google Cloud con el siguiente comando:

  • $ gcloud auth login –update-adc # not needed in Cloud Shell

Esto debería autenticarte a través de un acceso de OAuth en tu navegador. Asegúrate de acceder a través de Chrome con el mismo usuario (p. ej., vattelapesca@gmail.com) que accedió a Google Cloud con la facturación habilitada.

Habilita la API de Cloud Run con el siguiente comando:

  • $ gcloud services enable run.googleapis.com cloudbuild.googleapis.com

En este punto, todo está listo para la implementación en Cloud Run.

Implementa tu app en Cloud Run a través de gcloud

El comando que te permite implementar la app en Cloud Run es gcloud run deploy. Hay varias opciones de configuración para lograr tu objetivo. El conjunto mínimo de opciones (que puedes proporcionar a través de la línea de comandos o la herramienta te preguntará con una instrucción interactiva) es el siguiente:

  1. Nombre del servicio de Cloud Run que deseas implementar para tu app. Un servicio de Cloud Run te devolverá una URL que proporciona un extremo a tu app.
  2. Región de Google Cloud en la que se ejecutará tu app. (--region REGION)
  3. Imagen de contenedor que encapsula tu app.
  4. Variables de entorno que tu app necesita usar durante su ejecución.
  5. La marca Allow-Unauthenticated que permite que cualquier persona acceda a tu app sin más autenticación.

Consulta la documentación (o desplázate hacia abajo para ver una posible solución) para saber cómo aplicar esta opción a tu línea de comandos.

La implementación tardará unos minutos. Si todo está correcto, deberías ver algo similar a lo siguiente en la consola de Google Cloud.

ef1029fb62f8de81.png

f7191d579c21ca3e.png

Haz clic en la URL que proporciona Cloud Run y prueba tu aplicación. Una vez que te autentiques, deberías ver algo como lo siguiente.

d571a90cd5a373f9.png

"gcloud run deploy" sin argumentos

Quizás notaste que gcloud run deploy te hace las preguntas correctas y completa los espacios en blanco que dejaste. ¡Increíble!

Sin embargo, en algunos módulos, agregaremos este comando a un activador de Cloud Build, por lo que no podemos permitirnos preguntas interactivas. Debemos completar todas las opciones del comando. Así que quieres crear el gcloud run deploy --option1 blah --foo bar --region your-fav-region dorado. ¿Cómo lo harías?

  1. Repite los pasos 2, 3 y 4 hasta que gcloud deje de hacer preguntas:
  2. [LOOP] gcloud run deploy con las opciones encontradas hasta el momento
  3. Los sistemas [LOOP] solicitan la opción X
  4. [LOOP] Busca en la documentación pública cómo configurar X desde la CLI agregando la opción --my-option [my-value].
  5. Vuelve al paso 2, a menos que gcloud se complete sin más preguntas.
  6. Este gcloud run deploy BLAH BLAH BLAH es genial. Guarda el comando en algún lugar, ya que lo necesitarás más adelante para el paso de Cloud Build.

Puedes encontrar una posible solución aquí. Puedes encontrar la documentación aquí.

¡Felicidades! 🎉🎉🎉 Implementaste tu app en Google Cloud, lo que significa que completaste el primer paso de la modernización.

6. Módulo 4: Limpia la contraseña con Secret Manager

95cd57b03b4e3c73.png

En el paso anterior, pudimos implementar y ejecutar correctamente nuestra app en Cloud Run. Sin embargo, lo hicimos con una práctica inadecuada de seguridad: proporcionar algunos secretos en texto no cifrado.

Primera iteración: Actualiza tu archivo config.php para usar ENV

Quizás notaste que colocamos la contraseña de la base de datos directamente en el código del archivo config.php. Esto es adecuado para fines de prueba y para ver si la app funciona. Sin embargo, no puedes confirmar ni usar código de esa manera en un entorno de producción. La contraseña (y otros parámetros de conexión de la base de datos) se deben leer de forma dinámica y proporcionar a la app en el tiempo de ejecución. Cambia el archivo config.php para que lea los parámetros de la base de datos desde las variables de entorno. Si falla, considera establecer valores predeterminados. Esto es útil en caso de que no se cargue ENV, ya que el resultado de la página te indicará si se están usando los valores predeterminados. Completa los espacios en blanco y reemplaza el código en config.php.

<?php
// Database configuration with ENV variables. Set default values as well 
$db_host = getenv('DB_HOST') ?: 'localhost';
$db_name = getenv('DB_NAME') ?: 'image_catalog';
$db_user = getenv('DB_USER') ?: 'appmod-phpapp-user';
$db_pass = getenv('DB_PASS') ?: 'wrong_password';
// Note getenv() is PHP 5.3 compatible
try {
    $pdo = new PDO("mysql:host=$db_host;dbname=$db_name", $db_user, $db_pass);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    die("Errore di connessione: " . $e->getMessage());
}

session_start();
?>

Como tu app está en un contenedor, debes proporcionar una forma de suministrar las variables de entorno a la app. Esto se puede hacer de varias maneras:

  • En el momento de la compilación, en el Dockerfile Agrega a tu Dockerfile anterior los 4 parámetros con la sintaxis ENV DB_VAR=ENV_VAR_VALUE. Esto configurará valores predeterminados que se pueden anular durante el tiempo de ejecución. Por ejemplo, "DB_NAME" y "DB_USER" se podrían establecer aquí y en ningún otro lugar.
  • En el tiempo de ejecución Puedes configurar estas variables para Cloud Run, tanto desde la CLI como desde la IU. Este es el lugar adecuado para colocar todas tus 4 variables (a menos que quieras conservar los valores predeterminados establecidos en Dockerfile).

En localhost, es posible que desees colocar tus variables de entorno en un archivo .env (consulta la carpeta solutions).

También asegúrate de que .env se agregue a tu .gitignore : No querrás enviar tus secretos a GitHub.

echo .env >> .gitignore

Después, puedes probar la instancia de forma local:

docker run -it -p 8080:8080 --env-file .env my-php-app-docker

Ahora, lograste lo siguiente:

  1. Tu app leerá la variable de forma dinámica desde tu ENV
  2. Mejoraste la seguridad, ya que quitaste la contraseña de la base de datos de tu código.

Ahora puedes implementar una revisión nueva en Cloud Run. Vamos a la IU y configuremos las variables de entorno de forma manual:

  • Ve a https://console.cloud.google.com/run.
  • Haz clic en tu app.
  • Haz clic en "Editar e implementar una nueva revisión".
  • En la primera pestaña “Contenedores”, haz clic en la pestaña inferior “Variables y secretos”.
  • Haz clic en "+ Agregar variable" y agrega todas las variables necesarias. Deberías obtener algo como lo siguiente:

7a5fbfa448544d3.png

f2780c35585388ca.png

¿Es perfecto? No. Tu PASS sigue siendo visible para la mayoría de los operadores. Esto se puede mitigar con Google Cloud Secret Manager.

Segunda iteración: Secret Manager

Tus contraseñas desaparecieron de tu propio código: ¡victoria! Pero espera, ¿ya estamos a salvo?

Las contraseñas seguirán siendo visibles para cualquier persona que tenga acceso a la consola de Google Cloud. De hecho, si accedes al archivo de implementación YAML de Cloud Run, podrás recuperarlo. O bien, si intentas editar o implementar una nueva revisión de Cloud Run, la contraseña estará visible en la sección Variables y secretos, como se muestra en las siguientes capturas de pantalla.

Secret Manager de Google Cloud es un servicio seguro y centralizado para administrar información sensible, como claves de API, contraseñas, certificados y otros secretos.

Te permite almacenar, administrar y acceder a secretos con permisos detallados y encriptación sólida. Secret Manager, que se integra con Identity and Access Management (IAM) de Google Cloud, te permite controlar quién puede acceder a secretos específicos, lo que garantiza la seguridad de los datos y el cumplimiento de las reglamentaciones.

También admite la rotación y el control de versiones automáticos de secretos, lo que simplifica la administración del ciclo de vida de los secretos y mejora la seguridad en las aplicaciones de los servicios de Google Cloud.

Para acceder a Secret Manager, ve desde el menú de hamburguesas a los servicios de Seguridad y búscalo en la sección Protección de datos, como se muestra en la siguiente captura de pantalla.

6df83a1c3cb757f6.png

Habilita la API de Secret Manager cuando llegues a la página, como se muestra en la siguiente imagen.

a96c312e2c098db1.png

  • Ahora, haz clic en "Crear un secreto". Llamémoslo de forma racional:
  • Nombre: php-amarcord-db-pass
  • Valor secreto: "tu contraseña de la BD" (ignora la parte de "subir archivo").
  • Anota este vínculo secreto, que debería verse como projects/123456789012/secrets/php-amarcord-db-pass. Este es el puntero único a tu secreto (para Terraform, Cloud Run y otros). El número es el número de proyecto único.

Sugerencia: Intenta usar convenciones de nombres coherentes para tus secretos, especializándolos de izquierda a derecha, por ejemplo: cloud-devrel-phpamarcord-dbpass

  • Organización (con la empresa)
  • Equipo (dentro de la organización)
  • Aplicación (dentro del equipo)
  • Nombre de la variable (dentro de la app)

Esto te permitirá tener expresiones regulares sencillas para encontrar todos los secretos de una sola app.

Crea una revisión nueva de Cloud Run

Ahora que tenemos un nuevo secreto, debemos deshacernos de la variable de entorno DB_PASS y reemplazarla por el nuevo secreto. Entonces:

  • Acceso a Cloud Run con Google Cloud Console
  • Elige la app.
  • Haz clic en "Editar e implementar una revisión nueva".
  • Ubica la pestaña "Variables y Secrets".
  • Usa el botón "+ Reference a Secret" para restablecer la variable de entorno DB_PASS.
  • Usa la misma "DB_PASS" para los secretos a los que se hace referencia y usa la versión más reciente.

9ed4e35be7654dcb.png

Cuando termines, deberías recibir el siguiente error:

da0ccd7af39b04ed.png

Intenta averiguar cómo solucionarlo. Para resolver este problema, debes acceder a la sección IAM y administración y cambiar los permisos de otorgamiento. ¡Que disfrutes la depuración!

Una vez que lo hayas descubierto, regresa a Cloud Run y vuelve a implementar una revisión nueva. El resultado debería verse como en la siguiente figura:

e89f9ca780169b6b.png

Sugerencia: La consola para desarrolladores (IU) es excelente para señalar problemas de permisos. Tómate tu tiempo para navegar por todos los vínculos de tus entidades de Cloud.

7. Módulo 5: Configura tu CI/CD con Cloud Build

ba49b033c11be94c.png

¿Por qué usar una canalización de CI/CD?

A esta altura, deberías haber escrito gcloud run deploy varias veces, tal vez respondiendo la misma pregunta una y otra vez.

¿Te cansaste de implementar tu app de forma manual con gcloud run deploy? ¿No sería genial que tu app se implementara automáticamente cada vez que envías un cambio nuevo a tu repositorio de Git?

Para usar una canalización de CI/CD, necesitarás dos elementos:

  1. Un repositorio de Git personal: Por suerte, ya deberías haber bifurcado el repositorio del taller en tu cuenta de GitHub en el paso 2. Si no es así, vuelve y completa ese paso. Tu repositorio bifurcado debería verse de la siguiente manera: https://github.com/<YOUR_GITHUB_USER>/app-mod-workshop
  2. Cloud Build Este increíble y económico servicio te permite configurar automatizaciones de compilación para casi todo: Terraform, apps en contenedores, etc.

En esta sección, nos enfocaremos en configurar Cloud Build.

¡Ingresa a Cloud Build!

Usaremos Cloud Build para ello:

  • Compila tu código fuente (con Dockerfile). Piensa en esto como un "archivo .zip grande" que contiene todo lo que necesitas para compilarlo y ejecutarlo (tu "artefacto de compilación").
  • Envía este artefacto a Artifact Registry (AR).
  • Luego, emite una implementación desde AR a Cloud Run para la app "php-amarcord".
  • Esto creará una nueva versión (“revisión”) de la app existente (piensa en una capa con el código nuevo) y la configuraremos para desviar el tráfico a la versión nueva si la inserción se realiza correctamente.

Este es un ejemplo de algunas compilaciones de mi app de php-amarcord:

f30f42d4571ad5e2.png

¿Cómo hacemos todo esto?

  1. Creando un archivo YAML perfecto: cloudbuild.yaml
  2. Creando un activador de Cloud Build
  3. Conectándote a nuestro repositorio de GitHub a través de la IU de Cloud Build

Crea un activador (y conecta el repositorio)

  • Ve a https://console.cloud.google.com/cloud-build/triggers.
  • Haz clic en "Crear activador".
  • Compilación:
  • Nombre: Algo significativo, como on-git-commit-build-php-app
  • Evento: Envío a la rama
  • Fuente: "Conectar repositorio nuevo" texto alternativo
  • Se abrirá una ventana a la derecha: "Conectar repositorio".
  • Proveedor de la fuente: "Github" (primero)
  • "Continuar"
  • Authenticate abrirá una ventana en GitHub para realizar la autenticación cruzada. Sigue el flujo y ten paciencia. Si tienes muchos repositorios, es posible que te lleve un tiempo.
  • "Select repo": Selecciona tu cuenta o repositorio y marca la casilla "I understand…".
  • Si recibiste el error: La app de GitHub no está instalada en ninguno de tus repositorios, haz clic en "Instalar Google Cloud Build" y sigue las instrucciones.
  • 23e0e0f1219afea3.pngHaz clic en Conectar.
  • bafd904ec07122d2.png
  • ¡Bingo! Tu repo ya está conectado.
  • Volvamos a la parte del activador…
  • Configuración: Detectada automáticamente (*)
  • Opciones avanzadas: Selecciona la cuenta de servicio "[PROJECT_NUMBER]- compute@developer.gserviceaccount.com".
  • xxxxx es el ID de tu proyecto
  • La cuenta de servicio de Compute predeterminada es adecuada para un enfoque de laboratorio. No la uses en producción. ( Más información).
  • deja todo lo demás como está.
  • Haz clic en el botón “Crear”.

(*) Esta es la forma más sencilla, ya que verifica si hay un Dockerfile o un archivo cloudbuild.yaml. Sin embargo, el cloudbuild.yaml te da poder real para decidir qué hacer en cada paso.

¡Tengo el poder!

Ahora, el activador no funcionará a menos que le otorgues a la cuenta de servicio de Cloud Build (¿qué es una cuenta de servicio? Es el correo electrónico de un "robot" que actúa en tu nombre para una tarea (en este caso, compila elementos en la nube).

Tu SA no podrá compilar ni implementar la acción, a menos que le permitas hacerlo. Por suerte, es fácil.

  • Ve a "Cloud Build" > "Configuración".
  • Cuenta de servicio "[PROJECT_NUMBER]-compute@developer.gserviceaccount.com"
  • Marca estas casillas:
  • Cloud Run
  • Secret Manager
  • Cuentas de servicio
  • Cloud Build
  • También marca la casilla de verificación "Establecer como la cuenta de servicio preferida".

8715acca72286a46.png

¿Dónde está el archivo YAML de Cloud Build?

Te recomendamos que dediques tiempo a crear tu propio archivo YAML de Cloud Build.

Sin embargo, si no tienes tiempo o no quieres dedicarle tiempo, puedes inspirarte en esta carpeta de soluciones: .solutions

Ahora puedes enviar un cambio a GitHub y observar cómo Cloud Build hace su parte.

Configurar Cloud Build puede ser complicado. Se espera que haya un intercambio de información antes de la fecha límite:

  • Cómo verificar los registros en https://console.cloud.google.com/cloud-build/builds;region=global
  • Encontrar tu error
  • Se corrige en el código y se vuelve a emitir git commit / git push.
  • A veces, el error no está en el código, sino en alguna configuración. En ese caso, puedes emitir una nueva compilación desde la IU (Cloud Build > "Triggers" > Run).

97acd16980a144ab.png

Ten en cuenta que, si usas esta solución, aún queda trabajo por hacer. Por ejemplo, debes establecer las variables de entorno para los extremos de desarrollo y producción recién creados:

3da8723e4ff80c0a.png

Puedes hacerlo de dos maneras:

  • A través de la IU: Establece las variables de entorno nuevamente.
  • A través de la CLI, creando la secuencia de comandos "perfecta" para ti Puedes encontrar un ejemplo aquí: gcloud-run-deploy.sh . Debes ajustar algunas cosas, por ejemplo, el endpoint y el número de proyecto. Puedes encontrar el número de proyecto en el Resumen de Cloud.

¿Cómo confirmo código en GitHub?

En este taller, no se explica la mejor manera de git push a GitHub. Sin embargo, si te quedas atascado y estás en Cloud Shell, hay dos formas de continuar:

  1. CLI. Agrega una llave SSH de forma local y agrega un control remoto con git@github.com:YOUR_USER/app-mod-workshop.git (en lugar de http).
  2. VSCode. Si usas el editor de Cloud Shell, puedes usar la pestaña Control de código fuente (Ctrl+Mayúsculas+G), hacer clic en "Sincronizar cambios" y seguir las instrucciones. Deberías poder autenticar tu cuenta de GitHub en VS Code, y las operaciones de extracción y envío desde allí serán muy sencillas.

f0d53f839c7fa3b6.png

Recuerda git add clodubuild.yaml entre otros archivos, o no funcionará.

Comparación "profunda" y "superficial" de la paridad entre el desarrollo y la producción [opcional]

Si copiaste la versión del modelo desde aquí, tendrás dos versiones idénticas de DEV y PROD. Esto es genial y se alinea con la regla 10 de la app de doce factores.

Sin embargo, usamos dos extremos web diferentes para que una app apunte a la misma base de datos. Esto es suficiente para un taller, pero, en la vida real, debes dedicar tiempo a crear un entorno de producción adecuado. Esto significa tener dos bases de datos (una para desarrollo y otra para producción) y también elegir dónde tenerlas para la recuperación ante desastres o la alta disponibilidad. Esto va más allá del alcance de este taller, pero es algo para reflexionar.

Si tienes tiempo para hacer una versión "profunda" de la producción, ten en cuenta todos los recursos que necesitas duplicar, como los siguientes:

  • Base de datos de Cloud SQL (y, probablemente, instancia de SQL)
  • Bucket de GCS
  • Cloud Function.
  • Podrías usar Gemini 1.5 Flash como modelo en desarrollo (más económico y rápido) y Gemini 1.5 Pro (más potente).

En general, cada vez que hagas algo con la app, piensa de forma crítica: ¿La producción debería tener el mismo valor o no? De lo contrario, duplica tu esfuerzo. Por supuesto, esto es mucho más fácil con Terraform, ya que puedes insertar tu entorno (-dev, -prod) como sufijo de tus recursos.

8. Módulo 6: Migra a Google Cloud Storage

a680e0f287dd2dfb.png

Almacenamiento

dc3a4b8ea92aaef6.png

Actualmente, la app almacena el estado en un contenedor de Docker. Si la máquina se rompe, la app explota o simplemente envías una nueva revisión, se programará una nueva revisión con un almacenamiento nuevo y vacío: 🙈

¿Cómo lo solucionamos? Hay varias formas de hacerlo.

  1. Almacena imágenes en la BD. Eso es lo que terminé haciendo con mi app anterior de PHP. Es la solución más simple, ya que no le agrega complejidad. Sin embargo, agrega latencia y carga a tu base de datos.
  2. ¿Migrar tu app de Cloud Run a una solución compatible con el almacenamiento: GCE + disco persistente? ¿Quizás GKE + Storage? Nota: Lo que ganas en control, lo pierdes en agilidad.
  3. Ve a GCS. Google Cloud Storage ofrece el mejor almacenamiento de su clase para todo Google Cloud y es la solución más idiomática de Cloud. Sin embargo, requiere que nos ensuciemos las manos con las bibliotecas de PHP. ¿Tenemos bibliotecas de PHP 5.7 para GCS? ¿PHP 5.7 admite Composer (parece que PHP 5.3.2 es la versión más antigua compatible con Composer)?
  4. ¿Quizás deberías usar un sidecar de Docker?
  5. O quizás usar activaciones de volumen de Cloud Run de GCS. Suena increíble.

🤔 Migrar almacenamiento (pregunta abierta)

[Pregunta abierta] En este ejercicio, queremos que encuentres una solución para mover tus imágenes de una manera que se conserve de alguna forma.

Prueba de aceptación

No quiero decirte la solución, pero quiero que suceda lo siguiente:

  1. Subes newpic.jpg. Lo verás en la app.
  2. Actualizas la app a una versión nueva.
  3. newpic.jpg aún está allí, visible.

💡 Posible solución (activación de volúmenes de Cloud Run de GCS)

Esta es una solución muy elegante que nos permite lograr cargas de archivos con estado sin tocar el código EN ABSOLUTO (aparte de mostrar una descripción de la imagen, pero eso es trivial y solo para satisfacer la vista).

Esto debería permitirte activar una carpeta de Cloud Run en GCS, por lo que:

  1. Todas las cargas a GCS serán visibles en tu app.
  2. Todas las cargas a tu app se subirán a GCS
  3. Se aplicará magia a los objetos subidos en GCS (capítulo 7).

Nota. Lee la letra chica de FUSE. Esto NO está bien si el rendimiento es un problema.

Crea un bucket de GCS

GCS es el servicio de almacenamiento omnipresente de Google Cloud. Se probó en situaciones reales y lo usa cada servicio de GCP que necesita almacenamiento.

Ten en cuenta que Cloud Shell exporta PROJECT_ID como GOOGLE_CLOUD_PROJECT:

$ export PROJECT_ID=$GOOGLE_CLOUD_PROJECT

#!/bin/bash

set -euo pipefail

# Your Cloud Run Service Name, eg php-amarcord-dev
SERVICE_NAME='php-amarcord-dev'
BUCKET="${PROJECT_ID}-public-images"
GS_BUCKET="gs://${BUCKET}"

# Create bucket
gsutil mb -l "$GCP_REGION" -p "$PROJECT_ID" "$GS_BUCKET/"

# Copy original pictures there - better if you add an image of YOURS before.
gsutil cp ./uploads/*.png "$GS_BUCKET/"

Configura Cloud Run para que se vincule el bucket en la carpeta /uploads/

Ahora, veamos la parte elegante. Creamos un volumen php_uploads y le indicamos a Cloud Run que realice una activación de FUSE en MOUNT_PATH (algo como /var/www/html/uploads/):

#!/bin/bash

set -euo pipefail

# .. keep variables from previous script..

# Uploads folder within your docker container.
# Tweak it for your app code.
MOUNT_PATH='/var/www/html/uploads/'

# Inject a volume mount to your GCS bucket in the right folder.
gcloud --project "$PROJECT_ID" beta run services update "$SERVICE_NAME" \
    --region $GCP_REGION \
    --execution-environment gen2 \
    --add-volume=name=php_uploads,type=cloud-storage,bucket="$BUCKET"  \
    --add-volume-mount=volume=php_uploads,mount-path="$MOUNT_PATH"

Ahora, repite este paso para todos los extremos que desees dirigir a Cloud Storage.

También puedes lograr lo mismo desde la IU

  1. En la pestaña “Volúmenes”, crea una vinculación de volúmenes que apunte a tu bucket, del tipo “Bucket de Cloud Storage”, por ejemplo, con el nombre “php_uploads”.
  2. En Contenedores > Activaciones de volúmenes, activa el volumen que acabas de crear en el punto de volumen que solicita tu app. Esto depende del Dockerfile, pero podría verse como var/www/html/uploads/ .

De cualquier manera, si funciona, al editar la nueva revisión de Cloud Run, deberías ver algo como lo siguiente:

6c2bb98fc1b0e077.png

Ahora, prueba la nueva aplicación subiendo una imagen nueva al extremo /upload.php.

Las imágenes deben fluir sin problemas en GCS sin escribir una sola línea de PHP:

70032b216afee2d7.png

¿Qué pasó?

Sucedió algo muy mágico.

Una aplicación antigua con código antiguo sigue haciendo su trabajo. Una nueva pila modernizada nos permite tener todas las imágenes y fotos de nuestra app cómodamente en un bucket de Cloud con estado. Ahora el cielo es el límite:

  • ¿Quieres enviar un correo electrónico cada vez que recibas una imagen con contenido “peligroso” o “desnudos”? Puedes hacerlo sin modificar el código PHP.
  • ¿Quieres usar un modelo multimodal de Gemini cada vez que recibas una imagen para describirla y subir la base de datos con su descripción? Puedes hacerlo sin modificar el código PHP. ¿No me crees? Sigue leyendo el capítulo 7.

Acabamos de desbloquear un gran espacio de oportunidades.

9. Módulo 7: Potencia tu app con Google Gemini

c00425f0ad83b32c.png

Ahora tienes una increíble app de PHP nueva y moderna (como un Fiat 126 del 2024) con almacenamiento en la nube.

¿Para qué sirve?

Requisitos previos

En el capítulo anterior, una solución de modelo nos permitió montar imágenes /uploads/ en GCS, lo que separó de facto la lógica de la app del almacenamiento de imágenes.

En este ejercicio, deberás hacer lo siguiente:

  • Haber completado correctamente el ejercicio del capítulo 6 (almacenamiento)
  • Tener un bucket de GCS con las cargas de imágenes, en el que las personas suban fotos en tu app y las fotos fluyan a tu bucket

Configura una Cloud Function (en Python)

¿Alguna vez te preguntaste cómo implementar una aplicación basada en eventos? Algo como lo siguiente:

  • cuando sucede <event> => enviar un correo electrónico
  • cuando sucede <event> => si <condition> es verdadero, actualiza la base de datos.

El evento puede ser cualquier cosa, desde un nuevo registro disponible en BigQuery, un nuevo objeto que cambió en una carpeta de GCS o un nuevo mensaje que espera en una cola de Pub/Sub.

Google Cloud admite varios paradigmas para lograrlo. En particular, se destacan los siguientes:

En este ejercicio, profundizaremos en Cloud Functions para lograr un resultado bastante espectacular. Además, te proporcionaremos ejercicios opcionales.

Ten en cuenta que el código de muestra se proporciona en .solutions/.

Configura una Cloud Function (🐍 Python)

Estamos intentando crear un GCF muy ambicioso.

  1. Cuando se crea una imagen nueva en GCS… (probablemente porque alguien la subió a la app, pero no solo por eso)
  2. .. llama a Gemini para que la describa y obtén una descripción textual de la imagen .. (sería bueno verificar el MIME y asegurarse de que sea una imagen y no un PDF, MP3 o texto)
  3. .. and update the DB with this description. (es posible que esto requiera aplicar parches a la base de datos para agregar una columna description a la tabla images).

Aplica un parche a la base de datos para agregar description a las imágenes

  1. Abre Cloud SQL Studio:

b92b07c4cba658ef.png

  1. Ingresa tu usuario y contraseña para la base de datos de imágenes.
  2. Inyecta este SQL que agrega una columna para la descripción de una imagen:

ALTER TABLE images ADD COLUMN description TEXT;

3691aced78a6389.png

¡Y listo! Prueba ahora para verificar si funcionó:

SELECT * FROM images;

Deberías ver la nueva columna de descripción:

bed69d6ad0263114.png

Escribe la f(x) de Gemini.

Nota. Esta función se creó con la ayuda de Gemini Code Assist.

Nota. Es posible que se produzcan errores de permisos de IAM al crear esta función. Algunos se documentan a continuación en el párrafo "Posibles errores".

  1. Habilita las APIs
  2. Ve a https://console.cloud.google.com/functions/list.
  3. Haz clic en "Create Function".
  4. Habilita las APIs desde el asistente de APIs:

d22b82658cfd4c48.png

Puedes crear la GCF desde la IU o la línea de comandos. Aquí usaremos la línea de comandos.

Puedes encontrar un posible código en .solutions/.

  1. Crea una carpeta para alojar tu código, p. ej., "gcf/". Ingresa a la carpeta.
  2. Crea un archivo requirements.txt:
google-cloud-storage
google-cloud-aiplatform
pymysql
  1. Crea una función de Python. Código de muestra aquí: gcf/main.py.
#!/usr/bin/env python

"""Complete this"""

from google.cloud import storage
from google.cloud import aiplatform
import vertexai
from vertexai.generative_models import GenerativeModel, Part
import os
import pymysql
import pymysql.cursors

# Replace with your project ID
PROJECT_ID = "your-project-id"
GEMINI_MODEL = "gemini-1.5-pro-002"
DEFAULT_PROMPT = "Generate a caption for this image: "

def gemini_describe_image_from_gcs(gcs_url, image_prompt=DEFAULT_PROMPT):
    pass

def update_db_with_description(image_filename, caption, db_user, db_pass, db_host, db_name):
    pass

def generate_caption(event, context):
    """
    Cloud Function triggered by a GCS event.
    Args:
        event (dict): The dictionary with data specific to this type of event.
        context (google.cloud.functions.Context): The context parameter contains
                                                event metadata such as event ID
                                                and timestamp.
    """
    pass
  1. Envía la función. Puedes usar una secuencia de comandos similar a esta: gcf/push-to-gcf.sh.

Nota 1: Asegúrate de obtener los valores correctos de las variables de entorno o simplemente agrégalos en la parte superior (GS_BUCKET=blah, …):

Nota 2. Esto enviará todo el código local (.), así que asegúrate de incluir tu código en una carpeta específica y de usar .gcloudignore como un profesional para evitar enviar bibliotecas enormes. ( ejemplo).

#!/bin/bash

set -euo pipefail

# add your logic here, for instance:
source .env || exit 2 

echo "Pushing ☁️ f(x)☁ to 🪣 $GS_BUCKET, along with DB config.. (DB_PASS=$DB_PASS)"

gcloud --project "$PROJECT_ID" functions deploy php_amarcord_generate_caption \
    --runtime python310 \
    --region "$GCP_REGION" \
    --trigger-event google.cloud.storage.object.v1.finalized \
    --trigger-resource "$BUCKET" \
    --set-env-vars "DB_HOST=$DB_HOST,DB_NAME=$DB_NAME,DB_PASS=$DB_PASS,DB_USER=$DB_USER" \
    --source . \
    --entry-point generate_caption \
    --gen2

Nota: En este ejemplo, generate_caption será el método invocado, y Cloud Functions le pasará el evento de GCS con toda la información pertinente (nombre del bucket, nombre del objeto, etc.). Dedica tiempo a depurar ese diccionario de Python de eventos.

Prueba la función

Pruebas de unidades

La función tiene muchas partes móviles. Es posible que desees probar todas las unidades.

Puedes encontrar un ejemplo en gcf/test.py.

IU de Cloud Functions

También dedica tiempo a explorar tu función en la IU. Vale la pena explorar cada pestaña, en especial Source (mi favorita), Variables, Trigger y Logs. Pasarás mucho tiempo en Logs para solucionar errores (también consulta los posibles errores en la parte inferior de esta página). Además, asegúrate de revisar Permissions.

cf3ded30d532a2c7.png

Prueba de E2E

Es hora de probar la función de forma manual.

  1. Ve a tu app y accede
  2. Sube una foto (no demasiado grande, ya que hemos tenido problemas con imágenes grandes).
  3. Verifica en la IU que se haya subido la foto.
  4. Verifica en Cloud SQL Studio que se haya actualizado la descripción. Accede y ejecuta esta consulta: SELECT * FROM images.

43a680b12dbbdda0.png

¡Y funciona! También es posible que queramos actualizar el frontend para mostrar esa descripción.

Actualizar PHP para mostrar [opcional]

Demostramos que la app funciona. Sin embargo, sería bueno que los usuarios también pudieran ver esa descripción.

No necesitamos ser expertos en PHP para agregar la descripción a index.php. Este código debería hacer lo siguiente (sí, Gemini también lo escribió para mí):

<?php if (!empty($image['description'])): ?>
    <p class="font-bold">Gemini Caption:</p>
    <p class="italic"><?php echo $image['description']; ?></p>
<?php endif; ?>

Coloca este código dentro de foreach como prefieras.

En los próximos pasos, también veremos una versión de la IU más atractiva gracias a Gemini Code Assist. Una versión atractiva podría verse así:

fdc12de0c88c4464.png

Conclusiones

Obtuviste una Cloud Function que se activa cuando se agregan objetos nuevos a GCS, que puede anotar el contenido de la imagen como lo haría un humano y actualizar automáticamente la base de datos. ¡Guau!

Próximos pasos Podrías seguir el mismo razonamiento para lograr dos grandes funcionalidades.

[Opcional] Agrega más Cloud Functions [pregunta abierta]

Se me ocurren algunas funciones adicionales.

📩 Activador de correo electrónico

Un activador de correo electrónico que te envía un correo electrónico cada vez que alguien envía una foto

  • ¿Con demasiada frecuencia? Agrega una restricción adicional: Una imagen GRANDE o una imagen cuyo contenido de Gemini contenga las palabras "desnudo/desnudos/violento".
  • Considera verificar EventArc para esto.

🚫 Modera automáticamente las fotos inapropiadas

Actualmente, un administrador humano marca las imágenes como "inadecuadas". ¿Qué tal si Gemini se encarga del trabajo pesado y modera el espacio? Agrega una prueba para marcar el contenido del activador como inadecuado y actualizar la BD como aprendimos en la función anterior. Esto significa, básicamente, tomar la función anterior, cambiar la instrucción y actualizar la BD según la respuesta.

Advertencia. La IA generativa tiene resultados impredecibles. Asegúrate de que el "resultado creativo" de Gemini esté "en rieles". Puedes solicitar una respuesta determinística, como una puntuación de confianza de 0 a 1, un JSON, etc. Puedes lograr esto de muchas maneras, por ejemplo: * Usando bibliotecas de Python pydantic, langchain, etc. * Usando Salida estructurada de Gemini.

Nota. Podrías tener VARIAS funciones o una sola instrucción que imponga una respuesta en formato JSON (funciona muy bien con "Salida estructurada de Gemini", como se destacó anteriormente), como la siguiente:

¿Qué instrucción se usaría para generar esta imagen?

{
    "description": "This is the picture of an arrosticino",
    "suitable": TRUE
}

Podrías agregar campos adicionales a la instrucción para obtener estadísticas como: ¿Hay algo bueno en ella? ¿Qué tiene de malo? ¿Reconoces el lugar? ¿Hay algún texto? (El OCR nunca fue tan fácil):

  • goods: "Parece comida deliciosa"
  • bads: "Parece comida poco saludable"
  • OCR: "Da consumare preferibilmente prima del 10 Novembre 2024"
  • location: "Pescara, Lungomare"

Si bien suele ser mejor tener una función N para N resultados, es muy gratificante crear una que haga 10 cosas. Consulta este artículo de Riccardo para saber cómo hacerlo.

Posibles errores (principalmente de IAM o permisos)

La primera vez que desarrollé esta solución, me encontré con algunos problemas de permisos de IAM. Los agregaré aquí para generar empatía y dar algunas ideas sobre cómo solucionarlos.

Error: No hay suficientes permisos para la cuenta de servicio

  1. Ten en cuenta que, para implementar una función de GCF que escucha un bucket de GCS, debes configurar los permisos adecuados para la cuenta de servicio que usas para el trabajo, como se muestra en la siguiente figura:

22f51012fa6b4a24.png

También es posible que debas habilitar las APIs de EventArc unos minutos antes de que estén disponibles por completo.

Error: Falta el invocador de Cloud Run

  1. Otro comentario de la IU para el permiso de GCF es el siguiente ( rol de invocador de Cloud Run):

be72e17294f2d3f3.png

Este error se puede corregir ejecutando el comando que se muestra en la imagen, que es similar a fix-permissions.sh.

Este problema se describe aquí: https://cloud.google.com/functions/docs/securing/authenticating

Error: Se superó el límite de memoria

La primera vez que lo ejecuté, mis registros podrían decir: "Se superó el límite de memoria de 244 MiB con 270 MiB usados. Considera aumentar el límite de memoria. Consulta https://cloud.google.com/functions/docs/configuring/memory". Nuevamente, agrega RAM a tu GCF. Esto es muy fácil de hacer en la IU. Aquí tienes un posible aumento:

bed69d6ad0263114.png

Como alternativa, también puedes corregir tu secuencia de comandos de implementación de Cloud Run para aumentar la MEM/CPU. Este proceso tarda un poco más.

Error: PubSub Published

Al crear un activador con GCF v1, se produjo este error una vez:

e5c338ee35ad4c24.png

Nuevamente, esto se soluciona fácilmente. Ve a IAM y otorga a tu cuenta de servicio el rol de “Publicador de Pub/Sub”.

Error: No se usó Vertex AI

Si recibes este error, haz lo siguiente:

Permission Denied: 403 La API de Vertex AI no se usó en el proyecto YOUR_PROJECT antes o está inhabilitada. Para habilitarla, visita https://console.developers.google.com/apis/api/aiplatform.googleapis.com/overview?project=YOR_PROJECT

Solo debes habilitar las APIs de Vertex AI. La forma más sencilla de habilitar TODAS las APIs necesarias es la siguiente:

  1. https://console.cloud.google.com/vertex-ai
  2. Haz clic en "Habilitar todas las APIs recomendadas".

492f05ac377f3630.png

Error: No se encontró el activador de Eventarc.

Si ves este mensaje, vuelve a implementar la función.

8ec4fc11833d7420.png

Error: 400 Se están aprovisionando agentes de servicio

400 Service agents are being provisioned ( https://cloud.google.com/vertex-ai/docs/general/access-control#service-agents ). Se necesitan agentes de servicio para leer el archivo de Cloud Storage proporcionado. Vuelve a intentarlo en unos minutos.

Si esto sucede, espera un tiempo o consulta a un Googler.

10. Módulo 8: Crea SLO de disponibilidad

En este capítulo, intentamos lograr lo siguiente:

  1. Crea SLIs
  2. Crea SLOs basados en los SLI
  3. Crea alertas basadas en SLOs

f63426182c052123.png

Este es un tema muy importante para el autor, ya que Riccardo trabaja en el área de SRE / DevOps de Google Cloud.

(Pregunta abierta) Crea SLI y SLO para esta app

¿Qué tan buena es una app si no puedes saber cuándo no funciona?

¿Qué es un SLO?

¡Vaya! Google inventó los SLO. Para obtener más información, te sugiero que consultes los siguientes recursos:

Paso 1: Crea el SLI/SLO de disponibilidad

Comencemos con el SLO de disponibilidad, ya que es lo más fácil y, posiblemente, lo más importante que quieras medir.

Afortunadamente, Cloud Run incluye compatibilidad con SLO prediseñados gracias a Istio.

Una vez que tu app esté en Cloud Run, esto será muy sencillo de lograr. A mí me lleva 30 segundos.

  • Ve a tu página de Cloud Run.
  • Haz clic en tu app o selecciónala.
  • Selecciona la pestaña SLOs.
  • Haz clic en "+ Crear SLO".
  • Disponibilidad, basada en solicitudes
  • Continuar
  • Mes del calendario / 99%.
  • Haz clic en "Crear SLO".

e471c7ebdc56cdf6.png

Paso 2: Configura las alertas en este SLO

Te sugiero que crees 2 alertas:

  1. Una con una tasa de consumo baja ("Slowburn") para alertarte por correo electrónico (simula un ticket de prioridad baja).
  2. Una con una tasa de consumo alta ("Fastburn") para alertarte por SMS (simula un ticket de prioridad alta o un localizador)

Ve a tu SLO tab anterior.

Repite esta acción dos veces:

314bfd6b9ef0a260.png

  • Haz clic en "Crear alerta de SLO" (el botón de 🔔 con un signo más en el interior, a la derecha).
  • Duración de la visualización y límite de tasa de gasto:
  • [FAST]. Primeros: 60 min / 10 x
  • [LENTO]. Segundo: 720 min / 2 x
  • Canal de notificación: Haz clic en Administrar canales de notificaciones.
  • Primero, "Correo electrónico" -> Agregar nuevo -> …
  • En segundo lugar, "SMS" -> Agregar nuevo -> Verificar en el teléfono.
  • Sugerencia: Me gusta usar emojis en los nombres. Es divertido para las demostraciones.
  • Cuando termines, haz clic en la X grande de la esquina superior derecha.
  • Selecciona el teléfono primero (rápido) y el correo electrónico después (lento).
  • Agrega documentación de muestra, como la siguiente:
  • [PHP Amarcord] Riccardo told me to type sudo reboot or to check documentation in http://example.com/playbooks/1.php but I guess he was joking.

¡Bingo!

Resultado final

Podemos considerar que este ejercicio finalizó una vez que tengas 1 SLO en funcionamiento y 2 alertas para tu disponibilidad, y que se envíen alertas a tu correo electrónico y a tu teléfono.

Si quieres, puedes agregar una latencia (y te recomiendo que lo hagas) o incluso una más compleja. En el caso de la latencia, elige una que consideres razonable. Si tienes dudas, elige 200 ms.

11. Próximos pasos

Completaste TODO. ¿Qué falta?

Algunas ideas para reflexionar:

Juega con Gemini

Puedes usar Gemini de dos maneras:

  1. Vertex AI. La "forma empresarial", entrelazada con tu GCP, que exploramos en el capítulo 7 (GCF + Gemini). Toda la autenticación funciona mágicamente y los servicios se interconectan de forma impecable.
  2. IA de Google. La "forma del consumidor". Obtén una clave de la API de Gemini aquí y comienza a compilar pequeños secuencias de comandos que se pueden vincular a cualquier carga de trabajo que ya tengas (trabajo propietario, otras nubes, localhost, etc.). Solo tienes que sustituir tu clave de API y el código comenzará a funcionar mágicamente.

Te recomendamos que explores el (2) con tus propios proyectos personales.

UI Lifting

No soy bueno con las IU. Pero Gemini sí lo es. Puedes tomar una sola página PHP y decir algo como lo siguiente:

I have a VERY old PHP application. I want to touch it as little as possible. Can you help me:

1. add some nice CSS to it, a single static include for tailwind or similar, whatever you prefer
2. Transform the image print with description into cards, which fit 4 per line in the canvas?

Here's the code:

-----------------------------------
[Paste your PHP page, for instance index.php - mind the token limit!]

Puedes obtenerlo fácilmente en menos de 5 minutos, con una sola compilación de Cloud Build. :)

La respuesta de Gemini fue perfecta (es decir, no tuve que cambiar nada):

8a3d5fe37ec40bf8.png

Y aquí se muestra el nuevo diseño en la app personal del autor:

81620eb90ae3229a.png

Nota: El código se pega como imagen, ya que no queremos alentarte a que lo copies, sino a que le pidas a Gemini que lo escriba por ti, con tus propias restricciones creativas de IU o frontend. Confía en mí, solo tendrás que hacer cambios muy menores después.

Seguridad

Proteger correctamente esta app no es un objetivo de este taller de 4 horas, ya que aumentaría el tiempo para completarlo en 1 o 2 órdenes de magnitud.

Sin embargo, este tema es muy importante. Recopilamos algunas ideas en SECURITY.

12. ¡Felicitaciones!

¡Felicitaciones! 🎉🎉🎉 Modernizaste correctamente tu aplicación heredada de PHP con Google Cloud.

24cb9a39b1841fbd.png

En resumen, en este codelab aprendiste lo siguiente:

  • Cómo implementar una base de datos en Google Cloud SQL y cómo migrar tu base de datos existente a ella
  • Cómo alojar en contenedores tu aplicación PHP con Docker y Buildpacks, y almacenar su imagen en Artifact Registry de Google Cloud
  • Cómo implementar tu app alojada en contenedores en Cloud Run y hacer que se ejecute con Cloud SQL
  • Cómo almacenar o usar de forma secreta parámetros de configuración sensibles (como la contraseña de la base de datos) con Google Secret Manager
  • Cómo configurar tu canalización de CI/CD con Google Cloud Build para compilar e implementar automáticamente tu app en PHP cada vez que envíes código a tu repositorio de GitHub
  • Cómo usar Cloud Storage para "cloudificar" los recursos de tu app
  • Cómo aprovechar las tecnologías sin servidores para crear flujos de trabajo increíbles en Google Cloud sin modificar el código de tu app
  • Usa las capacidades multimodales de Gemini para un caso de uso adecuado.
  • Implementa los principios de SRE en Google Cloud

Este es un excelente comienzo para tu recorrido por la modernización de aplicaciones con Google Cloud.

🔁 Comentarios

Si quieres contarnos sobre tu experiencia con este taller, considera completar este formulario de comentarios.

También aceptamos tus comentarios y PRs para los fragmentos de código de los que te sientas particularmente orgulloso.

🙏 Gracias

El autor desea agradecer a Mirko Gilioli y Maurizio Ipsale de Datatonic por su ayuda en la redacción y las pruebas de la solución.