본문 바로가기

MLOps

AWS Serverless 2편

1편에서 생성한 Lambda의 스펙을 다시 한번 살펴보고 이번에는 구체적으로 해석해보자.

  ApiFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName:
        Fn::Sub: ${StageName}-mlops-api-function
      Handler: lambda_invoke.lambda_handler
      Runtime: python3.7
      CodeUri: s3://your-bucket/serverless/code.zip
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          STAGE_NAME:
            Ref: StageName

 

애플리케이션의 기능을 구현한 코드는 "your-bucket"이라는 이름의 s3 버킷에 "serverless/code.zip"이라는 zip 파일로 저장되어있다. 이 zip 파일을 열어보면 lambda_invoke.py라는 파일이 있고 그 안에 lambda_handler라는 함수에 애플리케이션의 핵심 기능이 구현되었다. 본 Lambda를 호출할 때마다 이 함수가 실행되는 것이다. 템플릿에서 선언한 환경 변수 LOG_LEVEL과 STAGE_NAME은 os 라이브러리를 이용하여 접근할 수 있다.

lambda_handler에 SageMaker 엔드포인트로부터 머신러닝 모델 추론 결과를 받아서 응답으로 내보내는 로직을 구현해보자. "SM_ENDPOINT_TEST"라는 이름의 SageMaker 엔드포인트가 미리 배포되었다고 가정하고 해당 엔드포인트에 학습된 "resnet"과 "vggnet"이라는 모델들이 서빙 중이라고 가정하자. SageMaker 엔드포인트와 통신하기 위하여 boto3 sagemaker-runtime client가 필요하다.

 

import boto3
import random
import json
import os
import logging

LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO").upper()
STAGE_NAME = os.environ["STAGE_NAME"]

logger = logging.getLogger()
logger.setLevel(LOG_LEVEL)
sm_runtime = boto3.client("sagemaker-runtime")


def lambda_handler(event, context):
    path = event["path"]
    data = event["body"]
    method = event["httpMethod"]
    content_type = "application/json"
    
    if path == "/api/v1/resnet":
    	variant_name = "resnet"
    elif path == "/api/v1/vggnet":
    	variant_name = "vggnet"
    
    response = sm_runtime.invoke_endpoint(
    	EndpointName="SM_ENDPOINT_TEST",
        ContentType=content_type,
        TargetVariant=variant_name,
        Body=data
    )
    
    invoked_variant = response["InvokedProductionVariant"]
    predictions = json.loads(response["Body"].read())["predictions"]
    
    result = {
    	"invoked_variant": invoked_variant,
        "predictions": predictions
    }
    status_code = 200
    
    return {"statusCode": status_code, "body": json.dumps(result)}

 

Lambda로 실행될 함수는 event와 context라는 인자를 받는다. event 인자에는 요청에 관한 각종 정보가 들어있다.

  • event["path"] : 클라이언트가 요청을 보낸 url 경로로 {HOST-NAME}/dev 뒷 부분. String 타입.
  • event["body"] : 요청 바디. JSON 타입.
  • event["httpMethod"] : Http 메서드 타입. String 타입. ex."GET", "POST", "PUT", ...

편의상 배포 stage를 dev로 가정했다. 요청 경로가 "{HOST-NAME}/dev/api/v1/resnet"인 경우 "resnet", "{HOST-NAME}/dev/api/v1/vggnet"인 경우 "vggnet" 모델로 추론 요청을 보내도록 했다. 돌아오는 response는 파이썬 딕셔너리 타입으로 추론에 사용된 모델/variant의 이름과 추론 결과 값을 갖고 있다. 예시 코드에서 사용된 variant의 이름을 invoked_variant, 추론 결과값을 predictions라는 변수에 담았다.

마지막으로 lambda_handler는 다음 형식의 딕셔너리를 반환해야한다.

{

    "statusCode" : Http 상태 코드. Int 타입,

    "body" : 응답 바디. JSON 타입.

}

'MLOps' 카테고리의 다른 글

Triton Python Backend 사용하기  (1) 2023.05.14
NVIDIA Triton 한 눈에 알아보기  (0) 2023.01.09
AWS Serverless 1편  (0) 2023.01.02
Amazon SageMaker  (0) 2022.04.25
Multi-Armed Bandit with Seldon Core  (0) 2022.02.20