Erste Schritte mit Cloud Run-Jobs

1. Einführung

1965fab24c502bd5.png

Übersicht

Cloud Run-Dienste eignen sich gut für Container, die unbegrenzt laufend auf HTTP-Anfragen warten. Cloud Run-Jobs eignen sich dagegen besser für Container, die bis zum Ende ausgeführt werden (derzeit bis zu 24 Stunden) und keine Anfragen verarbeiten. Zum Beispiel wäre die Verarbeitung von Datensätzen aus einer Datenbank, die Verarbeitung einer Liste von Dateien aus einem Cloud Storage-Bucket oder ein Vorgang mit langer Ausführungszeit wie die Berechnung von Pi gut geeignet, wenn sie als Cloud Run-Job implementiert würden.

Jobs können keine Anfragen verarbeiten oder einen Port überwachen. Das bedeutet, dass Jobs im Gegensatz zu Cloud Run-Diensten keinen Webserver bündeln sollten. Stattdessen sollten Jobcontainer beendet werden, sobald sie abgeschlossen sind.

In Cloud Run-Jobs können Sie mehrere Kopien des Containers parallel ausführen. Dazu geben Sie eine Reihe von Aufgaben an. Jede Aufgabe stellt eine laufende Kopie des Containers dar. Die Verwendung mehrerer Aufgaben ist nützlich, wenn jede Aufgabe eine Teilmenge der Daten unabhängig verarbeiten kann. So lässt sich beispielsweise die Verarbeitung von 10.000 Datensätzen aus Cloud SQL oder von 10.000 Dateien aus Cloud Storage schneller erledigen, wenn 10 Aufgaben parallel 1.000 Datensätze oder Dateien verarbeiten.

Die Verwendung von Cloud Run-Jobs erfolgt in zwei Schritten:

  1. Job erstellen:Enthält die gesamte Konfiguration, die zum Ausführen des Jobs erforderlich ist, z. B. das Container-Image, die Region und die Umgebungsvariablen.
  2. Job ausführen:Der Job wird neu ausgeführt. Optional können Sie Ihren Job so einrichten, dass er nach einem Zeitplan mit Cloud Scheduler ausgeführt wird.

In diesem Codelab lernen Sie zuerst eine Node.js-Anwendung kennen, mit der Sie Screenshots von Webseiten erstellen und in Cloud Storage speichern können. Anschließend erstellen Sie ein Container-Image für die Anwendung, führen sie in Cloud Run-Jobs aus, aktualisieren den Job, um mehr Webseiten zu verarbeiten, und führen den Job nach einem Zeitplan mit Cloud Scheduler aus.

Lerninhalte

  • Screenshots von Webseiten mit einer App aufnehmen
  • So erstellen Sie ein Container-Image für die Anwendung.
  • Cloud Run-Job für die Anwendung erstellen
  • So führen Sie die Anwendung als Cloud Run-Job aus.
  • So aktualisieren Sie den Job.
  • Den Job mit Cloud Scheduler planen

2. Einrichtung und Anforderungen

Umgebung zum selbstbestimmten Lernen einrichten

  1. Melden Sie sich in der Google Cloud Console an und erstellen Sie ein neues Projekt oder verwenden Sie ein vorhandenes Projekt. Wenn Sie noch kein Gmail- oder Google Workspace-Konto haben, müssen Sie eines erstellen.

295004821bab6a87.png

37d264871000675d.png

96d86d3d5655cdbe.png

  • Der Projektname ist der Anzeigename für die Projektteilnehmer. Es handelt sich um eine Zeichenfolge, die von Google APIs nicht verwendet wird. Sie können sie jederzeit aktualisieren.
  • Die Projekt-ID ist für alle Google Cloud-Projekte eindeutig und unveränderlich. Sie kann nach dem Festlegen nicht mehr geändert werden. Die Cloud Console generiert automatisch einen eindeutigen String. ist Ihnen meist egal, was es ist. In den meisten Codelabs musst du auf deine Projekt-ID verweisen, die üblicherweise als PROJECT_ID bezeichnet wird. Wenn Ihnen die generierte ID nicht gefällt, können Sie eine weitere zufällige ID generieren. Alternativ können Sie einen eigenen verwenden und nachsehen, ob er verfügbar ist. Sie kann nach diesem Schritt nicht mehr geändert werden und bleibt für die Dauer des Projekts erhalten.
  • Zur Information gibt es noch einen dritten Wert, die Projektnummer, die von manchen APIs verwendet wird. Weitere Informationen zu allen drei Werten finden Sie in der Dokumentation.
  1. Als Nächstes müssen Sie in der Cloud Console die Abrechnung aktivieren, um Cloud-Ressourcen/APIs verwenden zu können. Dieses Codelab ist kostengünstig. Sie können die von Ihnen erstellten Ressourcen oder das Projekt löschen, um Ressourcen herunterzufahren, um zu vermeiden, dass über diese Anleitung hinaus Kosten anfallen. Neue Google Cloud-Nutzer haben Anspruch auf das kostenlose Testprogramm mit 300$Guthaben.

