Systemtest von Cloud Functions mit Cloud Build und Terraform

In dieser Anleitung wird beschrieben, wie Sie End-to-End-Tests einer Anwendung, die mit Cloud Functions erstellt wurde, automatisieren. Cloud Build führt die Testpipeline aus. Mit HashiCorp Terraform werden die für die Tests erforderlichen Google Cloud-Ressourcen eingerichtet und heruntergefahren. Ein Cloud Build-Trigger initiiert die Pipeline nach jedem Code-Commit.

Die Testbarkeit spielt beim Entwerfen von Anwendungen sowie bei der Bewertung architektonischer Entscheidungen eine wichtige Rolle. Es ist wichtig, anhand einer umfassenden Reihe von Tests (einschließlich automatisierter Tests von Einheiten, Integrationen und End-to-End-Systemen) zu prüfen, ob sich die Anwendung erwartungsgemäß verhält. Ausführliche Informationen zur Vorgehensweise in den einzelnen Testkategorien für unterschiedliche Cloud Functions-Szenarien finden Sie in der Testübersicht.

Einheitentests lassen sich in der Regel einfach erstellen und ausführen, da sie von der Ausführungsumgebung isoliert und unabhängig sind. Integrations- und Systemtests hingegen sind komplexer, insbesondere in Cloud-Umgebungen. End-to-End-Systemtests sind insbesondere bei Anwendungen relevant, die serverlose Technologien wie Cloud Functions verwenden. Diese Anwendungen sind häufig ereignisgesteuert, lose gekoppelt und wurden möglicherweise unabhängig voneinander bereitgestellt. Umfassende End-to-End-Tests sind unerlässlich, um zu prüfen, ob die Funktionen auf Ereignisse in der Google Cloud-Ausführungsumgebung richtig reagieren.

Architektur

Im folgenden Diagramm sehen Sie die Komponenten, die Sie in dieser Anleitung verwenden.

Architekturdiagramm von Build- und Testprojekten

Die Architektur besteht aus folgenden Komponenten:

  • Dem Projekt build, das die Cloud Build-Pipeline hostet und ausführt.
  • Dem test-Projekt, das Google Cloud-Ressourcen für die zu testende Beispielanwendung hostet.
    • Als Beispielanwendung wird die in der Anleitung Serverlose Überwachung der Webleistung mit Cloud Functions beschriebene Anwendung verwendet.
    • Die Google Cloud-Ressourcen für die Beispielanwendungen werden für jede Build-Ausführung erstellt und gelöscht. Ausgenommen davon ist die Firestore-Datenbank. Sie wird einmal erstellt und für alle nachfolgenden Builds wiederverwendet.

Ziele

  • Sie erstellen eine Cloud Build-Pipeline, um Einheiten und End-to-End-Systeme für eine Beispielanwendung zu testen, die mit Cloud Functions erstellt wurde.
  • Sie richten mithilfe von Terraform (im Build enthalten) die für die Anwendung erforderlichen Google Cloud-Ressourcen ein und löschen sie anschließend wieder.
  • Sie isolieren die Testumgebung in einem speziell dafür erstellten Google Cloud-Testprojekt.
  • Sie erstellen in Cloud Source Repositories ein Git-Repository und fügen einen Cloud Build-Trigger hinzu, um nach einem Commit den End-to-End-Build auszuführen.

Kosten

In dieser Anleitung werden die folgenden kostenpflichtigen Komponenten von Google Cloud verwendet:

Mit dem Preisrechner können Sie eine Kostenschätzung für Ihre voraussichtliche Nutzung vornehmen. Neuen Google Cloud-Nutzern steht möglicherweise eine kostenlose Testversion zur Verfügung.

Nach Abschluss dieser Anleitung können Sie weitere Kosten vermeiden, indem Sie die erstellten Ressourcen löschen. Weitere Informationen finden Sie unter Bereinigen.

