Erste Schritte mit Cloud Run-Funktionen

1. Einführung

Übersicht

Cloud Run-Funktionen sind das FaaS-Angebot (Function as a Service) von Google Cloud auf Basis von Cloud Run und Eventarc. Es bietet Ihnen erweiterte Steuerungsmöglichkeiten für Leistung und Skalierbarkeit, mehr Kontrolle über die Laufzeit der Funktion sowie Trigger aus über 90 Ereignisquellen.

In diesem Codelab erfahren Sie, wie Sie Cloud Run-Funktionen erstellen, die auf HTTP-Aufrufe reagieren und durch Pub/Sub-Nachrichten und Cloud-Audit-Logs ausgelöst werden.

In diesem Codelab werden auch automatische Basis-Image-Updates für die Bereitstellung von Funktionen verwendet. Dazu wird ein Basis-Image mit dem Flag --base-image angegeben. Wenn Sie automatische Basis-Image-Aktualisierungen für Cloud Run konfigurieren, kann Google automatisch Sicherheits-Patches für die Betriebssystem- und Sprachlaufzeitkomponenten des Basis-Images anwenden. Sie müssen den Dienst nicht neu erstellen oder neu bereitstellen, damit das Basis-Image aktualisiert wird. Weitere Informationen finden Sie unter Automatische Basis-Image-Updates.

Wenn Sie keine automatischen Basis-Image-Updates verwenden möchten, können Sie das Flag --base-image aus den Beispielen in diesem Codelab entfernen.

Lerninhalte

  • Übersicht über Cloud Run-Funktionen und die Verwendung automatischer Basis-Image-Updates.
  • So schreiben Sie eine Funktion, die auf HTTP-Aufrufe reagiert.
  • So schreiben Sie eine Funktion, die auf Pub/Sub-Nachrichten reagiert.
  • So schreiben Sie eine Funktion, die auf Cloud Storage-Ereignisse reagiert.
  • So teilen Sie den Traffic auf zwei Überarbeitungen auf.
  • Kaltstarts mit einer Mindestanzahl von Instanzen vermeiden

2. Einrichtung und Anforderungen

Stammordner erstellen

Erstellen Sie einen Stammordner für alle Beispiele.

mkdir crf-codelab
cd crf-codelab

Umgebungsvariablen einrichten

Legen Sie Umgebungsvariablen fest, die in diesem Codelab verwendet werden.

gcloud config set project <YOUR-PROJECT-ID>
REGION=<YOUR_REGION>

PROJECT_ID=$(gcloud config get-value project)

APIs aktivieren

Aktivieren Sie alle erforderlichen Dienste:

gcloud services enable \
  artifactregistry.googleapis.com \
  cloudbuild.googleapis.com \
  eventarc.googleapis.com \
  run.googleapis.com \
  logging.googleapis.com \
  pubsub.googleapis.com

3. HTTP-Funktion

Für die erste Funktion erstellen wir eine authentifizierte Node.js-Funktion, die auf HTTP-Anfragen reagiert. Wir verwenden auch ein Zeitlimit von 10 Minuten, um zu zeigen, wie eine Funktion mehr Zeit für die Antwort auf HTTP-Anfragen haben kann.

Erstellen

Erstellen Sie einen Ordner für die App und wechseln Sie zu diesem Ordner:

mkdir hello-http
cd hello-http

Erstellen Sie eine index.js-Datei, die auf HTTP-Anfragen reagiert:

const functions = require('@google-cloud/functions-framework');

functions.http('helloWorld', (req, res) => {
  res.status(200).send('HTTP with Node.js in Cloud Run functions!');
});

Erstellen Sie eine package.json-Datei, um die Abhängigkeiten anzugeben:

{
  "name": "nodejs-run-functions-codelab",
  "version": "0.0.1",
  "main": "index.js",
  "dependencies": {
    "@google-cloud/functions-framework": "^2.0.0"
  }
}

Bereitstellen

Die Funktion bereitstellen:

gcloud run deploy nodejs-run-function \
      --source . \
      --function helloWorld \
      --base-image nodejs22 \
      --region $REGION \
      --timeout 600 \
      --no-allow-unauthenticated

