Introduzione ai job Cloud Run

1. Introduzione

1965fab24c502bd5.png

Panoramica

I servizi Cloud Run sono adatti ai container che vengono eseguiti a tempo indeterminato in ascolto di richieste HTTP, mentre i job Cloud Run sono più adatti ai container che vengono eseguiti fino al completamento (attualmente fino a 24 ore) e non gestiscono le richieste. Ad esempio, l'elaborazione di record da un database, l'elaborazione di un elenco di file da un bucket Cloud Storage o un'operazione a lunga esecuzione, come il calcolo di Pi greco, funzionerebbe bene se implementata come job Cloud Run.

I job non hanno la possibilità di gestire le richieste o rimanere in ascolto su una porta. Ciò significa che, a differenza dei servizi Cloud Run, i job non devono includere un server web. I container dei job devono invece uscire al termine.

Nei job Cloud Run, puoi eseguire più copie del container in parallelo specificando un numero di attività. Ogni attività rappresenta una copia in esecuzione del contenitore. L'utilizzo di più attività è utile se ogni attività può elaborare in modo indipendente un sottoinsieme dei dati. Ad esempio, l'elaborazione di 10.000 record da Cloud SQL o 10.000 file da Cloud Storage potrebbe essere eseguita più rapidamente con 10 attività che elaborano 1000 record o file ciascuna in parallelo.

L'utilizzo dei job Cloud Run è un processo in due fasi:

  1. Crea un job:questo include tutta la configurazione necessaria per eseguire il job, ad esempio l'immagine container, la regione e le variabili di ambiente.
  2. Esegui il job:viene creata una nuova esecuzione del job. Se vuoi, configura il job in modo che venga eseguito in base a una pianificazione utilizzando Cloud Scheduler.

In questo codelab, esplorerai innanzitutto un'applicazione Node.js per acquisire screenshot di pagine web e archiviarli in Cloud Storage. Quindi, crei un'immagine container per l'applicazione, la esegui sui job Cloud Run, aggiorni il job per elaborare più pagine web ed esegui il job in base a una pianificazione con Cloud Scheduler.

Obiettivi didattici

  • Come utilizzare un'app per acquisire screenshot di pagine web.
  • Come creare un'immagine container per l'applicazione.
  • Come creare un job Cloud Run per l'applicazione.
  • Come eseguire l'applicazione come job Cloud Run.
  • Come aggiornare il job.
  • Come pianificare il job con Cloud Scheduler.

2. Configurazione e requisiti

Configurazione dell'ambiente autonomo

  1. Accedi alla console Google Cloud e crea un nuovo progetto o riutilizzane uno esistente. Se non hai già un account Gmail o Google Workspace, devi crearne uno.

295004821bab6a87.png

37d264871000675d.png

96d86d3d5655cdbe.png

  • Il nome del progetto è il nome visualizzato per i partecipanti a questo progetto. È una stringa di caratteri non utilizzata dalle API di Google. Puoi sempre aggiornarlo.
  • L'ID progetto è univoco in tutti i progetti Google Cloud ed è immutabile (non può essere modificato dopo l'impostazione). La console Cloud genera automaticamente una stringa univoca, di solito non ti interessa di cosa si tratta. Nella maggior parte dei codelab, dovrai fare riferimento all'ID progetto (in genere identificato come PROJECT_ID). Se non ti piace l'ID generato, puoi generarne un altro casuale. In alternativa, puoi provare a crearne uno e vedere se è disponibile. Non può essere modificato dopo questo passaggio e rimane per tutta la durata del progetto.
  • Per tua informazione, esiste un terzo valore, un numero di progetto, utilizzato da alcune API. Scopri di più su tutti e tre questi valori nella documentazione.
  1. Successivamente, devi abilitare la fatturazione in Cloud Console per utilizzare le risorse/API Cloud. Completare questo codelab non costa molto, se non nulla. Per arrestare le risorse ed evitare addebiti oltre a quelli previsti in questo tutorial, puoi eliminare le risorse che hai creato o il progetto. I nuovi utenti di Google Cloud possono usufruire del programma prova senza costi di 300$.