Hinweis

  1. Wählen Sie ein Cloud-Projekt aus oder erstellen Sie eins. Dies ist das Projekt test, das die Beispielanwendung hostet.

    Zur Projektauswahl

  2. Notieren Sie sich die Google Cloud-Projekt-ID für das Projekt test. Sie benötigen diese ID im nächsten Abschnitt zum Einrichten der Umgebung.
  3. Aktivieren Sie die Cloud Build, Cloud Functions und Cloud Source Repositories APIs für dieses Projekt.

    APIs aktivieren

  4. Rufen Sie in der Cloud Console die Seite Firestore auf.
  5. Zur Seite "Firestore"
  6. Erstellen Sie eine Firestore-Datenbank.

    Firestore-Datenbank erstellen

  7. Wählen oder erstellen Sie ein anderes Google Cloud-Projekt. Dies ist das Projekt build, das die Cloud Build-Pipeline hostet.
  8. Zur Seite "Ressourcen verwalten"
  9. Achten Sie darauf, dass die Abrechnung für Ihre Google Cloud-Projekte aktiviert ist.

    Weitere Informationen zum Aktivieren der Abrechnung

Umgebung einrichten

In dieser Anleitung führen Sie Befehle in Cloud Shell aus. Cloud Shell ist eine Shell-Umgebung, in der das Cloud SDK einschließlich des gcloud-Befehlszeilentools vorinstalliert ist. Die Werte sind bereits für Ihr aktuelles Projekt festgelegt. Die Initialisierung von Cloud Shell kann mehrere Minuten dauern.

  1. Öffnen Sie in der Cloud Console für das Projekt build Cloud Shell.

    Zu Cloud Shell

  2. Legen Sie eine Variable für die Google Cloud-Projekt-ID test fest, die Sie zuvor kopiert haben:

    export TEST_PROJECT=your-test-project-id
    

    Ersetzen Sie Folgendes:

    • your-test-project-id: Die ID Ihres test-Google Cloud-Projekts.
  3. Legen Sie die Projekt-ID und die Projektnummer des aktuellen build-Google Cloud-Projekts als Variablen fest:

    export BUILD_PROJECT=$(gcloud config get-value core/project)
    export BUILD_PROJECT_NUM=$(gcloud projects list \
        --filter="$BUILD_PROJECT" --format="value(PROJECT_NUMBER)")
    
  4. Legen Sie für die Deployment-Region eine Variable fest:

    export REGION=us-central1
    

    Obwohl in dieser Anleitung die Region us-central1 verwendet wird, können Sie diese in jede Region ändern, in der Cloud Functions verfügbar ist.

  5. Klonen Sie das Repository, das den Code der in diesem Tutorial verwendeten Beispielanwendung enthält:

    git clone \
        https://github.com/GoogleCloudPlatform/solutions-serverless-web-monitoring.git
    
  6. Wechseln Sie zum Projektverzeichnis:

    cd solutions-serverless-web-monitoring
    

Testinfrastruktur mit Terraform erstellen

In dieser Anleitung wird Terraform verwendet, um Google Cloud-Ressourcen innerhalb des Testprojekts automatisch zu erstellen und zu löschen. Durch das Erstellen unabhängiger Ressourcen für jeden Build werden die Tests voneinander isoliert. Wenn Sie Tests isolieren, können Builds gleichzeitig ausgeführt und Test-Assertions für bestimmte Ressourcen gemacht werden. Durch das Herunterfahren der Ressourcen am Ende jedes Builds können Sie die Kosten minimieren.

In dieser Anleitung wird die in der Anleitung zur serverlosen Webüberwachung mit Cloud Functions beschriebene Anwendung bereitgestellt. Die Anwendung besteht aus einer Reihe von Cloud Functions-, Cloud Storage-Buckets, Pub/Sub-Ressourcen und einer Firestore-Datenbank. Die Terraform-Konfiguration definiert die zum Erstellen dieser Ressourcen erforderlichen Schritte. Die Firestore-Datenbank wird nicht von Terraform bereitgestellt. Die Datenbank wird einmal erstellt und von allen Tests wiederverwendet.