Mit diesem Befehl werden Buildpacks verwendet, um den Quellcode Ihrer Funktion in ein produktionsbereites Container-Image umzuwandeln.

Hinweis:

  • Das Flag --source wird verwendet, um Cloud Run anzuweisen, die Funktion in einem ausführbaren containerbasierten Dienst zu erstellen.
  • Das Flag --function (neu) wird verwendet, um den Einstiegspunkt des neuen Dienstes auf die Funktionssignatur festzulegen, die aufgerufen werden soll.
  • Das Flag --base-image (neu) gibt die Umgebung des Basis-Images für Ihre Funktion an, z. B. nodejs22, python312, go123, java21, dotnet8, ruby33 oder php83. Weitere Informationen zu Basis-Images und den in den einzelnen Images enthaltenen Paketen finden Sie unter Basis-Images für Laufzeiten.
  • (optional) Das Flag --timeout ermöglicht der Funktion ein längeres Zeitlimit für die Reaktion auf HTTP-Anfragen. In diesem Beispiel werden 600 Sekunden verwendet, um eine Reaktionszeit von 10 Minuten zu demonstrieren.
  • (optional) --no-allow-unauthenticated, um zu verhindern, dass Ihre Funktion öffentlich aufgerufen werden kann

Test

Testen Sie die Funktion mit den folgenden Befehlen:

# get the Service URL
SERVICE_URL="$(gcloud run services describe nodejs-run-function --region $REGION --format 'value(status.url)')"

# invoke the service
curl -H "Authorization: bearer $(gcloud auth print-identity-token)" -X GET $SERVICE_URL

Als Antwort sollte die Meldung HTTP with Node.js in Cloud Run functions! angezeigt werden.

4. Pub/Sub-Funktion

Für die zweite Funktion erstellen wir eine Python-Funktion, die durch eine Pub/Sub-Nachricht ausgelöst wird, die in einem bestimmten Thema veröffentlicht wird.

Pub/Sub-Authentifizierungstokens einrichten

Wenn Sie das Pub/Sub-Dienstkonto am oder vor dem 8. April 2021 aktiviert haben, weisen Sie dem Pub/Sub-Dienstkonto die Rolle iam.serviceAccountTokenCreator zu:

PROJECT_NUMBER=$(gcloud projects list --filter="project_id:$PROJECT_ID" --format='value(project_number)')

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member  serviceAccount:service-$PROJECT_NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com \
  --role roles/iam.serviceAccountTokenCreator

Erstellen

Erstellen Sie ein Pub/Sub-Thema für das Beispiel:

TOPIC=cloud-run-functions-pubsub-topic
gcloud pubsub topics create $TOPIC

Erstellen Sie einen Ordner für die App und wechseln Sie zu diesem Ordner:

mkdir ../hello-pubsub
cd ../hello-pubsub

Erstellen Sie eine main.py-Datei, in der eine Nachricht mit der CloudEvent-ID protokolliert wird:

import functions_framework

@functions_framework.cloud_event
def hello_pubsub(cloud_event):
   print('Pub/Sub with Python in Cloud Run functions! Id: ' + cloud_event['id'])

Erstellen Sie eine requirements.txt-Datei mit folgendem Inhalt, um die Abhängigkeiten anzugeben:

functions-framework==3.*

Bereitstellen

Die Funktion bereitstellen:

gcloud run deploy python-pubsub-function \
       --source . \
       --function hello_pubsub \
       --base-image python313 \
       --region $REGION \
       --no-allow-unauthenticated

Rufen Sie die Projektnummer ab, die für die Dienstkontoidentität verwendet werden soll.

PROJECT_NUMBER=$(gcloud projects list --filter="project_id:$PROJECT_ID" --format='value(project_number)')

Trigger erstellen

gcloud eventarc triggers create python-pubsub-function-trigger  \
    --location=$REGION \
    --destination-run-service=python-pubsub-function  \
    --destination-run-region=$REGION \
    --event-filters="type=google.cloud.pubsub.topic.v1.messagePublished" \
    --transport-topic=projects/$PROJECT_ID/topics/$TOPIC \
    --service-account=$PROJECT_NUMBER-compute@developer.gserviceaccount.com

