Pic-a-daily: Lab 3 – Eine Collage aus den neuesten Bildern erstellen

1. Übersicht

In diesem Codelab erstellen Sie einen neuen Cloud Run-Dienst namens „collage service“, der in regelmäßigen Abständen von Cloud Scheduler ausgelöst wird. Der Dienst ruft die zuletzt hochgeladenen Bilder ab und erstellt eine Collage aus diesen Bildern. Dazu wird die Liste der letzten Bilder in Cloud Firestore gesucht und dann die tatsächlichen Bilddateien aus Cloud Storage heruntergeladen.

df20f5d0402b54b4.png

Lerninhalte

  • Cloud Run
  • Cloud Scheduler
  • Cloud Storage
  • Cloud Firestore

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. Wenn Sie noch kein Gmail- oder Google Workspace-Konto haben, müssen Sie eines erstellen.

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

  • Der Projektname ist der Anzeigename für die Teilnehmer dieses Projekts. Es handelt sich um einen String, der nicht von Google APIs verwendet wird und den Sie jederzeit aktualisieren können.
  • Die Projekt-ID muss für alle Google Cloud-Projekte eindeutig sein und ist unveränderlich (kann nach der Festlegung nicht mehr geändert werden). In der Cloud Console wird automatisch ein eindeutiger String generiert. Normalerweise ist es nicht wichtig, wie dieser aussieht. In den meisten Codelabs müssen Sie auf die Projekt-ID verweisen (die in der Regel als PROJECT_ID angegeben wird). Wenn Ihnen die ID nicht gefällt, können Sie eine andere zufällige ID generieren oder eine eigene ID ausprobieren und sehen, ob sie verfügbar ist. Nachdem das Projekt erstellt wurde, wird es „eingefroren“.
  • Es gibt einen dritten Wert, die Projektnummer, die von einigen APIs verwendet wird. Weitere Informationen zu diesen drei Werten
  1. Als Nächstes müssen Sie die Abrechnung in der Cloud Console aktivieren, um Cloud-Ressourcen/-APIs verwenden zu können. Die Durchführung dieses Codelabs sollte keine oder nur geringe Kosten verursachen. Wenn Sie Ressourcen herunterfahren möchten, damit nach Abschluss dieses Codelabs keine Gebühren anfallen, folgen Sie den Bereinigungsanweisungen am Ende des Codelabs. Neue Nutzer von Google Cloud kommen für das Programm für kostenlose Testversionen mit einem Guthaben von 300$ infrage.

Cloud Shell starten

Während Sie Google Cloud von Ihrem Laptop aus per Fernzugriff nutzen können, wird in diesem Codelab Google Cloud Shell verwendet, eine Befehlszeilenumgebung, die in der Cloud ausgeführt wird.

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

bce75f34b2c53987.png

Die Bereitstellung und Verbindung mit der Umgebung sollte nur wenige Augenblicke dauern. Anschließend sehen Sie in etwa Folgendes:

f6ef2b5f13479f3a.png

Diese virtuelle Maschine verfügt über sämtliche Entwicklertools, die Sie benötigen. Sie bietet ein Basisverzeichnis mit 5 GB nichtflüchtigem Speicher und läuft in Google Cloud, was die Netzwerkleistung und Authentifizierung erheblich verbessert. Für dieses Lab benötigen Sie lediglich einen Browser.

3. APIs aktivieren

Sie benötigen einen Cloud Scheduler-Job, um den Cloud Run-Dienst in regelmäßigen Abständen auszulösen. Prüfen Sie, ob die Funktion aktiviert ist:

gcloud services enable cloudscheduler.googleapis.com

Der Vorgang sollte erfolgreich abgeschlossen werden:

Operation "operations/acf.5c5ef4f6-f734-455d-b2f0-ee70b5a17322" finished successfully.

4. Code klonen

Klonen Sie den Code, falls Sie das im vorherigen Codelab noch nicht getan haben:

git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop

Sie können dann zum Verzeichnis mit dem Dienst wechseln:

cd serverless-photosharing-workshop/services/collage/nodejs

Der Dienst hat das folgende Dateilayout:

services
 |
 ├── collage
      |
      ├── nodejs
           |
           ├── Dockerfile
           ├── index.js
           ├── package.json

Im Ordner befinden sich drei Dateien:

  • index.js enthält den Node.js-Code.
  • package.json definiert die Bibliotheksabhängigkeiten.
  • Dockerfile definiert das Container-Image.

5. Code ansehen

Abhängigkeiten

In der Datei package.json werden die erforderlichen Bibliotheksabhängigkeiten definiert:

{
  "name": "collage_service",
  "version": "0.0.1",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "bluebird": "^3.7.2",
    "express": "^4.17.1",
    "imagemagick": "^0.1.3",
    "@google-cloud/firestore": "^4.9.9",
    "@google-cloud/storage": "^5.8.3"
  }
}

Wir verwenden die Cloud Storage-Bibliothek, um Bilddateien in Cloud Storage zu lesen und zu speichern. Wir deklarieren eine Abhängigkeit von Cloud Firestore, um zuvor gespeicherte Bildmetadaten abzurufen. Express ist ein JavaScript-/Node-Web-Framework. Bluebird wird für die Verarbeitung von Promises verwendet und imagemagick ist eine Bibliothek zum Bearbeiten von Bildern.

Dockerfile

Dockerfile definiert das Container-Image für die Anwendung:

FROM node:14-slim

# installing Imagemagick
RUN set -ex; \
  apt-get -y update; \
  apt-get -y install imagemagick; \
  rm -rf /var/lib/apt/lists/*

WORKDIR /picadaily/services/collage
COPY package*.json ./
RUN npm install --production
COPY . .
CMD [ "npm", "start" ]

Wir verwenden ein kleines Node 14-Basis-Image. Wir installieren die ImageMagick-Bibliothek. Anschließend installieren wir die NPM-Module, die für unseren Code erforderlich sind, und führen unseren Node-Code mit „npm start“ aus.

index.js

Sehen wir uns den index.js-Code genauer an:

const express = require('express');
const imageMagick = require('imagemagick');
const Promise = require("bluebird");
const path = require('path');
const {Storage} = require('@google-cloud/storage');
const Firestore = require('@google-cloud/firestore');

Wir benötigen die verschiedenen Abhängigkeiten, die für die Ausführung unseres Programms erforderlich sind: Express ist das Node-Webframework, das wir verwenden werden, ImageMagick ist die Bibliothek für die Bildbearbeitung, Bluebird ist eine Bibliothek für die Verarbeitung von JavaScript-Promises, Path wird für die Verarbeitung von Datei- und Verzeichnispfaden verwendet und Storage und Firestore sind für die Arbeit mit Google Cloud Storage (unseren Bild-Buckets) bzw. dem Cloud Firestore-Datenspeicher vorgesehen.

const app = express();

app.get('/', async (req, res) => {
    try {
        console.log('Collage request');

        /* ... */

    } catch (err) {
        console.log(`Error: creating the collage: ${err}`);
        console.error(err);
        res.status(500).send(err);
    }
});

Oben sehen Sie die Struktur unseres Node-Handlers: Unsere App reagiert auf HTTP-GET-Anfragen. Wir führen auch eine Fehlerbehandlung durch, falls etwas schiefgeht. Sehen wir uns nun an, was sich in dieser Struktur befindet.

const thumbnailFiles = [];
const pictureStore = new Firestore().collection('pictures');
const snapshot = await pictureStore
    .where('thumbnail', '==', true)
    .orderBy('created', 'desc')
    .limit(4).get();

if (snapshot.empty) {
    console.log('Empty collection, no collage to make');
    res.status(204).send("No collage created.");
} else {

    /* ... */

}

Für unseren Collageservice sind mindestens vier Bilder erforderlich, deren Thumbnails generiert wurden. Laden Sie also zuerst vier Bilder hoch.

Wir rufen die vier neuesten Bilder, die von unseren Nutzern hochgeladen wurden, aus den in Cloud Firestore gespeicherten Metadaten ab. Wir prüfen, ob die resultierende Sammlung leer ist oder nicht, und fahren dann im else-Zweig unseres Codes fort.

