チュートリアル3: サービス間通信

概要

このチュートリアルでは、 チュートリアル1 で作成したEchoサービスと通信するSnowpark Container Servicesジョブサービスを作成します。ジョブサービスが実行されると、リクエスト本文に「Hello」文字列を含む POST リクエストをEchoサービス URL (サービス仕様で提供したもの)に送信します。Echoサービスは、応答本文に「Bob said Hello」という文字列を含む応答を返します。ジョブサービスコンテナのログにアクセスして、通信が成功したことを確認します。

このチュートリアルには2つのパートがあります。

  • パート1: ジョブサービスを作成してテストする。 このチュートリアルで提供されるコードをダウンロードし、ステップバイステップの手順に従います。

    1. このチュートリアルのジョブサービスコードをダウンロードします。

    2. Snowpark Container Services用のDockerイメージをビルドし、アカウントのリポジトリにイメージをアップロードします。

    3. Snowflakeにコンテナー構成情報を与える仕様ファイルをステージします。この仕様では、コンテナーの起動に使用するイメージの名前に加えて、仕様は、Echoサービス URL に環境変数(SERVICE_URL)を設定します。アプリケーションコードはこの環境変数を読み取り、Echoサービスにリクエストを送信します。

    4. ジョブサービスを実行します。EXECUTE JOBSERVICE コマンドを使用して、仕様ファイルとSnowflakeがコンテナを実行できるコンピューティングプールを指定すると、ジョブサービスを実行できます。そして最後に、ジョブサービスとサービス間の通信が成功したことを確認するために、ジョブサービスコンテナからログにアクセスします。

  • パート2: ジョブサービスコードを理解する。このセクションでは、サービスコードの概要を説明し、さまざまなコンポーネントがどのように連携しているかを明らかにします。

前提条件

チュートリアル1 を完了し、Echoサービスが実行されていることを確認します。

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

1: サービスコードをダウンロードする

ジョブサービスを作成するためのコード(Pythonアプリケーション)が提供されます。

  1. ダウンロード: SnowparkContainerServices -Tutorials.zip </samples/spcs/SnowparkContainerServices-Tutorials.zip> をダウンロードします。

  2. ファイルを解凍します。チュートリアルごとに1つのディレクトリが含まれています。 Tutorial-3 ディレクトリには以下のファイルがあります。

    • service_to_service.py

    • Dockerfile

    • service_to_service_spec.yaml

2: イメージをビルドしてアップロードする

Snowpark Container Servicesがサポートするlinux/amd64プラットフォーム用のイメージをビルドし、アカウントのイメージリポジトリにイメージをアップロードします(共通セットアップ を参照)。

イメージをビルドしてアップロードする前に、リポジトリに関する情報(リポジトリ URL とレジストリのホスト名)が必要です。詳細については、 レジストリおよびリポジトリ をご参照ください。

リポジトリに関する情報を取得する

  1. リポジトリ URL を取得するには、 SHOW IMAGE REPOSITORIES SQL コマンドを実行します。

    SHOW IMAGE REPOSITORIES;
    
    Copy
    • 出力の repository_url 列は、 URL を提供します。以下に例を示します。

      <orgname>-<acctname>.registry.snowflakecomputing.com/tutorial_db/data_schema/tutorial_repository
      
    • リポジトリ URL のホスト名はレジストリのホスト名です。以下に例を示します。

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

イメージをビルドし、リポジトリにアップロードする

  1. ターミナルウィンドウを開き、解凍したファイルのあるディレクトリに移動します。

  2. Dockerイメージをビルドするには、Docker CLI を使用して以下の docker build コマンドを実行します。このコマンドは、イメージのビルドに使用するファイルの PATH として、現在の作業ディレクトリ(.)を指定していることに注意してください。

    docker build --rm --platform linux/amd64 -t <repository_url>/<image_name> .
    
    Copy
    • image_name には、 service_to_service:latest を使用します。

    docker build --rm --platform linux/amd64 -t myorg-myacct.registry.snowflakecomputing.com/tutorial_db/data_schema/tutorial_repository/service_to_service:latest .
    
    Copy
  3. Snowflakeアカウントのリポジトリにイメージをアップロードします。Dockerがあなたの代わりにリポジトリにイメージをアップロードするには、まず レジストリでDockerを認証する 必要があります。

    1. SnowflakeレジストリでDockerを認証するには、以下のコマンドを実行します。

      docker login <registry_hostname> -u <username>
      
      Copy
      • username には、Snowflakeのユーザー名を指定します。Dockerは、パスワードの入力を求めるプロンプトを表示します。

    2. イメージをアップロードするには、以下のコマンドを実行します。

      docker push <repository_url>/<image_name>
      
      Copy

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

