1. Introducción
Descripción general
Los servicios de Cloud Run son una buena opción para contenedores que se ejecutan de forma indefinida escuchando solicitudes HTTP, mientras que los trabajos de Cloud Run son más adecuados para contenedores que se ejecutan hasta su finalización (en la actualidad, hasta 24 horas) y no entregan solicitudes. Por ejemplo, las actividades como procesar registros de una base de datos, procesar una lista de archivos desde un bucket de Cloud Storage o realizar una operación de larga duración (por ejemplo, calcular pi), funcionarían bien si se implementaran como un trabajo de Cloud Run.
Los trabajos no tienen la capacidad de entregar solicitudes ni escuchar en un puerto. Esto quiere decir que, a diferencia de los servicios de Cloud Run, los trabajos no deben empaquetar un servidor web. Además, los contenedores de los trabajos deben cerrarse cuando terminan.
En los trabajos de Cloud Run, se pueden especificar diversas tareas para ejecutar varias copias de tus contenedores en paralelo. Cada tarea representa una copia en ejecución del contenedor. Usar varias tareas es útil si cada una puede procesar de forma independiente un subconjunto de tus datos. Por ejemplo, procesar 10,000 registros de Cloud SQL o 10,000 archivos de Cloud Storage podría ser más rápido con 10 tareas que procesen 1,000 registros o archivos en paralelo.
El uso de trabajos de Cloud Run es un proceso de dos pasos:
- Crea un trabajo: Esto encapsula toda la configuración necesaria para ejecutar el trabajo, como la imagen de contenedor, la región y las variables de entorno.
- Ejecutar el trabajo: Esto crea una nueva ejecución del trabajo. De manera opcional, configura tu trabajo para que se ejecute según un programa con Cloud Scheduler.
En este codelab, primero explorarás una aplicación de Node.js para tomar capturas de pantalla de páginas web y almacenarlas en Cloud Storage. Luego, compilarás una imagen de contenedor para la aplicación, la ejecutarás en trabajos de Cloud Run, actualizarás el trabajo para que procese más páginas web y lo ejecutarás según un programa con Cloud Scheduler.
Qué aprenderás
- Cómo usar una app para tomar capturas de pantalla de páginas web
- Cómo compilar una imagen de contenedor para la aplicación
- Cómo crear un trabajo de Cloud Run para la aplicación
- Cómo ejecutar la aplicación como un trabajo de Cloud Run
- Cómo actualizar el trabajo
- Cómo programar el trabajo con Cloud Scheduler
2. Configuración y requisitos
Cómo configurar el entorno a tu propio ritmo
- 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.
- 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.
- 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.
Inicie Cloud Shell
Si bien Google Cloud y Spanner se pueden operar de manera remota desde tu laptop, en este codelab usarás Google Cloud Shell, un entorno de línea de comandos que se ejecuta en la nube.
En Google Cloud Console, haz clic en el ícono de Cloud Shell en la barra de herramientas en la parte superior derecha:
El aprovisionamiento y la conexión al entorno deberían tomar solo unos minutos. Cuando termine el proceso, debería ver algo como lo siguiente:
Esta máquina virtual está cargada con todas las herramientas de desarrollo que necesitarás. Ofrece un directorio principal persistente de 5 GB y se ejecuta en Google Cloud, lo que permite mejorar considerablemente el rendimiento de la red y la autenticación. Todo tu trabajo en este codelab se puede hacer en un navegador. No es necesario que instales nada.
Configura gcloud
Establece en Cloud Shell el ID del proyecto y la región en la que deseas implementar el trabajo de Cloud Run. Guárdalos como variables PROJECT_ID
y REGION
. En el futuro, podrás elegir una región de una de las ubicaciones de Cloud Run.
PROJECT_ID=[YOUR-PROJECT-ID] REGION=us-central1 gcloud config set core/project $PROJECT_ID
Habilita las APIs
Habilita todos los servicios necesarios con el siguiente comando:
gcloud services enable \ artifactregistry.googleapis.com \ cloudbuild.googleapis.com \ run.googleapis.com
3. Obtén el código
Primero, explorarás una aplicación de Node.js para tomar capturas de pantalla de páginas web y almacenarlas en Cloud Storage. Luego, compilarás una imagen de contenedor para la aplicación y la ejecutarás como un trabajo en Cloud Run.
En Cloud Shell, ejecuta el siguiente comando para clonar el código de la aplicación desde este repositorio:
git clone https://github.com/GoogleCloudPlatform/jobs-demos.git
Ve al directorio que contiene la aplicación:
cd jobs-demos/screenshot
Deberías ver el siguiente diseño de archivo:
screenshot | ├── Dockerfile ├── README.md ├── screenshot.js ├── package.json
A continuación, se incluye una breve descripción de cada página.
screenshot.js
contiene el código de Node.js para la aplicación.package.json
define las dependencias de la biblioteca.Dockerfile
define la imagen de contenedor.
4. Cómo explorar el código
Para explorar el código, haz clic en el botón Open Editor
ubicado en la parte superior de la ventana de Cloud Shell a fin de usar el editor de texto integrado.
A continuación, se incluye una explicación breve de cada archivo.
screenshot.js
screenshot.js
primero agrega Puppeteer y Cloud Storage como dependencias. Puppeteer es una biblioteca de Node.js que puedes usar para tomar capturas de pantalla de páginas web:
const puppeteer = require('puppeteer'); const {Storage} = require('@google-cloud/storage');
Hay una función initBrowser
para inicializar Puppeteer y una función takeScreenshot
a fin de tomar capturas de pantalla de una URL determinada:
async function initBrowser() { console.log('Initializing browser'); return await puppeteer.launch(); } async function takeScreenshot(browser, url) { const page = await browser.newPage(); console.log(`Navigating to ${url}`); await page.goto(url); console.log(`Taking a screenshot of ${url}`); return await page.screenshot({ fullPage: true }); }
A continuación, hay una función para obtener o crear un bucket de Cloud Storage, y otra destinada a subir la captura de pantalla de una página web a un bucket:
async function createStorageBucketIfMissing(storage, bucketName) { console.log(`Checking for Cloud Storage bucket '${bucketName}' and creating if not found`); const bucket = storage.bucket(bucketName); const [exists] = await bucket.exists(); if (exists) { // Bucket exists, nothing to do here return bucket; } // Create bucket const [createdBucket] = await storage.createBucket(bucketName); console.log(`Created Cloud Storage bucket '${createdBucket.name}'`); return createdBucket; } async function uploadImage(bucket, taskIndex, imageBuffer) { // Create filename using the current time and task index const date = new Date(); date.setMinutes(date.getMinutes() - date.getTimezoneOffset()); const filename = `${date.toISOString()}-task${taskIndex}.png`; console.log(`Uploading screenshot as '${filename}'`) await bucket.file(filename).save(imageBuffer); }
Por último, la función main
es el punto de entrada:
async function main(urls) { console.log(`Passed in urls: ${urls}`); const taskIndex = process.env.CLOUD_RUN_TASK_INDEX || 0; const url = urls[taskIndex]; if (!url) { throw new Error(`No url found for task ${taskIndex}. Ensure at least ${parseInt(taskIndex, 10) + 1} url(s) have been specified as command args.`); } const bucketName = process.env.BUCKET_NAME; if (!bucketName) { throw new Error('No bucket name specified. Set the BUCKET_NAME env var to specify which Cloud Storage bucket the screenshot will be uploaded to.'); } const browser = await initBrowser(); const imageBuffer = await takeScreenshot(browser, url).catch(async err => { // Make sure to close the browser if we hit an error. await browser.close(); throw err; }); await browser.close(); console.log('Initializing Cloud Storage client') const storage = new Storage(); const bucket = await createStorageBucketIfMissing(storage, bucketName); await uploadImage(bucket, taskIndex, imageBuffer); console.log('Upload complete!'); } main(process.argv.slice(2)).catch(err => { console.error(JSON.stringify({severity: 'ERROR', message: err.message})); process.exit(1); });
Ten en cuenta lo siguiente sobre el método main
:
- Las URLs se pasan como argumentos.
- El nombre del bucket se pasa como la variable de entorno
BUCKET_NAME
definida por el usuario. Este nombre debe ser único a nivel global en todo Google Cloud. - Los trabajos de Cloud Run pasan una variable de entorno
CLOUD_RUN_TASK_INDEX
. Los trabajos de Cloud Run pueden ejecutar varias copias de la aplicación como tareas únicas.CLOUD_RUN_TASK_INDEX
representa el índice de la tarea en ejecución. El valor predeterminado es cero cuando el código se ejecuta fuera de los trabajos de Cloud Run. Cuando la aplicación se ejecuta como varias tareas, cada tarea o contenedor recoge la URL de la que es responsable, toma una captura de pantalla y guarda la imagen en el bucket.
package.json
El archivo package.json
define la aplicación y especifica las dependencias de Cloud Storage y Puppeteer:
{ "name": "screenshot", "version": "1.0.0", "description": "Create a job to capture screenshots", "main": "screenshot.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "Google LLC", "license": "Apache-2.0", "dependencies": { "@google-cloud/storage": "^5.18.2", "puppeteer": "^13.5.1" } }
Dockerfile
Dockerfile
define la imagen de contenedor para la aplicación con todas las bibliotecas y dependencias requeridas:
FROM ghcr.io/puppeteer/puppeteer:16.1.0 COPY package*.json ./ RUN npm ci --omit=dev COPY . . ENTRYPOINT ["node", "screenshot.js"]
5. Implementa un trabajo
Antes de crear un trabajo, debes crear una cuenta de servicio que usarás para ejecutarlo.
gcloud iam service-accounts create screenshot-sa --display-name="Screenshot app service account"
Otorga el rol storage.admin
a la cuenta de servicio para que pueda usarse en la creación de buckets y objetos.
gcloud projects add-iam-policy-binding $PROJECT_ID \ --role roles/storage.admin \ --member serviceAccount:screenshot-sa@$PROJECT_ID.iam.gserviceaccount.com
Ya está todo listo para implementar un trabajo de Cloud Run que incluya la configuración necesaria para ejecutar el trabajo.
gcloud beta run jobs deploy screenshot \ --source=. \ --args="https://example.com" \ --args="https://cloud.google.com" \ --tasks=2 \ --task-timeout=5m \ --region=$REGION \ --set-env-vars=BUCKET_NAME=screenshot-$PROJECT_ID \ --service-account=screenshot-sa@$PROJECT_ID.iam.gserviceaccount.com
Usa una implementación basada en el código fuente y crea un trabajo de Cloud Run sin ejecutarlo.
Observa cómo se pasan las páginas web como argumentos. El nombre del bucket para guardar las capturas de pantalla se pasa como una variable de entorno.
Puedes ejecutar varias copias de tu contenedor en paralelo si especificas una cantidad de tareas que se ejecutarán con la marca --tasks
. Cada tarea representa una copia en ejecución del contenedor. Usar varias tareas es útil si cada una puede procesar de forma independiente un subconjunto de tus datos. Para facilitar esto, cada tarea conoce su índice, que se almacena en la variable de entorno CLOUD_RUN_TASK_INDEX
. Tu código es responsable de determinar qué tarea controla qué subconjunto de los datos. Observa --tasks=2
en esta muestra. Esto garantiza que se ejecuten 2 contenedores para las 2 URLs que queremos procesar.
Cada tarea puede ejecutarse hasta por 24 horas. Puedes disminuir este tiempo de espera con la marca --task-timeout
, como lo hicimos en este ejemplo. Todas las tareas deben completarse correctamente para que el trabajo se complete de la misma manera. Según la configuración predeterminada, no se reintentan las tareas con errores, pero puedes configurar tareas para que se vuelvan a intentar cuando fallen. Si alguna tarea excede la cantidad de reintentos, todo el trabajo fallará.
De forma predeterminada, tu trabajo se ejecutará con tantas tareas en paralelo como sea posible. Esta cantidad será igual a la cantidad de tareas para tu trabajo, hasta un máximo de 100. Recomendamos que la ejecución en paralelo sea más baja para los trabajos que acceden a un backend con escalabilidad limitada. Por ejemplo, una base de datos que admite una cantidad limitada de conexiones activas. Puedes reducir el paralelismo con la marca --parallelism
.
6. Ejecutar un trabajo
Antes de ejecutar el trabajo, enuméralo para ver que se creó:
gcloud run jobs list ✔ JOB: screenshot REGION: us-central LAST RUN AT: CREATED: 2022-02-22 12:20:50 UTC
Ejecuta el trabajo con el siguiente comando:
gcloud run jobs execute screenshot --region=$REGION
Esto ejecuta el trabajo. Puedes enumerar las ejecuciones actuales y anteriores:
gcloud run jobs executions list --job screenshot --region=$REGION ... JOB: screenshot EXECUTION: screenshot-znkmm REGION: $REGION RUNNING: 1 COMPLETE: 1 / 2 CREATED: 2022-02-22 12:40:42 UTC
Describe la ejecución. Deberías ver la marca de verificación verde y el mensaje tasks completed successfully
:
gcloud run jobs executions describe screenshot-znkmm --region=$REGION ✔ Execution screenshot-znkmm in region $REGION 2 tasks completed successfully Image: $REGION-docker.pkg.dev/$PROJECT_ID/containers/screenshot at 311b20d9... Tasks: 2 Args: https://example.com https://cloud.google.com Memory: 1Gi CPU: 1000m Task Timeout: 3600s Parallelism: 2 Service account: 11111111-compute@developer.gserviceaccount.com Env vars: BUCKET_NAME screenshot-$PROJECT_ID
También puede consultar la página de trabajos de Cloud Run de Cloud Console para ver el estado:
Si verificas el bucket de Cloud Storage, deberías ver los dos archivos de captura de pantalla creados:
A veces, es probable que debas detener una ejecución antes de que se complete. Puede ser porque te diste cuenta de que necesitas ejecutar el trabajo con parámetros diferentes o porque hay un error en el código y no deseas usar tiempo de procesamiento innecesario.
Para detener la ejecución de un trabajo, debes borrar la ejecución:
gcloud run jobs executions delete screenshot-znkmm --region=$REGION
7. Actualiza un trabajo
La próxima ejecución no recogerá automáticamente las versiones nuevas del contenedor mediante Cloud Run. Si cambias el código de tu trabajo, debes volver a compilar el contenedor y actualizar el trabajo. Las imágenes etiquetadas te ayudarán a identificar qué versión de la imagen se está usando actualmente.
Del mismo modo, también debes actualizar el trabajo si quieres actualizar algunas de las variables de configuración. Las ejecuciones posteriores del trabajo usarán el contenedor y la configuración nuevos.
Actualiza el trabajo y cambia las páginas de las que la app toma capturas de pantalla en la marca --args
. También actualiza la marca --tasks
para reflejar la cantidad de páginas.
gcloud run jobs update screenshot \ --args="https://www.pinterest.com" \ --args="https://www.apartmenttherapy.com" \ --args="https://www.google.com" \ --region=$REGION \ --tasks=3
Vuelve a ejecutar el trabajo. Pasa este tiempo a la marca --wait
para esperar a que finalicen las ejecuciones:
gcloud run jobs execute screenshot --region=$REGION --wait
Después de unos segundos, debería ver 3 capturas de pantalla más en el bucket:
8. Cómo programar un trabajo
Hasta ahora, estás ejecutando trabajos de forma manual. En una situación real, es probable que quieras ejecutar trabajos en respuesta a un evento o en un programa. Veamos cómo ejecutar el trabajo de captura de pantalla de manera programada con Cloud Scheduler.
Primero, asegúrate de que la API de Cloud Scheduler esté habilitada:
gcloud services enable cloudscheduler.googleapis.com
Ve a la página de detalles de los trabajos de Cloud Run y haz clic en la sección Triggers
:
Selecciona el botón Add Scheduler Trigger
:
Se abrirá un panel a la derecha. Crea un trabajo de Scheduler para ejecutarlo todos los días a las 9:00 a.m. con esta configuración y selecciona Continue
:
En la página siguiente, selecciona la cuenta de servicio de procesamiento predeterminada y elige Create
:
Ahora, deberías ver un nuevo activador de Cloud Scheduler creado:
Haz clic en View Details
para ir a la página de Cloud Scheduler.
Puedes esperar hasta las 9 a.m. para que se inicie el programador o puedes activar Cloud Scheduler de forma manual seleccionando Force Run
:
Después de unos segundos, deberías ver que el trabajo de Cloud Scheduler se ejecutó de forma correcta:
También deberías ver 3 capturas de pantalla más que agregó la llamada desde Cloud Scheduler:
9. Felicitaciones
¡Felicitaciones! Completaste el codelab.
Realiza una limpieza (opcional)
Para evitar incurrir en cargos, es una buena idea limpiar los recursos.
Si no necesitas el proyecto, simplemente puedes borrarlo:
gcloud projects delete $PROJECT_ID
Si lo necesitas, puedes borrar recursos de forma individual.
Borra el código fuente:
rm -rf ~/jobs-demos/
Borra el repositorio de Artifact Registry:
gcloud artifacts repositories delete containers --location=$REGION
Borra la cuenta de servicio:
gcloud iam service-accounts delete screenshot-sa@$PROJECT_ID.iam.gserviceaccount.com
Borra el trabajo de Cloud Run:
gcloud run jobs delete screenshot --region=$REGION
Borra el trabajo de Cloud Scheduler:
gcloud scheduler jobs delete screenshot-scheduler-trigger --location=$REGION
Borra el bucket de Cloud Storage:
gcloud storage rm --recursive gs://screenshot-$PROJECT_ID
Temas abordados
- Cómo usar una app para tomar capturas de pantalla de páginas web
- Cómo compilar una imagen de contenedor para la aplicación
- Cómo crear un trabajo de Cloud Run para la aplicación
- Cómo ejecutar la aplicación como un trabajo de Cloud Run
- Cómo actualizar el trabajo
- Cómo programar el trabajo con Cloud Scheduler