Tutorial 3: Dienst-zu-Dienst-Kommunikation

Wichtig

Das Job-Feature von Snowpark Container Services befindet sich derzeit in der privaten Vorschau und unterliegt den Nutzungsbedingungen für Vorschau-Features unter https://snowflake.com/legal. Weitere Informationen dazu erhalten Sie bei Ihrem Snowflake-Ansprechpartner.

Einführung

In diesem Tutorial erstellen Sie einen Snowpark Container Services-Job, der mit dem Echo-Dienst kommuniziert, den Sie in Tutorial 1 erstellt haben. Wenn der Job ausgeführt wird, sendet er eine POST-Anforderung an die Echo-Dienst-URL (die Sie in der Jobspezifikation angeben) mit einer „Hello“-Zeichenfolge im Anforderungstext. Der Echo-Dienst gibt eine Antwort mit der Zeichenfolge „Bob said Hello“ (Bob sagte Hallo) im Antworttext zurück. Sie greifen auf die Protokolle des Jobcontainers zu, um zu überprüfen, ob die Kommunikation erfolgreich war.

Dieses Tutorial besteht aus zwei Teilen:

  • Teil 1: Job erstellen und testen. Sie laden den für dieses Tutorial bereitgestellten Code herunter und befolgen die schrittweise Anleitung:

    1. Jobcode für dieses Tutorial herunterladen

    2. Docker-Image für Snowpark Container Services erstellen und Image in Repository im eigenen Konto hochladen

    3. Job-Spezifikationsdatei mit den Container-Konfigurationsinformationen für Snowflake im Stagingbereich bereitstellen. Neben dem Namen des Images, das zum Starten eines Containers verwendet werden soll, enthält die Spezifikationsdatei auch die Umgebungsvariable (SERVICE_URL) für die Echo-Dienst-URL. Der Anwendungscode liest diese Umgebungsvariable, um Anforderungen an den Echo-Dienst zu senden.

    4. Job ausführen. Mit dem Befehl EXECUTE SERVICE können Sie den Job ausführen, indem Sie die Spezifikationsdatei und den Computepool angeben, in dem Snowflake den Container ausführen kann. Und schließlich können Sie auf die Protokolle des Jobcontainers zugreifen, um zu überprüfen, ob die Kommunikation zwischen Job und Dienst erfolgreich war.

  • Teil 2: Erläuterungen zum Jobcode. Dieser Abschnitt bietet eine Übersicht zum Dienstcode und zeigt auf, wie die verschiedenen Komponenten zusammenarbeiten.

Voraussetzungen

Schließen Sie Tutorial 1 ab, und überprüfen Sie, ob der Echo-Dienst ausgeführt wird.

SELECT SYSTEM$GET_SERVICE_STATUS('echo_service', 10);
Copy

1: Dienstcode herunterladen

Zum Erstellen eines Jobs wird ein Code (eine Python-Anwendung) bereitgestellt.

  1. Laden Sie die ZIP-Datei in ein Verzeichnis herunter.

  2. Entpacken Sie den Inhalt, der ein Verzeichnis für jedes Tutorial enthält. Das Verzeichnis Tutorial-3 enthält die folgenden Dateien:

    • service_to_service.py

    • Dockerfile

    • service_to_service_spec.yaml

2: Image erstellen und hochladen

Erstellen Sie ein Image für die linux/amd64-Plattform, die von Snowpark Container Services unterstützt wird, und laden Sie das Image dann in das Image-Repository in Ihrem Konto hoch (siehe Grundlegende Einrichtung).

Sie benötigen Informationen zum Repository (die Repository-URL und den Hostnamen der Registry), bevor Sie das Image erstellen und hochladen können. Weitere Informationen dazu finden Sie unter Registry und Repositorys.

