AWS/lambda

AWS lambda Container Image로 배포하기 (with python, tensorflow)

jangdu 2024. 5. 11. 00:39

python 코드를 text로 받아 파싱 후 실행한 결과를 response로 보내는 api를 만들어보며, AWS lambda에 docker로 말아서 올려보는 방법을 알아보겠습니다.

 

여기서 python 코드에는 tensorflow 코드를 실행하며, 라이브러리와 함께 container를 CLI로 배포하는 과정을 다룹니다.

 

람다 함수에서 python 컨테이너 이미지를 빌드해 배포하는 방법은 3가지가 있는데, 이 글에서는 Lambda 전용 컨테이너 이미지를 CLI를 통해 빌드 후 ECR로 이미지를 배포하는 방법에 대해서만 다루겠습니다.

 

aws에서는 python Container를 v3.8 ~ v3.12까지 지원합니다.
그 이후의 버전의 이미지는 AWS Minimal container image를 기반으로 사용합니다. 자세한 내용은 해당 링크를 통해서 살펴볼 수 있습니다.

 

저는 이번에는 v3.12를 사용해서 빌드하겠습니다.

본 글에서 AWS IAM 계정이나 권한, docker, AWS CLI는 이미 세팅이 되어있다는 기준으로 작성되었습니다.

필요한 파일 작성하기

배포를 위해 필요한 파일은 다음과 같습니다. (.gitignore는 무시하셔도 괜찮습니다.)

lambda_function.py 작성

lambda에서 실행 할 python 코드는 아래와 같이 작성했습니다.

import tensorflow as tf 
import json import io
import sys 

def handler(event, context): 
    old_stdout = sys.stdout 
    new_stdout = io.StringIO() 
    sys.stdout = new_stdout 
    code_text = event['data'] 
    exec(code_text, globals()) 

    sys.stdout = old_stdout
    printed_output = new_stdout.getvalue()

    # ensure_ascii=False를 추가하여 유니코드 문자를 그대로 유지하고, 결과를 UTF-8로 인코딩 
    utf8_output = json.dumps(printed_output, ensure_ascii=False).encode('utf-8') 

    return { 'statusCode': 200, 'data': utf8_output }

위 코드는 간단한 python 코드를 request로 받아서 실행 후, 그 결과를 response하는 코드입니다.
본 글에서 python 코드는 비교적 중요하지 않아 자세한 설명은 생략하겠습니다.

 

위 코드에서는 tensorflow를 import해서 tensorflow를 사용한 코드를 실행 할 수 있다는 부분만 보시면 됩니다.

requirements.txt 작성하기

requirements파일에는 python 이미지에서 사용 할 외부 라이브러리를 명시해주는 파일입니다.


해당 파일에 작성해둔 라이브러리를 빌드해서 람다로 배포하기위해 필요합니다.

boto3
numpy
Pillow
tensorflow

 

위 처럼 그냥 사용 할 라이브러리를 적어두시기만 하시면 됩니다.

Dockerfile 작성하기

FROM public.ecr.aws/lambda/python:3.12 

COPY requirements.txt ${LAMBDA_TASK_ROOT} 

RUN pip install -r requirements.txt 

COPY lambda_function.py ${LAMBDA_TASK_ROOT} 

CMD [ "lambda_function.handler" ]

 

${LAMBDA_TASK_ROOT}는 AWS Lambda 함수에서 사용되는 환경 변수 중 하나로, Lambda 함수의 루트 디렉토리를 나타냅니다.

 

Dockerfile도 위와 같이 간단하게 requirements.txtlambda_function.py를 루트 디렉토리에 복사해 실행해 이미지를 생성하도록 작성합니다.

docker build

docker build --platform linux/amd64 -t docker-image:test .

 

위 명령어를 통해서 도커 이미지를 빌드합니다. docker-image는 이미지의 이름이고, test로 태그를 지정해서 빌드합니다.

ECR 생성하기

이미지를 Lambda에 연결하기 위해서, 위 과정을 통해서 빌드한 이미지를 AWS ECR로 배포하는 과정은 다음과 같습니다.

 

우선 현재 cmd에 aws 계정 등록이 되어있지 않다면 다음 명령어로 등록을 진행합니다.

> aws configure

 

AWS 계정이 등록이 완료되면, aws ecr CLI를 사용해서 Docker CLI를 ECR 레지스트리에 인증합니다.

> aws ecr get-login-password --region <aws_region> | docker login --username AWS --password-stdin <aws계정_id>.dkr.ecr.<aws_region>.amazonaws.com

 

레지스트리에 인증 후, aws ecr create-repository 명령어를 사용하여 Amazon ECR에 레포지토리를 생성합니다.

ECR의 레포지토리는 반드시 Lambda 함수와 동일한 Region내에 생성해야만 합니다.