Erstellen wir eine Liste der Dateinamen:

snapshot.forEach(doc => {
    thumbnailFiles.push(doc.id);
});
console.log(`Picture file names: ${JSON.stringify(thumbnailFiles)}`);

Wir laden jede dieser Dateien aus dem Thumbnail-Bucket herunter. Der Name des Buckets stammt aus einer Umgebungsvariablen, die wir bei der Bereitstellung festgelegt haben:

const thumbBucket = storage.bucket(process.env.BUCKET_THUMBNAILS);

await Promise.all(thumbnailFiles.map(async fileName => {
    const filePath = path.resolve('/tmp', fileName);
    await thumbBucket.file(fileName).download({
        destination: filePath
    });
}));
console.log('Downloaded all thumbnails');

Sobald die neuesten Thumbnails hochgeladen wurden, verwenden wir die ImageMagick-Bibliothek, um ein 4×4-Raster aus diesen Thumbnails zu erstellen. Wir verwenden die Bluebird-Bibliothek und ihre Promise-Implementierung, um den Callback-basierten Code in async- und await-freundlichen Code zu transformieren. Anschließend warten wir auf das Promise, das die Bildcollage erstellt:

const collagePath = path.resolve('/tmp', 'collage.png');

const thumbnailPaths = thumbnailFiles.map(f => path.resolve('/tmp', f));
const convert = Promise.promisify(im.convert);
await convert([
    '(', ...thumbnailPaths.slice(0, 2), '+append', ')',
    '(', ...thumbnailPaths.slice(2), '+append', ')',
    '-size', '400x400', 'xc:none', '-background', 'none',  '-append',
    collagePath]);
console.log("Created local collage picture");

Da das Collagebild lokal im temporären Ordner auf der Festplatte gespeichert wurde, müssen wir es jetzt in Cloud Storage hochladen und dann eine Erfolgsantwort (Statuscode 2xx) zurückgeben:

await thumbBucket.upload(collagePath);
console.log("Uploaded collage to Cloud Storage bucket ${process.env.BUCKET_THUMBNAILS}");

res.status(204).send("Collage created.");

Jetzt müssen wir unser Node-Skript so konfigurieren, dass es auf eingehende Anfragen wartet:

const PORT = process.env.PORT || 8080;

app.listen(PORT, () => {
    console.log(`Started collage service on port ${PORT}`);
});

Am Ende der Quelldatei finden wir die Anweisungen, mit denen Express unsere Webanwendung auf dem Standardport 8080 startet.

6. Lokal testen

Testen Sie den Code lokal, um sicherzustellen, dass er funktioniert, bevor Sie ihn in der Cloud bereitstellen.

Installieren Sie im Ordner collage/nodejs die npm-Abhängigkeiten und starten Sie den Server:

npm install; npm start

Wenn alles geklappt hat, sollte der Server auf Port 8080 gestartet werden:

Started collage service on port 8080

Verwenden Sie zum Beenden CTRL-C.

7. In Cloud Run erstellen und bereitstellen

Legen Sie vor der Bereitstellung in Cloud Run die Cloud Run-Region auf eine der unterstützten Regionen und die Plattform auf managed fest:

gcloud config set run/region europe-west1
gcloud config set run/platform managed

So prüfen Sie, ob die Konfiguration festgelegt ist:

gcloud config list

...
[run]
platform = managed
region = europe-west1

Anstatt das Container-Image manuell mit Cloud Build zu erstellen und zu veröffentlichen, können Sie es auch von Cloud Run mit Google Cloud Buildpacks erstellen lassen.

Führen Sie den folgenden Befehl aus, um das Container-Image zu erstellen:

BUCKET_THUMBNAILS=thumbnails-$GOOGLE_CLOUD_PROJECT
SERVICE_NAME=collage-service
gcloud run deploy $SERVICE_NAME \
    --source . \
    --no-allow-unauthenticated \
    --update-env-vars BUCKET_THUMBNAILS=$BUCKET_THUMBNAILS