Informationen zum Repository abrufen

  1. Um die Repository-URL zu erhalten, führen Sie den SQL-Befehl SHOW IMAGE REPOSITORIES aus.

    SHOW IMAGE REPOSITORIES;
    
    Copy
    • Die URL ist in der Spalte repository_url der Ausgabe enthalten. Siehe folgendes Beispiel:

      <orgname>-<acctname>.registry.snowflakecomputing.com/tutorial_db/data_schema/tutorial_repository
      
    • Der Hostname in der Repository-URL ist der Name des Registry-Hosts. Siehe folgendes Beispiel:

      <orgname>-<acctname>.registry.snowflakecomputing.com
      

Image erstellen und in das Repository hochladen

  1. Öffnen Sie ein Terminalfenster, und wechseln Sie in das Verzeichnis, das die entpackten Dateien enthält.

  2. Um ein Docker-Image zu erstellen, führen Sie den folgenden Befehl docker build mithilfe der Docker-CLI aus. Beachten Sie, dass der Befehl das aktuelle Arbeitsverzeichnis (.) als PATH für die Dateien angibt, die für das Erstellen des Images verwendet werden sollen.

    docker build --rm --platform linux/amd64 -t <repository_url>/<image_name> .
    
    Copy
    • Für image_name verwenden Sie service_to_service:latest:

    Beispiel

    docker build --rm --platform linux/amd64 -t myorg-myacct.registry.snowflakecomputing.com/tutorial_db/data_schema/tutorial_repository/service_to_service:latest .
    
    Copy
  3. Laden Sie das Image in das Repository in Ihrem Snowflake-Konto hoch. Damit Docker ein Image in Ihrem Namen in Ihr Repository hochladen kann, müssen Sie Docker zunächst mit Snowflake authentifizieren.

    1. Um Docker bei der Snowflake-Registrierung zu authentifizieren, führen Sie den folgenden Befehl aus.

      docker login <registry_hostname> -u <username>
      
      Copy
      • Geben Sie dabei für username Ihren Snowflake-Benutzernamen an. Docker fordert Sie zur Eingabe Ihres Kennworts auf.

    2. Führen Sie den folgenden Befehl aus, um das Image hochzuladen:

      docker push <repository_url>/<image_name>
      
      Copy

      Beispiel

      docker push myorg-myacct.registry.snowflakecomputing.com/tutorial_db/data_schema/tutorial_repository/service_to_service:latest
      
      Copy

3: Spezifikationsdatei im Stagingbereich bereitstellen

  • Um Ihre Jobspezifikationsdatei (service_to_service_spec.yaml) in den Stagingbereich zu laden, verwenden Sie eine der folgenden Optionen:

    Der Befehl setzt OVERWRITE=TRUE, sodass Sie die Datei bei Bedarf erneut hochladen können (z. B. wenn Sie einen Fehler in Ihrer Spezifikationsdatei behoben haben). Wenn der PUT-Befehl erfolgreich ausgeführt wurde, werden Informationen zu der hochgeladenen Datei ausgegeben.

4: Job ausführen