Test

Testen Sie die Funktion, indem Sie eine Nachricht an das Thema senden:

gcloud pubsub topics publish $TOPIC --message="Hello World"

Das empfangene CloudEvent sollte in den Logs angezeigt werden:

gcloud run services logs read python-pubsub-function --region $REGION --limit=10

5. Cloud Storage-Funktion

Als Nächstes erstellen wir eine Node.js-Funktion, die auf Ereignisse aus einem Cloud Storage-Bucket reagiert.

Einrichten

Wenn Sie Cloud Storage-Funktionen verwenden möchten, weisen Sie dem Cloud Storage-Dienstkonto die IAM-Rolle pubsub.publisher zu:

SERVICE_ACCOUNT=$(gsutil kms serviceaccount -p $PROJECT_NUMBER)

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member serviceAccount:$SERVICE_ACCOUNT \
  --role roles/pubsub.publisher

Erstellen

Erstellen Sie einen Ordner für die App und wechseln Sie zu diesem Ordner:

mkdir ../hello-storage
cd ../hello-storage

Erstellen Sie eine index.js-Datei, die einfach auf Cloud Storage-Ereignisse reagiert:

const functions = require('@google-cloud/functions-framework');

functions.cloudEvent('helloStorage', (cloudevent) => {
  console.log('Cloud Storage event with Node.js in Cloud Run functions!');
  console.log(cloudevent);
});

Erstellen Sie eine package.json-Datei, um die Abhängigkeiten anzugeben:

{
  "name": "nodejs-crf-cloud-storage",
  "version": "0.0.1",
  "main": "index.js",
  "dependencies": {
    "@google-cloud/functions-framework": "^2.0.0"
  }
}

Bereitstellen

Erstellen Sie zuerst einen Cloud Storage-Bucket oder verwenden Sie einen vorhandenen Bucket:

export BUCKET_NAME="gcf-storage-$PROJECT_ID"
​​export BUCKET="gs://gcf-storage-$PROJECT_ID"
gsutil mb -l $REGION $BUCKET

Die Funktion bereitstellen:

gcloud run deploy nodejs-crf-cloud-storage \
 --source . \
 --base-image nodejs22 \
 --function helloStorage \
 --region $REGION \
 --no-allow-unauthenticated

Nachdem die Funktion bereitgestellt wurde, können Sie sie im Cloud Run-Bereich der Cloud Console sehen.

Erstellen Sie nun den Eventarc-Trigger.

BUCKET_REGION=$REGION

gcloud eventarc triggers create nodejs-crf-cloud-storage-trigger \
  --location=$BUCKET_REGION \
  --destination-run-service=nodejs-crf-cloud-storage \
  --destination-run-region=$REGION \
  --event-filters="type=google.cloud.storage.object.v1.finalized" \
  --event-filters="bucket=$BUCKET_NAME" \
  --service-account=$PROJECT_NUMBER-compute@developer.gserviceaccount.com

Test

Testen Sie die Funktion. Laden Sie dazu eine Datei in den Bucket hoch:

echo "Hello World" > random.txt
gsutil cp random.txt $BUCKET/random.txt

Das empfangene CloudEvent sollte in den Logs angezeigt werden:

gcloud run services logs read nodejs-crf-cloud-storage --region $REGION --limit=10

6. Cloud-Audit-Logs

Als Nächstes erstellen wir eine Node.js-Funktion, die ein Cloud-Audit-Log-Ereignis empfängt, wenn eine Compute Engine-VM-Instanz erstellt wird. Als Reaktion darauf wird der neu erstellten VM ein Label hinzugefügt, das den Ersteller der VM angibt.

Neu erstellte Compute Engine-VMs ermitteln

Compute Engine gibt beim Erstellen einer VM zwei Audit-Logs aus.

Das erste wird zu Beginn der VM-Erstellung ausgegeben. Das zweite wird ausgegeben, nachdem die VM erstellt wurde.