Beachten Sie das Flag –-source. Dies ist die quellenbasierte Bereitstellung in Cloud Run. Wenn im Quellcodeverzeichnis ein Dockerfile vorhanden ist, wird der hochgeladene Quellcode mit diesem Dockerfile erstellt. Wenn im Quellcodeverzeichnis kein Dockerfile vorhanden ist, erkennen Google Cloud-Buildpacks die von Ihnen verwendete Sprache automatisch und rufen die Abhängigkeiten des Codes ab, um ein produktionsfähiges Container-Image zu erstellen, wobei ein von Google verwaltetes sicheres Basis-Image verwendet wird. Dadurch wird Cloud Run angewiesen, Google Cloud Buildpacks zu verwenden, um das in Dockerfile definierte Container-Image zu erstellen.

Beachten Sie auch, dass bei der quellbasierten Bereitstellung Artifact Registry zum Speichern von erstellten Containern verwendet wird. Artifact Registry ist eine moderne Version von Google Container Registry. Die CLI fordert Sie auf, die API zu aktivieren, falls sie im Projekt noch nicht aktiviert ist, und erstellt ein Repository mit dem Namen cloud-run-source-deploy in der Region, in der Sie die Bereitstellung durchführen.

Das Flag --no-allow-unauthenticated macht den Cloud Run-Dienst zu einem internen Dienst, der nur von bestimmten Dienstkonten ausgelöst wird.

8. Cloud Scheduler einrichten

Der Cloud Run-Dienst ist jetzt bereit und wurde bereitgestellt. Nun müssen Sie den regulären Zeitplan erstellen, um den Dienst jede Minute aufzurufen.

Erstellen Sie ein Dienstkonto:

SERVICE_ACCOUNT=collage-scheduler-sa
gcloud iam service-accounts create $SERVICE_ACCOUNT \
   --display-name "Collage Scheduler Service Account"

Erteilen Sie dem Dienstkonto die Berechtigung, den Cloud Run-Dienst aufzurufen:

gcloud run services add-iam-policy-binding $SERVICE_NAME \
   --member=serviceAccount:$SERVICE_ACCOUNT@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com \
   --role=roles/run.invoker

Erstellen Sie einen Cloud Scheduler-Job, der jede Minute ausgeführt wird:

SERVICE_URL=$(gcloud run services describe $SERVICE_NAME --format 'value(status.url)')
gcloud scheduler jobs create http $SERVICE_NAME-job --schedule "* * * * *" \
   --http-method=GET \
   --location=europe-west1 \
   --uri=$SERVICE_URL \
   --oidc-service-account-email=$SERVICE_ACCOUNT@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com \
   --oidc-token-audience=$SERVICE_URL

Im Cloud Scheduler-Bereich in der Cloud Console können Sie sehen, dass Cloud Scheduler eingerichtet ist und auf die Cloud Run-Dienst-URL verweist:

35119e28c1da53f3.png

9. Dienst testen

Um zu testen, ob die Einrichtung funktioniert, suchen Sie im Bucket thumbnails nach dem Collagebild (mit dem Namen collage.png). Sie können auch die Logs des Dienstes prüfen:

93922335a384be2e.png

10. Bereinigen (optional)

Wenn Sie nicht mit den anderen Labs der Reihe fortfahren möchten, können Sie Ressourcen bereinigen, um Kosten zu sparen und nicht mehr benötigte Ressourcen für andere freizugeben. Sie können Ressourcen einzeln so bereinigen:

Löschen Sie den Dienst:

gcloud run services delete $SERVICE_NAME -q

Cloud Scheduler-Job löschen:

gcloud scheduler jobs delete $SERVICE_NAME-job -q

Alternativ können Sie das gesamte Projekt löschen:

gcloud projects delete $GOOGLE_CLOUD_PROJECT

11. Glückwunsch!

Glückwunsch! Sie haben einen geplanten Dienst erstellt: Dank Cloud Scheduler, der jede Minute eine Nachricht an ein Pub/Sub-Thema sendet, wird Ihr Cloud Run-Collage-Dienst aufgerufen und kann Bilder zusammenfügen, um das resultierende Bild zu erstellen.

Behandelte Themen

  • Cloud Run
  • Cloud Scheduler
  • Cloud Storage
  • Cloud Firestore

Nächste Schritte