Introduzione ai job Cloud Run

1. Introduzione

1965fab24c502bd5.png

Panoramica

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

I job non possono gestire richieste o rimanere in ascolto su una porta. Ciò significa che, a differenza dei servizi Cloud Run, i job non devono raggruppare un server web. I container dei job dovrebbero invece uscire al termine dell'operazione.

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 container. L'utilizzo di più attività è utile se ognuna può elaborare in modo indipendente un sottoinsieme di 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, ciascuno in parallelo.

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

  1. Crea un job: incapsula tutta la configurazione necessaria per eseguire il job, ad esempio immagine container, regione e variabili di ambiente.
  2. Esegui il job: crea una nuova esecuzione del job. Facoltativamente, 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 delle pagine web e archiviarle in Cloud Storage. Quindi creerai un'immagine container per l'applicazione, la eseguirai sui job Cloud Run, aggiorni il job per elaborare più pagine web ed eseguirai 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.
  • 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 autogestito

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

295004821bab6a87.png

37d264871000675d.png

96d86d3d5655cdbe.png

  • Il Nome progetto è il nome visualizzato dei partecipanti del progetto. Si tratta di una stringa di caratteri non utilizzata dalle API di Google. Puoi sempre aggiornarla.
  • L'ID progetto è univoco in tutti i progetti Google Cloud ed è immutabile (non può essere modificato dopo essere stato impostato). La console Cloud genera automaticamente una stringa univoca. di solito non ti importa cosa sia. Nella maggior parte dei codelab, dovrai fare riferimento al tuo ID progetto (in genere identificato come PROJECT_ID). Se l'ID generato non ti soddisfa, potresti generarne un altro casuale. In alternativa, puoi provarne una personalizzata per verificare se è disponibile. Non può essere modificato dopo questo passaggio e rimane per tutta la durata del progetto.
  • Per informazione, c'è un terzo valore, un numero di progetto, utilizzato da alcune API. Scopri di più su tutti e tre questi valori nella documentazione.
  1. Successivamente, dovrai abilitare la fatturazione nella console Cloud per utilizzare risorse/API Cloud. L'esecuzione di questo codelab non ha alcun costo. Per arrestare le risorse ed evitare di incorrere in fatturazione dopo questo tutorial, puoi eliminare le risorse che hai creato o eliminare il progetto. I nuovi utenti di Google Cloud sono idonei al programma prova senza costi di 300$.

Avvia Cloud Shell

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

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

84688aa223b1c3a2.png

Dovrebbe richiedere solo qualche istante per eseguire il provisioning e connettersi all'ambiente. Al termine, dovresti vedere una schermata simile al seguente:

320e18fedb7fbe0.png

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

Configura gcloud

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

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

Abilita API

Abilita tutti i servizi necessari:

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

3. Ottieni il codice

Per prima cosa esplorerai un'applicazione Node.js per acquisire screenshot delle pagine web e archiviarli in Cloud Storage. Successivamente, creerai un'immagine container per l'applicazione ed la eseguirai come job in 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 di file:

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

Ecco una breve descrizione di ciascun 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, usa 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 ciascun file.

screenshot.js

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

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

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

Poi c'è una funzione per recuperare 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 è il punto di ingresso:

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

Nota 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 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à/contenitore 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

L'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 account di servizio da utilizzare per eseguire il job.

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

Concedi il ruolo storage.admin all'account di servizio, 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 è tutto pronto per 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 \
  --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 passate come argomenti. Il nome del bucket in cui salvare gli screenshot viene passato come variabile di ambiente.

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

Ogni attività può avere una durata massima di 24 ore. Puoi diminuire questo timeout utilizzando il flag --task-timeout, come in questo esempio. Tutte le attività devono essere completate affinché il job venga completato correttamente. Per impostazione predefinita, le attività non riuscite non vengono tentate di nuovo. Puoi configurare le attività da ripetere in caso di errore. Se un'attività supera il numero di nuovi tentativi, l'intero job non va a buon fine.

Per impostazione predefinita, il job verrà eseguito con il maggior numero possibile di attività in parallelo. Questo corrisponderà al numero di attività per il job, fino a un massimo di 100. Potresti voler impostare un parallelismo più basso 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, indicalo per vedere se è 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

In questo modo il job viene eseguito. 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 visualizzare 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

Puoi anche consultare la pagina dei job Cloud Run della console Cloud per verificare lo stato:

1afde14d65f0d9ce.png

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

7c4d355f6f65106.png

A volte potresti dover arrestare un'esecuzione prima che venga completata, ad esempio perché hai capito di dover eseguire il job con parametri diversi o perché il codice contiene un errore e non vuoi utilizzare tempo di calcolo non necessario.

Per arrestare l'esecuzione del job, devi eliminarla:

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

7. Aggiorna un job

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

Allo stesso modo, devi aggiornare anche il job se vuoi aggiornare alcune variabili di configurazione. Le esecuzioni successive del job utilizzeranno il nuovo container 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. Questo passaggio 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 3 screenshot aggiunti al bucket:

ed0cbe0b5a5f9144.png

8. Pianifica un job

Al momento i job vengono eseguiti manualmente. In uno scenario reale, probabilmente vorrai eseguire 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 Scheduler da eseguire ogni giorno alle 9:00 con questa configurazione e seleziona Continue:

81fd098be0db216.png

Nella pagina successiva, seleziona l'account di servizio Compute predefinito e seleziona Create:

fe479501dfb91f9f.png

Ora dovresti vedere un nuovo trigger di Cloud Scheduler creato:

5a7bc6d96b970b92.png

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

Puoi attendere fino alle 9:00 per l'attivazione dello scheduler oppure puoi attivare manualmente Cloud Scheduler selezionando Force Run:

959525f2c8041a6a.png

Dopo alcuni secondi, il job Cloud Scheduler dovrebbe essere stato eseguito correttamente:

d64e03fc84d61145.png

Dovresti anche vedere altri 3 screenshot aggiunti tramite la chiamata da Cloud Scheduler:

56398a0e827de8b0.png

9. Complimenti

Complimenti, hai completato il codelab.

Pulizia (facoltativo)

Per evitare addebiti, ti consigliamo di eseguire la pulizia delle risorse.

Se il progetto non ti serve, 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 l'account di servizio:

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.
  • 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.