In den Audit-Logs sind die Vorgangsfelder unterschiedlich und enthalten die Werte first: true und last: true. Das zweite Audit-Log enthält alle Informationen, die wir zum Labeln einer Instanz benötigen. Daher verwenden wir das Flag last: true, um es in Cloud Run Functions zu erkennen.

Einrichten

Wenn Sie Cloud-Audit-Log-Funktionen verwenden möchten, müssen Sie Audit-Logs für Eventarc aktivieren. Außerdem müssen Sie ein Dienstkonto mit der Rolle eventarc.eventReceiver verwenden.

  1. Aktivieren Sie Cloud-Audit-Logs für die Logtypen Administratorlesevorgänge, Datenlesevorgänge und Datenschreibvorgänge für die Compute Engine API.
  2. Weisen Sie dem Compute Engine-Standarddienstkonto die IAM-Rolle eventarc.eventReceiver zu:
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com \
  --role roles/eventarc.eventReceiver

Funktion erstellen

In diesem Codelab wird Node.js verwendet. Weitere Beispiele finden Sie unter https://github.com/GoogleCloudPlatform/eventarc-samples.

package.json-Datei erstellen

{
  "dependencies": {
    "googleapis": "^84.0.0"
  }
}

node.js-Datei erstellen

// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
const { google } = require("googleapis");
var compute = google.compute("v1");

exports.labelVmCreation = async (cloudevent) => {
  const data = cloudevent.body;

  // in case an event has >1 audit log
  // make sure we respond to the last event
  if (!data.operation || !data.operation.last) {
    console.log("Operation is not last, skipping event");
    return;
  }

  // projects/dogfood-gcf-saraford/zones/us-central1-a/instances/instance-1
  var resourceName = data.protoPayload.resourceName;
  var resourceParts = resourceName.split("/");
  var project = resourceParts[1];
  var zone = resourceParts[3];
  var instanceName = resourceParts[5];
  var username = data.protoPayload.authenticationInfo.principalEmail.split("@")[0];

  console.log(`Setting label username: ${username} to instance ${instanceName} for zone ${zone}`);

  var authClient = await google.auth.getClient({
    scopes: ["https://www.googleapis.com/auth/cloud-platform"]
  });

  // per docs: When updating or adding labels in the API,
  // you need to provide the latest labels fingerprint with your request,
  // to prevent any conflicts with other requests.
  var labelFingerprint = await getInstanceLabelFingerprint(authClient, project, zone, instanceName);

  var responseStatus = await setVmLabel(
    authClient,
    labelFingerprint,
    username,
    project,
    zone,
    instanceName
  );

  // log results of setting VM label
  console.log(JSON.stringify(responseStatus, null, 2));
};

async function getInstanceLabelFingerprint(authClient, project, zone, instanceName) {
  var request = {
    project: project,
    zone: zone,
    instance: instanceName,
    auth: authClient
  };

  var response = await compute.instances.get(request);
  var labelFingerprint = response.data.labelFingerprint;
  return labelFingerprint;
}

async function setVmLabel(authClient, labelFingerprint, username, project, zone, instanceName) {
  var request = {
    project: project,
    zone: zone,
    instance: instanceName,

    resource: {
      labels: { "creator": username },
      labelFingerprint: labelFingerprint
    },

    auth: authClient
  };

  var response = await compute.instances.setLabels(request);
  return response.statusText;
}

Bereitstellen

Die Funktion bereitstellen:

gcloud run deploy gce-vm-labeler \
  --source . \
  --function labelVmCreation \
  --region $REGION \
  --no-allow-unauthenticated

Erstellen Sie nun den Trigger. Beachten Sie, dass die Funktion Audit-Logs für Compute Engine-Einfügungen mit dem Flag --trigger-event-filters filtert.

gcloud eventarc triggers create gce-vm-labeler-trigger \
  --location=$REGION \
  --destination-run-service=gce-vm-labeler \
  --destination-run-region=$REGION \
  --event-filters="type=google.cloud.audit.log.v1.written,serviceName=compute.googleapis.com,methodName=v1.compute.instances.insert" \
  --service-account=$ROJECT_NUMBER-compute@developer.gserviceaccount.com

Test

Umgebungsvariablen festlegen:

# if you're using europe-west1 as your region
ZONE=europe-west1-d
VM_NAME=codelab-crf-auditlog

Mit dem folgenden Befehl erstellen Sie eine VM:

gcloud compute instances create $VM_NAME --zone=$ZONE --machine-type=e2-medium --image-family=debian-11  --image-project=debian-cloud

Nachdem die VM erstellt wurde, sollte das hinzugefügte creator-Label in der Cloud Console im Abschnitt Grundlegende Informationen oder mit dem folgenden Befehl angezeigt werden:

gcloud compute instances describe $VM_NAME --zone=$ZONE

Das Label sollte in der Ausgabe wie im folgenden Beispiel zu sehen sein:

...
labelFingerprint: ULU6pAy2C7s=
labels:
  creator: atameldev
...

Bereinigen

Löschen Sie die VM-Instanz. Es wird in diesem Lab nicht noch einmal verwendet.

gcloud compute instances delete $VM_NAME --zone=$ZONE

7. Traffic-Aufteilung

Cloud Run Functions unterstützt mehrere Überarbeitungen Ihrer Funktionen, sodass Sie den Traffic auf verschiedene Überarbeitungen aufteilen und Ihre Funktion auf eine frühere Version zurücksetzen können.

In diesem Schritt stellen Sie zwei Überarbeitungen einer Funktion bereit und teilen den Traffic dann 50 : 50 auf.

Erstellen

Erstellen Sie einen Ordner für die App und wechseln Sie zu diesem Ordner:

mkdir ../traffic-splitting
cd ../traffic-splitting

Erstellen Sie eine main.py-Datei mit einer Python-Funktion, die eine Umgebungsvariable für die Farbe liest und mit Hello World in dieser Hintergrundfarbe antwortet:

import os

color = os.environ.get('COLOR')

def hello_world(request):
    return f'<body style="background-color:{color}"><h1>Hello World!</h1></body>'

Erstellen Sie eine requirements.txt-Datei mit folgendem Inhalt, um die Abhängigkeiten anzugeben:

functions-framework==3.*

Bereitstellen

Stellen Sie die erste Version der Funktion mit einem orangefarbenen Hintergrund bereit:

COLOR=orange
gcloud run deploy hello-world-colors \
 --source . \
 --base-image python313 \
 --function hello_world \
 --region $REGION \
 --allow-unauthenticated \
 --update-env-vars COLOR=$COLOR

Wenn Sie die Funktion jetzt testen, indem Sie den HTTP-Trigger (die URI-Ausgabe des obigen Bereitstellungsbefehls) in Ihrem Browser aufrufen, sollte Hello World mit einem orangefarbenen Hintergrund angezeigt werden:

36ca0c5f39cc89cf.png

Stellen Sie die zweite Überarbeitung mit einem gelben Hintergrund bereit:

COLOR=yellow
gcloud run deploy hello-world-colors \
 --source . \
 --base-image python313 \
 --function hello_world \
 --region $REGION \
 --allow-unauthenticated \
 --update-env-vars COLOR=$COLOR

Da dies die letzte Überarbeitung ist, sollten Sie beim Testen der Funktion Hello World mit einem gelben Hintergrund sehen:

391286a08ad3cdde.png

Traffic 50 : 50 aufteilen

Um den Traffic zwischen den orangefarbenen und gelben Überarbeitungen aufzuteilen, müssen Sie die Überarbeitungs-IDs der Cloud Run-Dienste ermitteln. Mit diesem Befehl können Sie die Revisions-IDs aufrufen:

gcloud run revisions list --service hello-world-colors \
  --region $REGION --format 'value(REVISION)'

Die Ausgabe sollte in etwa so aussehen:

hello-world-colors-00001-man
hello-world-colors-00002-wok

Teilen Sie den Traffic jetzt so auf die beiden Überarbeitungen auf (aktualisieren Sie X-XXX entsprechend Ihren Überarbeitungsnamen):

gcloud run services update-traffic hello-world-colors \
  --region $REGION \
  --to-revisions hello-world-colors-0000X-XXX=50,hello-world-colors-0000X-XXX=50