Das folgende Codebeispiel aus der Terraform-Konfigurationsdatei main.tf zeigt die Schritte, die zum Bereitstellen der Cloud Functions-Funktion trace erforderlich sind. Die vollständige Konfiguration finden Sie in der Datei.

data "archive_file" "local_tracer_source" {
  type        = "zip"
  source_dir  = "./functions/tracer"
  output_path = "${var.local_output_path}/tracer.zip"
}

resource "google_storage_bucket_object" "gcs_tracer_source" {
  name   = "tracer.zip"
  bucket = "${google_storage_bucket.bucket_source_archives.name}"
  source = "${data.archive_file.local_tracer_source.output_path}"
}

resource "google_cloudfunctions_function" "function_tracer" {
  name = "tracer-${var.suffix}"
  project = "${var.project_id}"
  region = "${var.region}"
  available_memory_mb = "1024"
  entry_point = "trace"
  runtime = "nodejs8"
  trigger_http = "true"
  source_archive_bucket = "${google_storage_bucket.bucket_source_archives.name}"
  source_archive_object = "${google_storage_bucket_object.gcs_tracer_source.name}"
  environment_variables = {
    BUCKET_METRICS = "${google_storage_bucket.bucket_metrics.name}"
    ALLOWED_HOSTS = "${var.allowed_hosts}"
  }
}

// prevent unauthenticated invocations
resource "google_cloudfunctions_function_iam_binding" "tracer_disallow_unauthenticated" {
  project = "${var.project_id}"
  region = "${var.region}"
  cloud_function = "${google_cloudfunctions_function.function_tracer.name}"
  role = "roles/cloudfunctions.invoker"
  members = [
  ]
  depends_on = [
    google_cloudfunctions_function.function_tracer
  ]
}

In diesem Schritt führen Sie die Terraform-Konfiguration aus, um die Testressourcen bereitzustellen. In einem späteren Schritt werden die Ressourcen von Cloud Build automatisch bereitgestellt.

  1. Initialisieren Sie in Cloud Shell Terraform:

    docker run -v $(pwd):/app -w /app hashicorp/terraform:0.12.0 init
    

    Sie verwenden das öffentliche Docker-Image für Terraform. Docker ist bereits in Cloud Shell installiert. Das aktuelle Arbeitsverzeichnis wird als Volume bereitgestellt, damit der Docker-Container die Terraform-Konfigurationsdatei lesen kann.

  2. Erstellen Sie die Ressourcen mit dem Terraform-Befehl apply:

    docker run -v $(pwd):/app -w /app hashicorp/terraform:0.12.0 apply \
        --auto-approve \
        -var "project_id=$TEST_PROJECT" \
        -var "region=$REGION" \
        -var "suffix=$TEST_PROJECT"
    

    Der Befehl enthält Variablen, die das Google Cloud-Projekt und die Region angeben, in der die Testressourcen erstellt werden sollen. Außerdem enthält er ein Suffix zum Erstellen benannter Ressourcen für diesen Schritt. In einem späteren Schritt wird von Cloud Build automatisch ein geeignetes Suffix bereitgestellt.

    Es dauert einige Minuten, bis der Vorgang abgeschlossen ist.

  3. Bestätigen Sie, dass Ressourcen im Projekt test erstellt wurden:

    gcloud functions list --project $TEST_PROJECT
    

    Die Ausgabe enthält drei Cloud Functions-Funktionen, deren Namen mit dem zuvor angegebenen Suffix enden.

End-to-End-Tests ausführen

In diesem Abschnitt führen Sie End-to-End-Tests für die Testinfrastruktur durch, die Sie im vorherigen Abschnitt bereitgestellt haben.