3: 仕様ファイルをステージする

  • サービス仕様ファイル(service_to_service_spec.yaml)をステージにアップロードするには、以下のオプションのいずれかを使用します。

    このコマンドは OVERWRITE=TRUE を設定するため、必要な場合(例: 仕様ファイルのエラーを修正した場合)は、ファイルを再度アップロードすることができます。PUT コマンドが正常に実行されると、アップロードされたファイルに関する情報がプリントアウトされます。

4: ジョブサービスを実行する

これで、作成したSnowflakeジョブサービスをテストする準備が整いました。ジョブサービスが実行されると、Snowflakeは、コンテナ内のコードが標準出力または標準エラーに出力したものをログとして収集します。 SYSTEM$GET_SERVICE_LOGS システム関数を使用して、ログにアクセスできます。詳細については、 Snowpark Container Services: サービスのその他の考慮事項 をご参照ください。

  1. ジョブサービスを開始するには、 EXECUTE JOBSERVICE コマンドを実行します。

    EXECUTE JOB SERVICE
      IN COMPUTE POOL tutorial_compute_pool
      NAME=tutorial_db.data_schema.tutorial3_job_service
      FROM @tutorial_stage
      SPEC='service_to_service_spec.yaml';
    
    Copy

    次の点に注意してください。

    • FROM と SPEC は、ステージ名とサービス仕様ファイル名を提供します。

    • COMPUTE_POOL は、Snowflakeがジョブサービスを実行するコンピューティングリソースを提供します。

    Snowflakeは、仕様ファイルで特定されたコンテナーを実行します。コンテナーは SERVICE_URL 環境変数値(http://echo-service:8000/echo)を読み取り、 /echo HTTP パスのポート8000でEchoサービスにリクエストを送信します。

    Snowflakeはジョブサービスを開始し、以下の出力を返します。

    +------------------------------------------------------------------------+
    | status                                                                 |
    |------------------------------------------------------------------------|
    | Job TUTORIAL3_JOB_SERVICE completed successfully with status: DONE     |
    +------------------------------------------------------------------------+
    

    応答にはジョブサービス名が含まれることに注意してください。

  2. (オプション)ジョブサービスの完了後に、実行されたジョブサービスに関する詳細情報を取得できます。これはジョブサービスの失敗をデバッグするのに便利です。ジョブサービスのステータスを取得するには、 SYSTEM$GET_SERVICE_STATUS --- 非推奨 関数を呼び出します。

    CALL SYSTEM$GET_SERVICE_STATUS('TUTORIAL3_JOB_SERVICE');
    
    Copy

    サンプル出力:

    [
      {
        "status":"DONE",
        "message":"Container finished",
        "containerName":"main",
        "instanceId":"0",
        "serviceName":"TUTORIAL3_JOB_SERVICE",
        "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. ジョブサービスのログを読むには、 SYSTEM$GET_SERVICE_LOGS を呼び出します。

    CALL SYSTEM$GET_SERVICE_LOGS('tutorial_3_job_service', 0, 'main');
    
    Copy

    main は、ログを取得するコンテナーの名前です。このコンテナ名は、サービス仕様ファイルでコンテナに設定します。

    サンプルログ:

    +--------------------------------------------------------------------------------------------------------------------------+
    | 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: クリーンアップする

Snowflakeは、お客様のアカウントでアクティブなコンピューティングプールノードに対して課金します。(コンピューティングプールの操作 を参照。)不要な課金を防ぐには、まず、コンピューティングプールで現在実行中のサービスをすべて停止します。そして、コンピューティングプールを中断するか(後ほど再度使用する予定の場合)、ドロップします。

  1. コンピューティングプール上のすべてのサービスとジョブサービスを停止します。

    ALTER COMPUTE POOL tutorial_compute_pool STOP ALL;
    
    Copy
  2. コンピューティングプールを削除します。

    DROP COMPUTE POOL tutorial_compute_pool;
    
    Copy

イメージレジストリ(すべてのイメージを削除)と内部ステージ(仕様を削除)をクリーンアップすることもできます。

DROP IMAGE REPOSITORY tutorial_repository;
DROP STAGE tutorial_stage;
Copy

6: ジョブサービスコードを見直す

このセクションでは、以下のトピックを取り上げます。

提供されたファイルの調査

ダウンロードしたzipファイルには以下のファイルが含まれています。

  • service_to_service.py

  • Dockerfile

  • service_to_service_spec.yaml

このセクションでは、コードがジョブサービスを実装する方法の概要を説明します。

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

ジョブサービスの実行時:

  1. Snowflakeは、コンテナー内の SERVICE_URL 環境変数を設定するために、指定ファイルで提供された値を使用します。

  2. コードは環境変数を読み取ります。

    SERVICE_URL = os.getenv('SERVICE_URL', 'http://localhost:8080/echo').
    
    Copy
  3. call_service() 関数は、 SERVICE_URL を使用してEchoサービスと通信します。

Dockerfile

このファイルには、Dockerを使用してイメージを構築するためのすべてのコマンドが含まれています。

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

service_to_service_spec.yamlファイル(サービス仕様)

Snowflakeは、この仕様で提供された情報を使用して、サービスを構成および実行します。

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

この仕様は、ジョブの構成と実行用の情報をSnowflakeに提供します。Echoサービスと通信するために、このジョブには次が必要です。

  • リクエストを送信するEchoサービスの DNS 名前。

  • Echoサービスがリッスンしている HTTP ポート。

  • Echoサービスが予期しているリクエストを送信する HTTP パス。

この情報を得るには、

  1. Echoサービスの DNS 名(チュートリアル1)を取得するには、 DESCRIBE SERVICE SQL コマンドを実行します。

    DESCRIBE SERVICE echo_service;
    
    Copy

    Echoサービス用に得られる DNS名:

    echo-service.fsvv.svc.spcs.internal
    
    Copy

    このチュートリアルでは、Echoサービス(チュートリアル1)が作成されるのと同じデータベーススキーマ(data-schema)にジョブサービスを作成することに注意してください。したがって、前述の DNS 名の「echo-service」部分のみが SERVICE_URL の構成に必要です。

  2. Echoサービス仕様ファイル(チュートリアル1)から、Echoサービスがリッスンしているポート番号(8000)を取得します。 SHOW ENDPOINTS SQL コマンドを使用することもできます。

次に、先行する仕様ファイル(service_to_service_spec.yaml)を作成します。必須フィールドの containers.namecontainers.image に加えて、オプションの containers.env フィールドも含めて、サービスで使用する環境変数を指定します。

ローカルでのイメージの構築とテスト

Snowflakeアカウントのリポジトリにアップロードする前に、ローカルでDockerイメージをテストすることができます。ローカルテストでは、コンテナはスタンドアロンで実行されます(Snowflakeが実行するジョブサービスではありません)。

注釈

このチュートリアルで提供されるPythonコードは、 requests ライブラリを使用して別のSnowpark Containersサービスにリクエストを送信します。このライブラリがインストールされていない場合は、pipを実行します(例: pip3 install requests)。

以下のステップを使用して、チュートリアル3Dockerイメージをテストします。

  1. Echoサービスを実行状態にする必要があります(チュートリアル1)。チュートリアル1のEchoサービスを開始するには、ターミナルウィンドウで次のPythonコマンドを実行します。

    SERVER_PORT=8000 python3 echo_service.py
    
    Copy
  2. 別のターミナルウィンドウを開き、このチュートリアルで提供されるPythonコードを実行します。

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

    SERVICE_URL は環境変数であることに注意してください。ローカルテストでは、この変数を明示的に設定する必要があります。この URL は、Echoサービスを開始したときに明示的に指定したポートと HTTP パスに一致します。

    ジョブが実行されると、リクエスト本文に「Hello」文字列を含む POST リクエストをポート8000でリッスンしているEchoサービスに送信します。Echoサービスは入力をエコーして、「I said Hello」という応答を返します。

    サンプル応答:

    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
    

    ログをレビューして、サービス間通信が成功したことを確認します。

次の内容

チュートリアル4: ブロックストレージボリュームがマウントされたサービスを作成する