Cloud Shell starten

Sie können Google Cloud zwar von Ihrem Laptop aus der Ferne bedienen, in diesem Codelab verwenden Sie jedoch Google Cloud Shell, eine Befehlszeilenumgebung, die in der Cloud ausgeführt wird.

Klicken Sie in der Google Cloud Console rechts oben in der Symbolleiste auf das Cloud Shell-Symbol:

84688aa223b1c3a2.png

Die Bereitstellung und Verbindung mit der Umgebung dauert nur einen Moment. Wenn er abgeschlossen ist, sollten Sie in etwa Folgendes sehen:

320e18fedb7fbe0.png

Diese virtuelle Maschine verfügt über sämtliche Entwicklertools, die Sie benötigen. Es bietet ein Basisverzeichnis mit 5 GB nichtflüchtigem Speicher und läuft auf Google Cloud, wodurch die Netzwerkleistung und Authentifizierung erheblich verbessert werden. Alle Arbeiten in diesem Codelab können in einem Browser erledigt werden. Sie müssen nichts installieren.

gcloud einrichten

Legen Sie in Cloud Shell Ihre Projekt-ID und die Region fest, in der Sie den Cloud Run-Job bereitstellen möchten. Speichern Sie sie als Variablen des Typs PROJECT_ID und REGION. In Zukunft können Sie eine Region aus einem der Cloud Run-Standorte auswählen.

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

APIs aktivieren

Aktivieren Sie alle erforderlichen Dienste:

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

3. Code abrufen

Sie erkunden zuerst eine Node.js-Anwendung, um Screenshots von Webseiten zu erstellen und sie in Cloud Storage zu speichern. Später erstellen Sie ein Container-Image für die Anwendung und führen es als Job in Cloud Run aus.

Führen Sie in Cloud Shell den folgenden Befehl aus, um den Anwendungscode aus diesem Repository zu klonen:

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

Wechseln Sie zum Verzeichnis mit der Anwendung:

cd jobs-demos/screenshot

Sie sollten dieses Dateilayout sehen:

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

Nachfolgend finden Sie eine kurze Beschreibung der einzelnen Dateien:

  • screenshot.js enthält den Node.js-Code für die Anwendung.
  • package.json definiert die Bibliotheksabhängigkeiten.
  • Dockerfile definiert das Container-Image.

4. Code ansehen

Mit dem integrierten Texteditor können Sie sich den Code ansehen. Klicken Sie dazu oben im Cloud Shell-Fenster auf die Schaltfläche Open Editor.

15a2cdc9b7f6dfc6.png

Im Folgenden werden die einzelnen Dateien kurz erläutert.

screenshot.js

screenshot.js fügt zuerst Puppeteer und Cloud Storage als Abhängigkeiten hinzu. Puppeteer ist eine Node.js-Bibliothek, mit der Sie Screenshots von Webseiten erstellen können:

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

Es gibt eine initBrowser-Funktion zum Initialisieren von Puppeteer und die takeScreenshot-Funktion, um Screenshots einer bestimmten URL zu erstellen:

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

Als Nächstes gibt es eine Funktion zum Abrufen oder Erstellen eines Cloud Storage-Buckets und eine weitere Funktion zum Hochladen des Screenshots einer Webseite in einen 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);
}

Die Funktion main ist der Einstiegspunkt:

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

Beachten Sie Folgendes bei der Methode main:

  • URLs werden als Argumente übergeben.
  • Der Bucket-Name wird als benutzerdefinierte BUCKET_NAME-Umgebungsvariable übergeben. Der Bucket-Name muss global in Google Cloud eindeutig sein.
  • Die Umgebungsvariable CLOUD_RUN_TASK_INDEX wird von Cloud Run-Jobs übergeben. Cloud Run-Jobs können mehrere Kopien der Anwendung als einzelne Aufgaben ausführen. CLOUD_RUN_TASK_INDEX steht für den Index der laufenden Aufgabe. Der Standardwert ist null, wenn der Code außerhalb von Cloud Run-Jobs ausgeführt wird. Wenn die Anwendung als mehrere Aufgaben ausgeführt wird, ruft jede Aufgabe bzw. jeder Container die entsprechende URL ab, erstellt einen Screenshot und speichert das Bild im Bucket.