Der folgende Codeausschnitt zeigt die Tests. Die Tests validieren sowohl das erfolgreiche als auch das fehlgeschlagene Szenario. Die Testpipeline kann so zusammengefasst werden:

  • Zuerst ruft der Test die Funktion trace auf. Dadurch werden ein Ereignisfluss durch die Anwendung sowie weitere Funktionen ausgelöst.
  • Anschließend prüft der Test das Verhalten der einzelnen Funktionen und bestätigt, dass Objekte in Cloud Storage geschrieben, Ergebnisse in Firestore gespeichert und Pub/Sub-Warnungen bei Fehlern erzeugt werden.
def test_e2e_pass():
  run_pipeline('http://www.example.com/', True)

def test_e2e_fail():
  run_pipeline('https://cloud.google.com/docs/tutorials', False)

def run_pipeline(url, should_pass):
  """Triggers the web analysis pipeline and verifies outputs of each stage.

  Args:
    url (str): The page to analyze.
    should_pass (bool): Whether the page should load within the threshold time.
  """
  trace_response = call_tracer(url)
  filename = assert_tracer_response(trace_response)
  assert_gcs_objects(filename)
  assert_firestore_doc(filename, should_pass)
  assert_pubsub_message(should_pass)

  # clean up
  delete_gcs_objects(filename)
  delete_firestore_doc(filename)

Führen Sie für die End-to-End-Tests die folgenden Schritte aus:

  1. Erstellen Sie in Cloud Shell eine neue virtualenv-Umgebung. Das Dienstprogramm virtualenv ist bereits in Cloud Shell installiert.

    virtualenv venv
    
  2. Aktivieren Sie die Umgebung virtualenv:

    source venv/bin/activate
    
  3. Installieren Sie die erforderlichen Python-Bibliotheken:

    pip install -r requirements.txt
    
  4. Führen Sie die End-to-End-Tests aus:

    python -m pytest e2e/ --tfstate terraform.tfstate
    

    Sie übergeben die Terraform-Statusdatei, die Details zu den im vorherigen Abschnitt erstellten Testressourcen enthält.

    Die Tests können einige Minuten dauern. Eine Meldung zeigt an, dass zwei Tests bestanden wurden. Sie können alle Warnungen ignorieren.

  5. Deaktivieren Sie die Testressourcen mit dem Terraform-Befehl destroy:

    docker run -v $(pwd):/app -w /app hashicorp/terraform:0.12.0 destroy \
        --auto-approve \
        -var "project_id=$TEST_PROJECT" \
        -var "region=$REGION" \
        -var "suffix=$TEST_PROJECT"
    
  6. Prüfen Sie, ob die Ressourcen heruntergefahren wurden:

    gcloud functions list --project $TEST_PROJECT
    

    Es gibt keine Cloud Functions-Funktionen mehr, deren Namen mit dem zuvor angegebenen Suffix enden.

Cloud Build-Pipeline senden

In diesem Abschnitt automatisieren Sie die Testpipeline mit Cloud Build.

Cloud Build-Berechtigungen festlegen

Sie führen Cloud Build mit einem Cloud Build-Dienstkonto aus. Bei den vom Build ausgeführten Systemtests werden Cloud Functions-Funktionen, Cloud Storage-Buckets, Pub-/Sub-Ressourcen und Firestore-Dokumente erstellt, mit denen interagiert wird. Für diese Aufgaben benötigt Cloud Build Folgendes:

In diesem Verfahren fügen Sie erst die entsprechenden Rollen und dann das Cloud Build-Dienstkonto hinzu.

  1. Fügen Sie in Cloud Shell dem standardmäßigen Cloud Build-Dienstkonto die entsprechenden IAM-Rollen hinzu:

    for role in cloudfunctions.developer pubsub.editor storage.admin datastore.user; do \
        gcloud projects add-iam-policy-binding $TEST_PROJECT \
        --member="serviceAccount:$BUILD_PROJECT_NUM@cloudbuild.gserviceaccount.com" \
        --role="roles/$role"; \
        done
    
  2. Fügen Sie das Cloud Build-Dienstkonto als serviceAccountUser des App Engine-Dienstkontos im Testprojekt hinzu:

    gcloud iam service-accounts add-iam-policy-binding \
        $TEST_PROJECT@appspot.gserviceaccount.com \
        --member="serviceAccount:$BUILD_PROJECT_NUM@cloudbuild.gserviceaccount.com" \
        --role=roles/iam.serviceAccountUser \
        --project $TEST_PROJECT
    

Manuellen Build senden

Der Build führt vier logische Aufgaben aus:

  • Einheitentests ausführen
  • Beispielanwendung bereitstellen
  • End-to-End-Tests ausführen
  • Beispielanwendung herunterfahren

Prüfen Sie das folgende Code-Snippet aus der Datei cloudbuild.yaml. Das Snippet zeigt die einzelnen Cloud Build-Schritte, mit denen die Beispielanwendung mit Terraform bereitgestellt und die End-to-End-Tests ausgeführt werden.

# setup Terraform using public terraform Docker image
- id: terraform-init
  name: hashicorp/terraform:0.12.0
  args: ['init']

# deploy the required GCP resources
- id: terraform-apply
  name: hashicorp/terraform:0.12.0
  args: ['apply', '-auto-approve']
  env:
    - 'TF_VAR_project_id=$_TEST_PROJECT_ID'
    - 'TF_VAR_region=$_REGION'
    - 'TF_VAR_suffix=$BUILD_ID'

# run end-to-end tests to verify live interactions
- id: end-to-end-tests
  name: 'python:3.7-slim'
  entrypoint: /bin/sh
  args:
    - -c
    - 'pip install -r requirements.txt && python -m pytest e2e --tfstate terraform.tfstate'

Gehen Sie wie folgt vor, um einen manuellen Build an Cloud Build zu senden und End-to-End-Tests auszuführen:

  • Geben Sie in Cloud Shell folgenden Befehl ein:

    gcloud builds submit --config cloudbuild.yaml \
        --substitutions=_REGION=$REGION,_TEST_PROJECT_ID=$TEST_PROJECT
    

    Die Ausführung des Builds dauert mehrere Minuten. Die folgenden Build-Schritte werden ausgeführt:

    • Cloud Build verwendet Substitutionen, um Variablen bereitzustellen, die das Google Cloud-Projekt und die Region für die von Ihnen erstellten Testressourcen angeben.

    • Cloud Build führt den Build im Google Cloud-Projekt build aus. Die Testressourcen werden im separaten Projekt test erstellt.

    Die Build-Logs werden an Cloud Shell gestreamt, damit Sie den Fortschritt des Builds verfolgen können. Der Log-Stream wird nach der Erstellung beendet. Die anschließend eingeblendeten Meldungen geben an, dass der letzte Build-Schritt terraform-destroy erfolgreich war und der Build abgeschlossen ist.

Testausführung automatisieren

Ein Grundprinzip der kontinuierlichen Integration (Continuous Integration, CI) ist das regelmäßige Durchführen umfassender automatisierter Tests. Normalerweise wird die Build-Testpipeline für jeden Commit an ein gemeinsam genutztes Code-Repository ausgeführt. Dies gewährt, dass jedes Commit für das gemeinsam genutzte Repository getestet und validiert wird, sodass Ihr Team Probleme frühzeitig erkennen kann.

In den nächsten Abschnitten führen Sie die folgenden Aktionen aus:

  • Git-Repository in Cloud Source Repositories erstellen
  • Cloud Build-Trigger hinzufügen, um bei jedem Commit den End-to-End-Build auszuführen
  • Code mit Push in das Repository übertragen, um den Build auszulösen