Test

Testen Sie die Funktion, indem Sie die öffentliche URL aufrufen. Die orangefarbene und die gelbe Überarbeitung sollten jeweils etwa halb so oft angezeigt werden:

36ca0c5f39cc89cf.png 391286a08ad3cdde.png

Weitere Informationen finden Sie unter Rollbacks, graduelle Einführungen und Trafficmigration.

8. Mindestanzahl von Instanzen

In Cloud Run Functions können Sie eine Mindestanzahl von Funktionsinstanzen angeben, die einsatzbereit gehalten werden und zum Verarbeiten von Anfragen bereit sein sollen. Dies ist nützlich, um die Anzahl der Kaltstarts zu begrenzen.

In diesem Schritt stellen Sie eine Funktion mit langsamer Initialisierung bereit. Sie werden das Kaltstartproblem beobachten. Anschließend stellen Sie die Funktion mit dem Mindestinstanzwert 1 bereit, um den Kaltstart zu vermeiden.

Erstellen

Erstellen Sie einen Ordner für die App und wechseln Sie zu diesem Ordner:

mkdir ../min-instances
cd ../min-instances

Erstellen Sie eine main.go-Datei. Dieser Go-Dienst hat eine init-Funktion, die 10 Sekunden lang in den Ruhemodus wechselt, um eine lange Initialisierung zu simulieren. Sie enthält auch eine HelloWorld-Funktion, die auf HTTP-Aufrufe reagiert:

package p

import (
        "fmt"
        "net/http"
        "time"
)

func init() {
        time.Sleep(10 * time.Second)
}

func HelloWorld(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, "Slow HTTP Go in Cloud Run functions!")
}

Bereitstellen

Stellen Sie die erste Überarbeitung der Funktion mit dem Standardwert für die Mindestanzahl von Instanzen (0) bereit:

gcloud run deploy go-slow-function \
 --source . \
 --base-image go123 \
 --function HelloWorld \
 --region $REGION \
 --no-allow-unauthenticated

Testen Sie die Funktion mit diesem Befehl:

# get the Service URL
SERVICE_URL="$(gcloud run services describe go-slow-function --region $REGION --format 'value(status.url)')"

# invoke the service
curl -H "Authorization: bearer $(gcloud auth print-identity-token)" -X GET $SERVICE_URL

Beim ersten Aufruf kommt es zu einer Verzögerung von 10 Sekunden (Kaltstart), bevor die Meldung angezeigt wird. Nachfolgende Aufrufe sollten sofort zurückgegeben werden.

Mindestanzahl von Instanzen festlegen

Um den Kaltstart bei der ersten Anfrage zu vermeiden, stellen Sie die Funktion mit dem Flag --min-instances auf 1 noch einmal bereit:

gcloud run deploy go-slow-function \
 --source . \
 --base-image go123 \
 --function HelloWorld \
 --region $REGION \
 --no-allow-unauthenticated \
 --min-instances 1

Test

Testen Sie die Funktion noch einmal:

curl -H "Authorization: bearer $(gcloud auth print-identity-token)" -X GET $SERVICE_URL

Bei der ersten Anfrage sollte es nicht mehr zu einer Verzögerung von 10 Sekunden kommen. Das Kaltstartproblem für den ersten Aufruf (nach langer Zeit ohne Aufrufe) ist dank der Mindestanzahl von Instanzen behoben.

Weitere Informationen finden Sie unter Mindestinstanzen verwenden.

9. Glückwunsch!

Herzlichen Glückwunsch zum Abschluss des Codelabs!

Behandelte Themen

  • Übersicht über Cloud Run-Funktionen und die Verwendung automatischer Basis-Image-Updates.
  • So schreiben Sie eine Funktion, die auf HTTP-Aufrufe reagiert.
  • So schreiben Sie eine Funktion, die auf Pub/Sub-Nachrichten reagiert.
  • So schreiben Sie eine Funktion, die auf Cloud Storage-Ereignisse reagiert.
  • So teilen Sie den Traffic auf zwei Überarbeitungen auf.
  • Kaltstarts mit einer Mindestanzahl von Instanzen vermeiden