Jetzt sind Sie bereit, den von Ihnen erstellen Snowflake-Job zu testen. Während der Job ausgeführt wird, erfasst Snowflake alle Daten, die Ihr Code im Container an die Standardausgabe oder den Standardfehler ausgibt, als Protokolleinträge. Sie können die Systemfunktion SYSTEM$GET_JOB_LOGS verwenden, um auf die Protokolleinträge zuzugreifen. Weitere Informationen dazu finden Sie unter Snowpark Container Services: Zusätzliche Hinweise zu Diensten und Jobs.

  1. Um einen Job zu starten, führen Sie den Befehl EXECUTE SERVICE aus:

    EXECUTE SERVICE
      IN COMPUTE POOL tutorial_compute_pool
      FROM @tutorial_stage
      SPEC='service_to_service_spec.yaml';
    
    Copy

    Beachten Sie Folgendes:

    • FROM und SPEC geben den Namen des Stagingbereichs bzw. den Namen der Jobspezifikationsdatei an.

    • COMPUTE_POOL stellt die Computeressourcen bereit, auf denen Snowflake den Job ausführt.

    Snowflake führt den in der Spezifikationsdatei angegebenen Container aus. Der Container liest den Wert der Umgebungsvariablen SERVICE_URL (http://echo-service:8000/echo) und sendet eine Anforderung an den Echo-Dienst an Port 8000 im HTTP-Pfad /echo.

    Snowflake startet den Job und gibt in der Ausgabe eine Job-ID zurück:

    +------------------------------------------------------------------------+
    | status                                                                 |
    |------------------------------------------------------------------------|
    | Execution 01af7ee6-0001-cb52-0020-c5870077223a completed successfully. |
    +------------------------------------------------------------------------+
    

    Beachten Sie, dass die Antwort eine von Snowflake zugewiesene Abfrage-Job-ID enthält.

  2. (optional) Nach Beendigung des Jobs können Sie weitere Informationen zu dem ausgeführten Job abrufen. Dies ist nützlich für die Fehlersuche bei Fehlschlagen des Jobs.

    1. Optional: Um die Job-ID des obigen EXECUTE SERVICE-Befehls in einer Sitzungsvariablen zu speichern, damit Sie später darauf verweisen können, führen Sie den folgenden Befehl aus:

      SET job_id = last_query_id();
      
      Copy

      Beachten Sie, dass Sie die Job-ID unmittelbar nach seiner Erstellung erfassen müssen, da Sie sonst den falschen Wert erfassen.

    2. Um den Jobstatus durch Aufrufen der Funktion SYSTEM$GET_JOB_STATUS zu ermitteln, verwenden Sie eine der folgenden Optionen:

      • Verwenden der von EXECUTE SERVICE zurückgegebenen Job-ID:

        CALL SYSTEM$GET_JOB_STATUS('<job_id returned by EXECUTE SERVICE>');
        
        Copy
      • Verwenden der Sitzungsvariable:

        CALL SYSTEM$GET_JOB_STATUS($job_id);
        
        Copy

      Beispielausgabe:

      [
        {
          "status":"DONE",
          "message":"Container finished",
          "containerName":"main",
          "instanceId":"0",
          "serviceName":"JOB_01ABD9E5000046DF00000E99000BC01E",
          "image":"myorg-myacct.registry.snowflakecomputing.com/tutorial_db/data_schema/tutorial_repository/service_to_service:latest",
          "restartCount":0,
          "startTime":"2023-01-01T00:00:00Z"
        }
      ]
      
  3. Um die Jobprotokolle zu lesen, verwenden Sie eine der folgenden Optionen:

    • Verwenden Sie die von EXECUTE SERVICE zurückgegebene Abfrage-Job-ID:

      CALL SYSTEM$GET_JOB_LOGS('<job_id returned by EXECUTE SERVICE>', 'main');
      
      Copy
    • Verwenden Sie die Sitzungsvariable:

      CALL SYSTEM$GET_JOB_LOGS($job_id, 'main');
      
      Copy

    main ist der Name des Containers, aus dem Sie das Protokoll abrufen. Diesen Containernamen verwenden Sie in der Jobspezifikationsdatei zur Angabe des Containers.

    Beispielprotokoll:

    +--------------------------------------------------------------------------------------------------------------------------+
    | SYSTEM$GET_JOB_LOGS                                                                                                      |
    |--------------------------------------------------------------------------------------------------------------------------|
    | service-to-service [2023-04-29 21:52:09,208] [INFO] Calling http://echo-service:8000/echo with input Hello               |
    | service-to-service [2023-04-29 21:52:09,212] [INFO] Received response from http://echo-service:8000/echo: Bob said Hello |
    +--------------------------------------------------------------------------------------------------------------------------+
    

5: Bereinigen

Snowflake erhebt Gebühren für die Computepool-Knoten, die für Ihr Konto aktiv sind. (Siehe Verwenden von Computepools). Um unerwünschte Gebühren zu vermeiden, stoppen Sie zunächst alle Dienste, die derzeit in einem Computepool ausgeführt werden. Anschließend können Sie den Computepool entweder anhalten (wenn Sie ihn später wieder verwenden möchten) oder ihn löschen.

  1. Stoppen Sie alle Dienste und Jobs im Computepool.

    ALTER COMPUTE POOL tutorial_compute_pool STOP ALL;
    
    Copy
  2. Löschen Sie den Computepool.

    DROP COMPUTE POOL tutorial_compute_pool;
    
    Copy

Sie können auch die Image-Registry (alle Images entfernen) und den internen Stagingbereich (Spezifikationen entfernen) bereinigen.

DROP IMAGE REPOSITORY tutorial_repository;
DROP STAGE tutorial_stage;
Copy

6: Erläuterungen zum Jobcode

In diesem Abschnitt werden die folgenden Themen behandelt:

Untersuchen der bereitgestellten Dateien

Die heruntergeladene ZIP-Datei enthält die folgenden Dateien:

  • service_to_service.py

  • Dockerfile

  • service_to_service_spec.yaml

Dieser Abschnitt bietet eine Übersicht darüber, wie der Code den Job implementiert.

Datei „service_to_service.py“

import json
import logging
import os
import requests
import sys

SERVICE_URL = os.getenv('SERVICE_URL', 'http://localhost:8080/echo')
ECHO_TEXT = 'Hello'

def get_logger(logger_name):
  logger = logging.getLogger(logger_name)
  logger.setLevel(logging.DEBUG)
  handler = logging.StreamHandler(sys.stdout)
  handler.setLevel(logging.DEBUG)
  handler.setFormatter(
    logging.Formatter(
      '%(name)s [%(asctime)s] [%(levelname)s] %(message)s'))
  logger.addHandler(handler)
  return logger

logger = get_logger('service-to-service')

def call_service(service_url, echo_input):
  logger.info(f'Calling {service_url} with input {echo_input}')

  row_to_send = {"data": [[0, echo_input]]}
  response = requests.post(url=service_url,
                           data=json.dumps(row_to_send),
                           headers={"Content-Type": "application/json"})

  message = response.json()
  if message is None or not message["data"]:
    logger.error('Received empty response from service ' + service_url)

  response_row = message["data"][0]
  if len(response_row) != 2:
    logger.error('Unexpected response format: ' + response_row)

  echo_reponse = response_row[1]
  logger.info(f'Received response from {service_url}: ' + echo_reponse)

if __name__ == '__main__':
  call_service(SERVICE_URL, ECHO_TEXT)
Copy

Bei Ausführung des Jobs passiert Folgendes:

  1. Snowflake verwendet den in der Spezifikationsdatei angegebenen Wert, um die Umgebungsvariable SERVICE_URL im Container festzulegen.

  2. Der Code liest die Umgebungsvariable.

    SERVICE_URL = os.getenv('SERVICE_URL', 'http://localhost:8080/echo').
    
    Copy
  3. Die Funktion call_service() verwendet SERVICE_URL zur Kommunikation mit dem Echo-Dienst.

Datei „Dockerfile“

Diese Datei enthält alle Befehle, um ein Image mit Docker zu erstellen.

ARG BASE_IMAGE=python:3.10-slim-buster
FROM $BASE_IMAGE
COPY service_to_service.py ./
RUN pip install --upgrade pip && \
  pip install requests
CMD ["python3", "service_to_service.py"]
Copy

Datei „service_to_service_spec.yaml“ (Jobspezifikation)

Snowflake verwendet die von Ihnen in dieser Spezifikation angegebenen Informationen zur Konfiguration und Ausführung Ihres Dienstes.

spec:
container:
   - name: main
      image: /tutorial_db/data_schema/tutorial_repository/service_to_service:latest
      env:
      SERVICE_URL: "http://echo-service:8000/echo"
Copy

Diese Spezifikation stellt Informationen für Snowflake zum Konfigurieren und Ausführen Ihres Jobs bereit. Um mit dem Echo-Dienst zu kommunizieren, benötigt der Job Folgendes:

  • DNS-Name des Echo-Dienstes, an den Anforderungen gesendet werden.

  • HTTP-Port, der vom Echo-Dienst auf Anforderungen überwacht wird.

  • HTTP-Pfad, wo der Echo-Dienst die zu sendende Anforderung erwartet.

So erhalten Sie diese Informationen:

  1. Um den DNS-Namen des Echo-Dienstes (Tutorial 1) zu erhalten, führen Sie den Befehl DESCRIBE SERVICE SQL aus:

    DESCRIBE SERVICE echo_service;
    
    Copy

    Resultierender DNS-Name für den Echo-Dienst:

    echo-service.data-schema.tutorial-db.snowflakecomputing.internal
    
    Copy

    Beachten Sie, dass Sie in diesem Tutorial den Job in demselben Datenbankschema (data-schema) erstellen, in dem auch der Echo-Dienst (Tutorial 1) erstellt wird. Daher benötigen Sie nur den „echo-service“-Teil des obigen DNS-Namens, um den Wert für SERVICE_URL zu konstruieren.

  2. Rufen Sie die Portnummer (8000), der vom Echo-Dienst auf Anforderungen überwacht wird, aus der Dienstspezifikationsdatei des Echo-Dienstes ab (Tutorial 1).

Erstellen Sie dann die obige Spezifikationsdatei (service_to_service_spec.yaml). Zusätzlich zu den Pflichtfeldern containers.name und containers.image fügen Sie auch das optionale Feld containers.env hinzu.

Lokales Erstellen und Testen eines Images

Sie können das Docker-Image lokal testen, bevor Sie es in ein Repository Ihres Snowflake-Kontos hochladen. Bei lokalen Tests wird Ihr Container eigenständig ausgeführt (er ist kein Job, der von Snowflake ausgeführt wird).

Bemerkung

Der für dieses Tutorial bereitgestellte Python-Code verwendet die requests-Bibliothek, um Anfragen an einen anderen Snowpark Container Services-Dienst zu senden. Wenn Sie diese Bibliothek nicht installiert haben, führen Sie pip aus (zum Beispiel pip3 install requests).

Führen Sie die folgenden Schritte aus, um das Docker-Image von Tutorial 3 zu testen:

  1. Der Echo-Dienst muss aktiviert sein (Tutorial 1). Zum Starten des Echo-Dienstes aus Tutorial 1 führen Sie in einem Terminalfenster den folgenden Python-Befehl aus:

    SERVER_PORT=8000 python3 echo_service.py
    
    Copy
  2. Öffnen Sie ein weiteres Terminalfenster, und führen Sie den Python-Code aus, der für dieses Tutorial bereitgestellt wurde:

    SERVICE_URL=http://localhost:8000/echo python3 service_to_service.py
    
    Copy

    Beachten Sie, dass SERVICE_URL eine Umgebungsvariable ist. Für lokale Tests müssen Sie für diese Variable explizit einen Wert festlegen. Diese URL entspricht dem Port und dem HTTP-Pfad, die beim Starten des Echo-Dienstes explizit angegeben wurden.

    Wenn der Job ausgeführt wird, sendet er eine POST-Anforderung an den Echo-Dienst, der Port 8080 überwacht, mit der Zeichenfolge „Hello“ im Anforderungstext. Der Echo-Dienst gibt die Eingabe als Echo wieder zurück und antwortet mit „I said Hello“ (Ich sagte Hallo).

    Beispielantwort:

    service-to-service
      [2023-04-23 22:30:41,278]
      [INFO] Calling http://localhost:8000/echo with input Hello
    
    
    service-to-service
      [2023-04-23 22:30:41,287]
      [INFO] Received response from http://localhost:8000/echo: I said Hello
    

    Überprüfen Sie das Protokoll, um sicherzustellen, dass die Kommunikation zwischen den Diensten erfolgreich war.

Nächste Schritte

Nachdem Sie dieses Tutorial abgeschlossen haben, können Sie zu Erweiterte Tutorials zurückkehren, um weitere Themen zu erkunden.