자습서 3: 서비스 간 통신

중요

Snowpark Container Services 작업 기능은 현재 비공개 미리 보기로 제공되며 https://snowflake.com/legal 의 미리 보기 조건이 적용됩니다. 자세한 내용은 Snowflake 담당자에게 문의하십시오.

소개

이 자습서에서는 자습서 1 에서 만든 Echo 서비스와 통신하는 Snowpark Container Services 작업을 만듭니다. 작업이 실행되면 요청 본문에 《Hello》 문자열이 포함된 POST 요청을 (작업 사양에서 제공한) Echo 서비스 URL로 보냅니다. Echo 서비스는 응답 본문에 《Bob said Hello》 문자열이 포함된 응답을 반환합니다. 작업 컨테이너 로그에 액세스하여 통신이 성공했는지 확인합니다.

이 자습서는 다음 두 부분으로 구성됩니다.

  • 파트 1: 작업을 만들고 테스트합니다. 이 자습서에 제공된 코드를 다운로드하고 단계별 지침을 따릅니다.

    1. 이 자습서를 위한 작업 코드를 다운로드합니다.

    2. Snowpark Container Services용 Docker 이미지를 만들어 자기 계정의 리포지토리에 업로드합니다.

    3. Snowflake에 컨테이너 구성 정보를 제공하는 사양 파일을 스테이징합니다. 컨테이너를 시작하는 데 사용할 이미지 이름 외에도 사양에서는 환경 변수(SERVICE_URL)를 Echo 서비스 URL로 설정합니다. 애플리케이션 코드는 이 환경 변수를 읽어 Echo 서비스에 요청을 보냅니다.

    4. 작업을 실행합니다. EXECUTE SERVICE 명령을 사용하면 Snowflake가 컨테이너를 실행할 수 있는 사양 파일과 컴퓨팅 풀을 제공하여 작업을 실행할 수 있습니다. 마지막으로, 작업 컨테이너의 로그에 액세스하여 작업과 서비스 간의 통신이 성공했는지 확인합니다.

  • 파트 2: 작업 코드를 이해합니다. 이 섹션에서는 서비스 코드의 개요를 제공하고 다양한 구성 요소가 어떻게 협력하는지 강조합니다.

전제 조건

자습서 1 을 완료하고 Echo 서비스가 실행 중인지 확인하십시오.

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

1: 서비스 코드 다운로드

작업을 생성하기 위한 코드(Python 애플리케이션)가 제공됩니다.

  1. zip 파일 을 디렉토리에 다운로드합니다.

  2. 이 파일의 압축을 풀면 각 자습서마다 하나의 디렉터리가 포함된 것을 알 수 있습니다. 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가 사용자를 대신하여 리포지토리에 이미지를 업로드하려면 먼저 Snowflake로 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_JOB_LOGS 시스템 함수를 사용하여 로그에 액세스할 수 있습니다. 자세한 내용은 Snowpark Container Services: 서비스 및 작업의 추가 고려 사항 섹션을 참조하십시오.

  1. 작업을 시작하려면 EXECUTE SERVICE 명령을 실행하십시오.

    EXECUTE SERVICE
      IN COMPUTE POOL tutorial_compute_pool
      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는 작업을 시작하고 출력에 작업 ID를 반환합니다.

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

    응답에는 Snowflake에서 할당한 쿼리 작업 ID가 포함됩니다.

  2. (선택 사항) 작업이 완료된 후 실행된 작업에 대한 추가 정보를 얻을 수 있습니다. 이는 작업 실패를 디버깅하는 데 유용합니다.

    1. 선택 사항: 나중에 참조할 수 있도록 세션 변수에서 이전 EXECUTE SERVICE 명령의 작업 ID을 캡처하려면 다음 명령을 실행하십시오.

      SET job_id = last_query_id();
      
      Copy

      작업 ID는 생성되는 즉시 캡처해야 합니다. 그렇지 않으면 잘못된 값을 캡처하게 됩니다.

    2. 작업 상태를 가져오려면 다음 옵션 중 하나를 사용하여 SYSTEM$GET_JOB_STATUS 함수를 호출하십시오.

      • EXECUTE SERVICE에서 반환된 작업 ID 사용:

        CALL SYSTEM$GET_JOB_STATUS('<job_id returned by EXECUTE SERVICE>');
        
        Copy
      • 세션 변수 사용:

        CALL SYSTEM$GET_JOB_STATUS($job_id);
        
        Copy

      샘플 출력:

      [
        {
          "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. 작업 로그를 읽으려면 다음 옵션 중 하나를 사용하십시오.

    • EXECUTE SERVICE에서 반환된 쿼리 작업 ID를 사용합니다.

      CALL SYSTEM$GET_JOB_LOGS('<job_id returned by EXECUTE SERVICE>', 'main');
      
      Copy
    • 세션 변수를 사용합니다.

      CALL SYSTEM$GET_JOB_LOGS($job_id, '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.data-schema.tutorial-db.snowflakecomputing.internal
    
    Copy

    이 자습서에서는 Echo 서비스(자습서 1)가 생성된 것과 동일한 데이터베이스 스키마(data-schema)에서 작업을 생성합니다. 따라서 SERVICE_URL 을 생성하려면 앞의 DNS 이름 중 《echo-service》 부분만 필요합니다.

  2. Echo 서비스 사양 파일(자습서 1)에서 Echo 서비스가 수신 대기 중인 포트 번호(8000)를 가져옵니다.

그런 다음 이전 사양 파일(service_to_service_spec.yaml)을 생성합니다. 필수적인 containers.namecontainers.image 필드 외에, 선택적인 containers.env 필드도 포함하십시오.

로컬에서 이미지 만들기 및 테스트하기

Docker 이미지를 Snowflake 계정의 리포지토리에 업로드하기 전에 로컬에서 테스트할 수 있습니다. 로컬 테스트에서는 컨테이너가 독립형으로 실행됩니다(Snowflake가 실행하는 작업이 아님).

참고

이 자습서를 위해 제공되는 Python 코드는 requests 라이브러리를 사용하여 다른 Snowpark Containers 서비스에 요청을 보냅니다. 이 라이브러리가 설치되어 있지 않으면 pip를 실행하십시오(예: pip3 install requests).

자습서 3 Docker 이미지를 테스트하려면 다음 단계를 따르십시오.

  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》 문자열을 포함하여 포트 8080에서 수신 대기 중인 Echo 서비스에 POST 요청이 전송됩니다. 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
    

    로그를 검토하여 서비스 간 통신이 성공했는지 확인하십시오.

다음에는 무엇을 해야 합니까?

이제 이 자습서를 마쳤으므로 고급 자습서 로 돌아가서 다른 주제를 살펴볼 수 있습니다.