package.json

Die Datei package.json definiert die Anwendung und gibt die Abhängigkeiten für Cloud Storage und Puppeteer an:

{
  "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 definiert das Container-Image für die Anwendung mit allen erforderlichen Bibliotheken und Abhängigkeiten:

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

5. Job bereitstellen

Bevor Sie einen Job erstellen, müssen Sie ein Dienstkonto erstellen, mit dem Sie den Job ausführen.

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

Weisen Sie dem Dienstkonto die Rolle storage.admin zu, damit es zum Erstellen von Buckets und Objekten verwendet werden kann.

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

Sie können jetzt einen Cloud Run-Job bereitstellen, der die zum Ausführen des Jobs erforderliche Konfiguration enthält.

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

Dadurch wird die quellbasierte Bereitstellung verwendet und ein Cloud Run-Job erstellt, ohne ihn auszuführen.

Beachten Sie, wie die Webseiten als Argumente übergeben werden. Der Bucket-Name zum Speichern der Screenshots wird als Umgebungsvariable übergeben.

Sie können mehrere Kopien des Containers parallel ausführen. Geben Sie dazu mit dem Flag --tasks eine Reihe von Aufgaben an, die ausgeführt werden sollen. Jede Aufgabe stellt eine laufende Kopie des Containers dar. Die Verwendung mehrerer Aufgaben ist nützlich, wenn jede Aufgabe eine Teilmenge der Daten unabhängig verarbeiten kann. Zu diesem Zweck erkennt jede Aufgabe ihren Index, der in der Umgebungsvariablen CLOUD_RUN_TASK_INDEX gespeichert ist. Ihr Code ist dafür verantwortlich, zu bestimmen, welche Aufgabe welche Teilmenge der Daten verarbeitet. Beachten Sie in diesem Beispiel --tasks=2. Dadurch werden zwei Container für die beiden URLs ausgeführt, die verarbeitet werden sollen.

Jede Task kann bis zu 24 Stunden ausgeführt werden. Sie können dieses Zeitlimit mit dem Flag --task-timeout verkürzen, wie in diesem Beispiel. Alle Aufgaben müssen erfolgreich sein, damit der Job erfolgreich abgeschlossen werden kann. Standardmäßig werden fehlgeschlagene Aufgaben nicht wiederholt. Sie können festlegen, dass Aufgaben wiederholt werden sollen, wenn sie fehlschlagen. Wenn eine Aufgabe die Anzahl der Wiederholungsversuche überschreitet, schlägt der gesamte Job fehl.

Ihr Job wird standardmäßig mit so vielen Aufgaben wie möglich parallel ausgeführt. Dies entspricht der Anzahl der Tasks für Ihren Job bis maximal 100. Sie können die Parallelität für Jobs verringern, die auf ein Backend mit eingeschränkter Skalierbarkeit zugreifen. Beispiel: eine Datenbank, die eine begrenzte Anzahl aktiver Verbindungen unterstützt. Sie können die Parallelität mit dem Flag --parallelism verringern.

6. Job ausführen

Bevor Sie den Job ausführen, listen Sie ihn auf, um zu prüfen, ob er erstellt wurde:

gcloud run jobs list

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

Führen Sie den Job mit dem folgenden Befehl aus:

gcloud run jobs execute screenshot --region=$REGION

Dadurch wird der Job ausgeführt. Sie können aktuelle und frühere Ausführungen auflisten:

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

Beschreiben Sie die Ausführung. Sie sollten das grüne Häkchen und die Meldung tasks completed successfully sehen:

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

Sie können den Status auch auf der Seite „Cloud Run-Jobs“ der Cloud Console ansehen:

1afde14d65f0d9ce.png

Wenn Sie den Cloud Storage-Bucket prüfen, sollten Sie die beiden erstellten Screenshotdateien sehen:

7c4d355f6f65106.png

Manchmal müssen Sie eine Ausführung stoppen, bevor sie abgeschlossen ist. Dies kann beispielsweise daran liegen, dass Sie den Job mit anderen Parametern ausführen müssen oder ein Fehler im Code vorliegt und Sie keine unnötige Rechenzeit verbrauchen.

Zum Beenden einer Ausführung Ihres Jobs müssen Sie die Ausführung löschen:

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

7. Job aktualisieren

Neue Versionen des Containers werden nicht automatisch bei der nächsten Ausführung von Cloud Run-Jobs ausgewählt. Wenn Sie den Code für Ihren Job ändern, müssen Sie den Container neu erstellen und den Job aktualisieren. Durch die Verwendung getaggter Bilder können Sie ermitteln, welche Version des Images derzeit verwendet wird.

Ebenso müssen Sie den Job aktualisieren, wenn Sie einige Konfigurationsvariablen aktualisieren möchten. Bei nachfolgenden Ausführungen des Jobs werden der neue Container und die Konfigurationseinstellungen verwendet.

Aktualisieren Sie den Job und ändern Sie im Flag --args die Seiten, von denen die App Screenshots erstellt. Aktualisieren Sie außerdem das Flag --tasks, um die Anzahl der Seiten anzugeben.

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

Führen Sie den Job noch einmal aus. Übergeben Sie diesmal das Flag --wait, um auf den Abschluss der Ausführungen zu warten:

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

Nach ein paar Sekunden sollten Sie drei weitere Screenshots zum Bucket sehen:

ed0cbe0b5a5f9144.png

8. Job planen

Bisher führen Sie Jobs manuell aus. In der Praxis möchten Sie Jobs wahrscheinlich als Reaktion auf ein Ereignis oder nach einem Zeitplan ausführen. Sehen wir uns an, wie Sie den Screenshot-Job mithilfe von Cloud Scheduler nach einem Zeitplan ausführen.

Prüfen Sie zuerst, ob die Cloud Scheduler API aktiviert ist:

gcloud services enable cloudscheduler.googleapis.com

Rufen Sie die Detailseite des Cloud Run-Jobs auf und klicken Sie auf den Abschnitt Triggers:

8ae456368905472f.png

Wählen Sie die Schaltfläche Add Scheduler Trigger aus:

48cbba777f75e1eb.png

Auf der rechten Seite wird ein Steuerfeld geöffnet. Erstellen Sie einen Scheduler-Job, der jeden Tag um 9:00 Uhr mit dieser Konfiguration ausgeführt werden soll, und wählen Sie Continue aus:

81fd098be0db216.png

Wählen Sie auf der nächsten Seite das Compute Engine-Standarddienstkonto und dann Create aus:

fe479501dfb91f9f.png

Es sollte nun ein neuer Cloud Scheduler-Trigger erstellt werden:

5a7bc6d96b970b92.png

Klicken Sie auf View Details, um die Cloud Scheduler-Seite aufzurufen.

Sie können bis 9:00 Uhr warten, bis der Planer aktiviert wird, oder den Cloud Scheduler manuell auslösen, indem Sie Force Run auswählen:

959525f2c8041a6a.png

Nach einigen Sekunden sollte der Cloud Scheduler-Job erfolgreich ausgeführt werden:

d64e03fc84d61145.png

Außerdem sollten Sie drei weitere Screenshots sehen, die durch den Aufruf von Cloud Scheduler hinzugefügt wurden:

56398a0e827de8b0.png

9. Glückwunsch

Glückwunsch, du hast das Codelab abgeschlossen.

Bereinigen (optional)

Es empfiehlt sich, Ressourcen zu bereinigen, um Gebühren zu vermeiden.

Wenn Sie das Projekt nicht benötigen, können Sie es einfach löschen:

gcloud projects delete $PROJECT_ID

Wenn Sie das Projekt benötigen, können Sie Ressourcen einzeln löschen.

Löschen Sie den Quellcode:

rm -rf ~/jobs-demos/

Löschen Sie das Artifact Registry-Repository:

gcloud artifacts repositories delete containers --location=$REGION

Löschen Sie das Dienstkonto:

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

Löschen Sie den Cloud Run-Job:

gcloud run jobs delete screenshot --region=$REGION

Löschen Sie den Cloud Scheduler-Job:

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

Löschen Sie den Cloud Storage-Bucket:

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

Behandelte Themen

  • Screenshots von Webseiten mit einer App aufnehmen
  • So erstellen Sie ein Container-Image für die Anwendung.
  • Cloud Run-Job für die Anwendung erstellen
  • So führen Sie die Anwendung als Cloud Run-Job aus.
  • So aktualisieren Sie den Job.
  • Den Job mit Cloud Scheduler planen