Avvia Cloud Shell

Anche se Google Cloud può essere gestito da remoto dal tuo laptop, in questo codelab utilizzerai Google Cloud Shell, un ambiente a riga di comando in esecuzione nel cloud.

Nella console Google Cloud, fai clic sull'icona di Cloud Shell nella barra degli strumenti in alto a destra:

Attiva Cloud Shell

Bastano pochi istanti per eseguire il provisioning e connettersi all'ambiente. Al termine, dovresti vedere un risultato simile a questo:

Screenshot del terminale Google Cloud Shell che mostra che l'ambiente è connesso

Questa macchina virtuale è caricata con tutti gli strumenti per sviluppatori di cui avrai bisogno. Offre una home directory permanente da 5 GB e viene eseguita su Google Cloud, migliorando notevolmente le prestazioni e l'autenticazione della rete. Tutto il lavoro in questo codelab può essere svolto all'interno di un browser. Non devi installare nulla.

Configura gcloud

In Cloud Shell, imposta l'ID progetto e la regione in cui vuoi eseguire il deployment del job Cloud Run. Salvali come variabili PROJECT_ID e REGION. In futuro potrai scegliere una regione tra le località di Cloud Run.

PROJECT_ID=[YOUR-PROJECT-ID]
REGION=us-central1
gcloud config set core/project $PROJECT_ID

Abilita API

Attiva tutti i servizi necessari:

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

3. Ottieni il codice

Per prima cosa, esplori un'applicazione Node.js per acquisire screenshot di pagine web e archiviarli in Cloud Storage. Successivamente, crei un'immagine container per l'applicazione e la esegui come job su Cloud Run.

Da Cloud Shell, esegui questo comando per clonare il codice dell'applicazione da questo repository:

git clone https://github.com/GoogleCloudPlatform/jobs-demos.git

Vai alla directory contenente l'applicazione:

cd jobs-demos/screenshot

Dovresti vedere questo layout del file:

screenshot
 |
 ├── Dockerfile
 ├── README.md
 ├── screenshot.js
 ├── package.json

Ecco una breve descrizione di ogni file:

  • screenshot.js contiene il codice Node.js per l'applicazione.
  • package.json definisce le dipendenze della libreria.
  • Dockerfile definisce l'immagine container.

4. Esplora il codice

Per esplorare il codice, utilizza l'editor di testo integrato facendo clic sul pulsante Open Editor nella parte superiore della finestra di Cloud Shell.

15a2cdc9b7f6dfc6.png

Ecco una breve spiegazione di ogni file.

screenshot.js

screenshot.js aggiunge prima Puppeteer e Cloud Storage come dipendenze. Puppeteer è una libreria Node.js che utilizzi per acquisire screenshot di pagine web:

const puppeteer = require('puppeteer');
const {Storage} = require('@google-cloud/storage');

Esiste una funzione initBrowser per inizializzare Puppeteer e una funzione takeScreenshot per acquisire screenshot di un determinato URL:

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
  });
}

Successivamente, c'è una funzione per ottenere o creare un bucket Cloud Storage e un'altra per caricare lo screenshot di una pagina web in 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);
}

Infine, la funzione main è l'entry point:

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);
});

Tieni presente quanto segue in merito al metodo main:

  • Gli URL vengono passati come argomenti.
  • Il nome del bucket viene passato come variabile di ambiente BUCKET_NAME definita dall'utente. Il nome del bucket deve essere univoco a livello globale in tutto Google Cloud.
  • Una variabile di ambiente CLOUD_RUN_TASK_INDEX viene passata dai job Cloud Run. I job Cloud Run possono eseguire più copie dell'applicazione come attività uniche. CLOUD_RUN_TASK_INDEX rappresenta l'indice dell'attività in esecuzione. Il valore predefinito è zero quando il codice viene eseguito al di fuori dei job Cloud Run. Quando l'applicazione viene eseguita come più attività, ogni attività/container recupera l'URL di cui è responsabile, acquisisce uno screenshot e salva l'immagine nel bucket.

package.json