Cloud Source Repository und Cloud Build-Trigger erstellen

  1. Erstellen Sie in Cloud Shell ein neues Cloud Source Repository:

    gcloud source repos create serverless-web-monitoring
    
  2. Öffnen Sie in der Cloud Console die Cloud Build-Seite Trigger.

    Zur Seite "Trigger"

  3. Klicken Sie auf Trigger erstellen.

    Die Seite Trigger erstellen wird geöffnet.

  4. Füllen Sie die folgenden Optionen aus:

    • Geben Sie im Feld Name den Wert end-to-end-tests ein.
    • Wählen Sie unter Ereignis die Option Push zu Zweig aus.
    • Wählen Sie unter Quelle serverless-web-monitoring als Repository und ^master$ als Zweig aus.
    • Unter Konfiguration wählen Sie die Konfigurationsdatei (yaml oder json) von Cloud Build.
    • Geben Sie für den Speicherort der Cloud Build-Konfigurationsdatei cloudbuild.yaml ein.
    • Wenn Sie eine Variablensubstitution hinzufügen möchten, um die Google Cloud-Region anzugeben, in der die Testressourcen erstellt werden, klicken Sie auf Variable hinzufügen:

      • Variable: _REGION
      • Wert: your-test-region

        Ersetzen Sie Folgendes:

        • your-test-region: Der Wert der Variable $REGION in Cloud Shell.
    • Wenn Sie eine weitere Variablensubstitution hinzufügen möchten, um die ID des Projekts anzugeben, in dem die Testressourcen gehostet werden, klicken Sie auf Variable hinzufügen:

      • Variable: _TEST_PROJECT_ID
      • Wert: your-test-project

        Ersetzen Sie Folgendes:

        • your-test-project: Der Wert der Variable $TEST_PROJECT in Cloud Shell.
  5. Klicken Sie auf Erstellen, um den Build-Trigger zu speichern.

Build starten

  1. Fügen Sie in Cloud Shell das Repository als neuen Remotebereich in Ihrer Git-Konfiguration hinzu:

    git remote add csr \
        https://source.developers.google.com/p/$BUILD_PROJECT/r/serverless-web-monitoring
    
  2. Zum Auslösen des Builds übertragen Sie den Code in das Repository.

    git push csr master
    
  3. Listen Sie die neuesten Builds auf:

    gcloud builds list --limit 3
    

    Die Ausgabe zeigt einen Build mit dem Status WORKING. Dies weist darauf hin, dass der Build wie erwartet ausgelöst wurde.

  4. Kopieren Sie die ID des Builds WORKING für den nächsten Schritt.

  5. Streamen Sie die Build-Logs in die Cloud Console:

    gcloud builds log --stream build-id
    

    Ersetzen Sie Folgendes:

    • build-id: Die ID des Builds WORKING, den Sie im vorherigen Schritt kopiert haben.

    Der Log-Stream wird nach der Erstellung beendet. Die anschließend eingeblendeten Meldungen geben an, dass der letzte Build-Schritt terraform-destroy erfolgreich war und der Build abgeschlossen ist.

Bereinigen

Damit Ihrem Google Cloud-Konto die in dieser Anleitung verwendeten Ressourcen nicht in Rechnung gestellt werden, löschen Sie entweder das Projekt, das die Ressourcen enthält, oder Sie behalten das Projekt und löschen die einzelnen Ressourcen.

Projekt löschen

  1. Wechseln Sie in der Cloud Console zur Seite Ressourcen verwalten.

    Zur Seite „Ressourcen verwalten“

  2. Wählen Sie in der Projektliste das Projekt aus, das Sie löschen möchten, und klicken Sie dann auf Löschen.
  3. Geben Sie im Dialogfeld die Projekt-ID ein und klicken Sie auf Shut down (Beenden), um das Projekt zu löschen.

Nächste Schritte