> aws ecr create-repository --repository-name <repo_name> --region <aws_region> --image-scanning-configuration scanOnPush=true --image-tag-mutability MUTABLE

 

레포지토리가 성공적으로 생성되면 다음과 같은 응답이 표시됩니다.

{
    "repository": {
        "repositoryArn": "arn:aws:ecr:us-east-1:111122223333:repository/hello-world",
        "registryId": "111122223333",
        "repositoryName": "hello-world",
        "repositoryUri": "111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world",
        "createdAt": "2023-03-09T10:39:01+00:00",
        "imageTagMutability": "MUTABLE",
        "imageScanningConfiguration": {
            "scanOnPush": true
        },
        "encryptionConfiguration": {
            "encryptionType": "AES256"
        }
    }
}

 

위 응답에서 repositoryUri를 미리 복사합니다.

생성된 ECR에 배포하기

위에서 생성한 로컬의 도커 이미지를 ECR 리포지토리에 배포하기 위해, 다음 docker tag명령어를 사용해 최신 버전으로 태그를 바꿔줍니다.

repositoryUri은 ECR 레포지토리를 생성할 때의 응답에서 복사해 사용하고, url의 뒤에 latest를 적어 최신 버전을 명시합니다.

> docker tag docker-image:test <ECRrepositoryUri>:latest

 

태그를 완료한 뒤 docker push 명령을 실행하여 Amazon ECR 레포지토리에 이미지를 배포합니다. 위에서 바꾼 태그를 포함해서 명령어를 실행시켜줍니다.

> docker push <ECRrepositoryUri>:latest 

배포된 ECR 이미지로 람다 생성하기

아래의 명령어로 배포된 ECR 레포지토리 이미지를 사용해서 Lambda함수를 생성해줍니다.
CLI를 통한 함수를 생성시, Role을 포함시켜 명령어를 실행해야합니다.

> aws lambda create-function \ 
--function-name <function_name> \ 
--package-type Image \ 
--code ImageUri=<ECRrepositoryUri>:latest \ 
--role <role_arn> \ 
--memory-size 2048 \ 
--timeout 60

빌드된 도커 이미지가 커지면, 기본 시간이나 메모리가 부족할 수 있으므로, --memory-size--timeout 60옵션을 포함해서 생성해야합니다.그렇지 않으면 메모리나 시간이 부족해 함수가 실행되지 않을 수 있습니다.

 

이제 AWS Console에서 생성한 리전의 lambda와 ECR에서 배포된 함수나 이미지를 확인해 볼 수 있습니다.

생성된 람다 함수를 실행해 테스트하기

테스트용 함수를 CLI로 실행하기 위해서 request에 필요한 input.json파일을 생성합니다.


다음 json에서는 이미지가 잘 빌드되어 배포됐는지를 확인하기 위해, 간단한 tensorflow를 실행해보는 코드를 텍스트로 전달하기위한 데이터를 작성합니다.

{ 
  "data": "tensor = tf.constant([[8, 4], [3, 4]])\nresult = tf.math.add(tensor, tensor)"
}

 

다음 명령어를 사용해서 실제 lambda 함수에 api를 보내, 테스트를 진행합니다. function_name은 위에서 생성한 함수의 이름입니다.

> aws lambda invoke --function-name <function_name> --payload fileb://input.json response.json

 

위 명령어를 통해서 실제 생성된 람다 함수에 요청을 보내며, 성공적으로 실행된 응답은 response.json으로 저장됩니다.

제대로 배포가 되었다면 다음과 같은 결과를 볼 수 있습니다.

{"statusCode": 200, "data": "\"TensorFlow operation result:\\n[[16 8]\\n [6 8]]\\n\""}

수정하기

이미 배포된 람다 및 ECR이미지를 수정 할 수 있습니다.
아래 순서로 진행하면 되며, tag를 사용해서 버전을 관리하는 것도 가능합니다.

 

수정된 코드나 파일을 다시 도커로 빌드합니다.

> docker build --platform linux/amd64 -t docker-image:test .

 

빌드된 이미지를 기존에 배포된 ECR uri를 명령어에 포함해 tag를 수정하고, 수정된 태그를 추가 배포합니다.

> docker tag docker-image:test <ECRrepositoryUri>:latest 
> docker push <ECRrepositoryUri>

 

수정된 이미지를 배포한 뒤, 람다 함수에 배포된 이미지로 교체 연결해주면 수정이 완료됩니다.

> aws lambda update-function-code \ 
--function-name <function_name> \ 
--image-uri <ECRrepositoryUri>:latest

 

새로 수정 후 빌드 및 배포된 람다 함수의 api나 실행 명령어를 통해서 실행, 테스트 해보세요.