Il file package.json definisce l'applicazione e specifica le dipendenze per Cloud Storage e 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 definisce l'immagine container per l'applicazione con tutte le librerie e le dipendenze richieste:

FROM ghcr.io/puppeteer/puppeteer:16.1.0
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
ENTRYPOINT ["node", "screenshot.js"]

5. Esegui il deployment di un job

Prima di creare un job, devi creare un service account che utilizzerai per eseguirlo.

gcloud iam service-accounts create screenshot-sa --display-name="Screenshot app service account"

Concedi il ruolo storage.admin al service account, in modo che possa essere utilizzato per creare bucket e oggetti.

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --role roles/storage.admin \
  --member serviceAccount:screenshot-sa@$PROJECT_ID.iam.gserviceaccount.com

Ora puoi eseguire il deployment di un job Cloud Run che include la configurazione necessaria per eseguire il job.

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-$RANDOM \
  --service-account=screenshot-sa@$PROJECT_ID.iam.gserviceaccount.com

Utilizza il deployment basato sull'origine e crea un job Cloud Run senza eseguirlo.

Nota come le pagine web vengono trasmesse come argomenti. Il nome del bucket in cui salvare gli screenshot viene passato come variabile di ambiente.

Puoi eseguire più copie del contenitore in parallelo specificando un numero di attività da eseguire con il flag --tasks. Ogni attività rappresenta una copia in esecuzione del contenitore. L'utilizzo di più attività è utile se ogni attività può elaborare in modo indipendente un sottoinsieme dei dati. Per facilitare questa operazione, ogni attività è a conoscenza del proprio indice, che viene memorizzato nella variabile di ambiente CLOUD_RUN_TASK_INDEX. Il tuo codice è responsabile della determinazione di quale attività gestisce quale sottoinsieme dei dati. Nota --tasks=2 in questo esempio. In questo modo, vengono eseguiti due container per i due URL che vogliamo elaborare.

Ogni attività può essere eseguita per un massimo di 24 ore. Puoi ridurre questo timeout utilizzando il flag --task-timeout, come abbiamo fatto in questo esempio. Tutte le attività devono avere esito positivo affinché il job venga completato correttamente. Per impostazione predefinita, le attività non riuscite non vengono riprovate. Puoi configurare i tentativi per le attività non riuscite. Se un'attività supera il numero di tentativi, l'intero job non riesce.

Per impostazione predefinita, il job verrà eseguito con il maggior numero possibile di attività in parallelo. Sarà uguale al numero di attività per il job, fino a un massimo di 100. Potresti voler impostare un parallelismo inferiore per i job che accedono a un backend con scalabilità limitata. Ad esempio, un database che supporta un numero limitato di connessioni attive. Puoi ridurre il parallelismo con il flag --parallelism.

6. Esegui un job

Prima di eseguire il job, elencalo per verificare che sia stato creato:

gcloud run jobs list

✔
JOB: screenshot
REGION: us-central
LAST RUN AT:
CREATED: 2022-02-22 12:20:50 UTC

Esegui il job con il seguente comando:

gcloud run jobs execute screenshot --region=$REGION

Viene eseguito il job. Puoi elencare le esecuzioni attuali e passate:

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

Descrivi l'esecuzione. Dovresti vedere il segno di spunta verde e il messaggio 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-$RANDOM

Puoi anche controllare la pagina dei job Cloud Run di Cloud Console per visualizzare lo stato:

1afde14d65f0d9ce.png

Se controlli il bucket Cloud Storage, dovresti vedere i due file di screenshot creati:

7c4d355f6f65106.png

A volte potrebbe essere necessario interrompere un'esecuzione prima che venga completata, ad esempio perché ti sei reso conto che devi eseguire il job con parametri diversi o perché c'è un errore nel codice e non vuoi utilizzare tempo di calcolo non necessario.

Per interrompere l'esecuzione di un job, devi eliminarla:

gcloud run jobs executions delete screenshot-znkmm --region=$REGION

7. Aggiornare un job

Le nuove versioni del container non vengono selezionate automaticamente dai job Cloud Run nella successiva esecuzione. Se modifichi il codice del job, devi ricompilare il container e aggiornare il job. L'utilizzo di immagini con tag ti aiuterà a identificare la versione dell'immagine attualmente in uso.

Allo stesso modo, devi aggiornare il job se vuoi aggiornare alcune variabili di configurazione. Le esecuzioni successive del job utilizzeranno il nuovo contenitore e le nuove impostazioni di configurazione.

Aggiorna il job e modifica le pagine di cui l'app acquisisce screenshot nel flag --args. Aggiorna anche il flag --tasks in modo che rifletta il numero di pagine.

gcloud run jobs update screenshot \
  --args="https://www.pinterest.com" \
  --args="https://www.apartmenttherapy.com" \
  --args="https://www.google.com" \
  --region=$REGION \
  --tasks=3

Esegui di nuovo il job. Passa questo periodo di tempo nel flag --wait per attendere il completamento delle esecuzioni:

gcloud run jobs execute screenshot --region=$REGION --wait

Dopo alcuni secondi, dovresti vedere altri tre screenshot aggiunti al bucket:

ed0cbe0b5a5f9144.png

8. Pianificare un job

Finora hai eseguito i job manualmente. In uno scenario reale, probabilmente vuoi eseguire i job in risposta a un evento o in base a una pianificazione. Vediamo come eseguire il job di screenshot in base a una pianificazione utilizzando Cloud Scheduler.

Innanzitutto, assicurati che l'API Cloud Scheduler sia abilitata:

gcloud services enable cloudscheduler.googleapis.com

Vai alla pagina dei dettagli dei job Cloud Run e fai clic sulla sezione Triggers:

3ae456368905472f.png

Seleziona il pulsante Add Scheduler Trigger:

48cbba777f75e1eb.png

Sulla destra si apre un riquadro. Crea un job di pianificazione da eseguire ogni giorno alle 9:00 con questa configurazione e seleziona Continue:

81fd098be0db216.png

Nella pagina successiva, seleziona il service account predefinito di Compute e seleziona Create:

fe479501dfb91f9f.png

Ora dovresti vedere un nuovo attivatore Cloud Scheduler creato:

5a7bc6d96b970b92.png

Fai clic su View Details per andare alla pagina Cloud Scheduler.

Puoi aspettare le 9:00 per l'attivazione di Cloud Scheduler oppure attivarlo manualmente selezionando Force Run:

959525f2c8041a6a.png

Dopo alcuni secondi, dovresti vedere l'esecuzione del job Cloud Scheduler riuscita:

d64e03fc84d61145.png

Dovresti anche vedere altri tre screenshot aggiunti dalla chiamata da Cloud Scheduler:

56398a0e827de8b0.png

9. Complimenti

Congratulazioni, hai completato il codelab.

Pulizia (facoltativo)

Per evitare addebiti, è consigliabile eseguire la pulizia delle risorse.

Se non hai bisogno del progetto, puoi semplicemente eliminarlo:

gcloud projects delete $PROJECT_ID

Se hai bisogno del progetto, puoi eliminare le risorse singolarmente.

Elimina il codice sorgente:

rm -rf ~/jobs-demos/

Elimina il repository Artifact Registry:

gcloud artifacts repositories delete containers --location=$REGION

Elimina il service account:

gcloud iam service-accounts delete screenshot-sa@$PROJECT_ID.iam.gserviceaccount.com

Elimina il job Cloud Run:

gcloud run jobs delete screenshot --region=$REGION

Elimina il job Cloud Scheduler:

gcloud scheduler jobs delete screenshot-scheduler-trigger --location=$REGION

Elimina il bucket Cloud Storage:

gcloud storage rm --recursive gs://screenshot-$PROJECT_ID

Argomenti trattati

  • Come utilizzare un'app per acquisire screenshot di pagine web.
  • Come creare un'immagine container per l'applicazione.
  • Come creare un job Cloud Run per l'applicazione.
  • Come eseguire l'applicazione come job Cloud Run.
  • Come aggiornare il job.
  • Come pianificare il job con Cloud Scheduler.