AWS Lambda로 서버리스 환경 구축 및 배치 프로그램을 통해서 한번에 배포하기 - (1)

전현준·2024년 6월 9일
0

AWS Lambda 구축기

목록 보기
1/2
post-thumbnail

🤔 도입부


필자는 졸업 작품으로 프로젝트를 하고 있었습니다.

사이드 프로젝트든, 졸업 작품이든, 개발할 때 프론트측에서 언제든지 테스트도 하고,

빠르게 개발하기 위해서는 개발 서버가 준비되어 있어야 테스트하기 편합니다.

두달 EC2를 쓰고 10만원을 낸 나..

[Termux] 안드로이드 폰에 MySQL을 설치해보자. [1편]

그래서 이전에 안드로이드 폰을 이용해서 구축하는 방법으로 개발용 서버를 돌리곤 했죠.


근데 여기서 문제가 생깁니다. 😭

리눅스 환경이긴 하나, 생각했던 OS와는 달랐습니다.

도커도 설치 안되고. Tensorflow도 설치 안됐습니다. 아니 대체 되는건 뭔데

그래도 머신러닝은 테스트 시에만 대면으로 하고, 그 외는 MySQL + Java 조합으로 서버로 사용하고 있었죠.

하지만 프로젝트 막바지가 다가오고, 머신러닝 분야까지 배포해야하는 상황이 생깁니다.

또 EC2에 Tensorflow + Python 써야 하나? 고민하던 찰나,
서버리스 Lambda쓰면 비용 좀 줄일 수 있지 않나? 생각이 들었습니다.

그래 해보자!



AWS Lambda Function


아주 쉽게 이야기 하면, 서버를 열기 위해서는 컴퓨터가 필요합니다.

클라우드는 컴퓨터를 빌려주는 시스템입니다.

아주 쉽게 생각하면 EC2도 이런 개념이죠.
언제든지 테스트하라면 서버는 항상 켜져있어야 합니다.

테스트 할 때마다 켜면 비용을 아낄 수 있지만 할 때마다 연락해야하죠.


하지만, Lambda를 쓰면 그럴 필요가 없어집니다.

내가 쓰는 만큼만 비용을 청구하기 때문이죠.

상시 테스트 가능하게 했을 때, 사용한 만큼만 지불하면 좋으니, Lambda가 더 이득이죠.


오늘 할 일


그래서 오늘 할 업무는 다음과 같습니다.

TODO
1. 기존의 머신러닝 (Tensorflow) Window에서 실행 하던 코드를   Ubuntu 환경(EC2)에서 테스트
  ➜ 테스트 완료
2. 테스트가 잘 수행 되면 Lambda 함수로 배포.
3. GitHub Action으로 Lambda 함수 배포 자동화
4. Lambda 함수에 HTTP 요청 테스트

아키텍처 상으로는 다음과 같습니다.

일단 오늘은 AWS 콘솔(페이지)에서 Lambda 함수 다루는 방법을 알아보려 합니다.


AWS Lambda 함수


사용 언어 : Python 3.11

AWS Lambda 홈페이지에 들어가 함수 생성을 눌러줍니다.

저는 일단 Python 3.11로 진행했습니다.

Python 3.12
3.12가 최신 버전이지만, 3.12부터 OS 버전이 변경되었습니다.
그래서 OS가 달라짐에 따라서, 여러가지도 변경된게 있겠지만, 패키지 매니저도 달라졌더라구요.

  • Python 3.11 : yum install -y mesa-libGL
  • Python 3.12 : dnf install -y mesa-libGL


    변경 된 부분이 많을 것 같아, 저는 일단 Python 3.11로 사용했습니다.

Lambda 문법

REST API의 용도로 사용할 코드를 살펴봅시다. 처음 함수를 만들고 나면 이런 코드가 반겨줍니다.

return을 하면 Json의 형태로 전달하는 코드입니다. 흔히 생각하는 HTTP 통신을 생각하시면 됩니다.

그러면 응답을 봤으니, 요청을 처리하는 코드도 봐야겠죠?

우리가 요청 한 내용이 event 객체에 담기게 됩니다.

우리가 코드를 변경하면, Deploy 버튼을 눌러 배포를 진행해야합니다.

Deploy를 하고 나면 상단에 이 메세지가 뜨는지 확인합니다.

테스트 탭에서 테스트를 할 수 있습니다.

아래의 내용으로 테스트를 해보겠습니다.

그러면 성공한 로그를 볼 수 있는데, 우리가 작성했던 코드대로 잘 실행 된 것을 알 수 있었습니다.

Lambda 설정

  • Lambda 일반 구성
    Lambda는 기본이 3초입니다. 최대 10분까지 설정 할 수 있습니다.
    혹시 테스트에 실패한다면, 제한 시간을 늘려보세요.
    또 메모리 크기마다 CPU 코어 수가 달라집니다. 적절히 조절해보세요.

    Lambda 기본 세팅
    - 메모리 : 128MB
    - 임시 스토리지 : 512 MB
    - 제한 시간 : 3초

  • 권한
    역할 이름을 클릭해보세요. 권한을 부여할 수 있습니다.

    저는 S3 저장소를 사용하고, CloudWatch 로그를 남기기 위해 두개를 추가했습니다.

  • 환경 변수
    설정 > 환경 변수에서 설정 할 수 있습니다. 추후 값을 불러올 때는 코드에서 os.getenv로 사용하면 됩니다.


[오류] Memory less than 3008MB

Lambda 설정 시, 메모리 크기가 3008MB 이상 설정되지 않는 오류

StackOverflow에서 답변을 얻을 수 있었습니다.

참고 : https://stackoverflow.com/questions/70943739/aws-lambda-memorysize-value-failed-to-satisfy-constraint

그래서 저도 다른 리전에서 생성을 시도했지만, 번번히 실패했습니다. 현재 프리티어 계정이라서 이런 문제가 발생한다는 답변도 있어, 기존에 생성한지 1년이 넘은 계정은 오류가 발생하지 않았습니다.

생성한지 오래된 계정으로 시도해보세요.


AWS S3 저장소


머신러닝 코드를 실행하기 위해서는 모델이라는 파일이 필요합니다.

그러면 파일을 저장할 저장소가 필요한데, 저는 Google Drive와 GCP를 사용하고 있었습니다.

하지만, 1GB가 넘어가는 모델을 다운받는데 외부 접속을 느릴 수 있기 때문에, S3에 모델을 저장해 두고,

호출 할 때마다 파일을 불러오는 코드를 작성해보겠습니다.

Region은 Lambda를 생성한 곳과 동일한 곳으로 해서 S3 버킷을 만들어주세요.

세팅은 그대로 두시고, 생성하면 됩니다.


버킷 이름은 중복되면 안되니, 여기서라도 레어닉을..

가장 주의 할 점이, 퍼블릭 액세스 인데 어짜피 Lambda에서만 사용한다면 퍼블릭 액세스 차단으로 생성하시는 것을 권장드립니다.

혹여나, 누군가, 내 S3를 무자비하게 사용한다면, 비용이 매우 많이 청구되겠죠?

Lambda에서 사용할 파일과 폴더를 업로드 해둡니다.
저는 모델 폴더 4개, 인증 관련 json 2개를 업로드 해놨습니다.

이제 Lambda에서 S3에 있는 파일을 다운 받는 코드를 작성하면 됩니다.

[예제] Lambda에서 S3 저장소에 있는 파일 다운 받기

간단한 예제 코드이다. S3 하나의 파일을 다운받는 코드이다.

lambda 함수는 파일을 다운 받으려면 /tmp 디렉토리에 무조건 위치해야 한다.

그래서 다운 받는 /tmp/Models 디렉토리를 생성하고, 거기에 파일을 다운로드 받았다.

import os.path
import boto3

def lambda_handler(event, context):
	bucket_name = "bucket_name"
    
    # S3 클라이언트 생성
    s3 = boto3.client('s3')
    
    # Model들 저장할 폴더 생성
    download_path = '/tmp/Models/'
    os.makedirs(download_path, exist_ok=True)
    
    ## credentials 다운로드
    s3.download_file(bucket_name, 'credentials_service.json', '/tmp/Models/credentials_service.json')
    

[오류] 403 에러 발생

혹시라도, 403 오류가 발생한다면 이 글 위에 작성해둔 Lambda 설정에 권한을 살펴보자. S3 권한이 필요하다.

  • 권한
    역할 이름을 클릭해보세요. 권한을 부여할 수 있습니다.

    저는 S3 저장소를 사용하고, CloudWatch 로그를 남기기 위해 두개를 추가했습니다.

[오류] FileNotFoundError

파일 다운 시, 파일명 뒤에 이상한 문자 붙음

해쉬 문자열 같이, 파일명에 계속 붙는다면, 저장할 위치를 잘 확인 하세요.

저장할 위치를 꼭 디렉토리를 생성해주어야 하고, 오타가 없는지 확인해야합니다!



도커 이미지 패키징 하기


이젠 머신러닝 코드를 실행 시킬 때입니다.

근데 하다보니, 문제가 더 발생합니다. 우리 라이브러리 설치할 때 pip 쓰시죠?

근데 Lambda는 터미널이 없어 pip를 못씁니다. 🧐 아니;; 그럼 못하는거 아님?

방법이 있습니다! 도커 이미지로 만들어서 배포하면 됩니다.

하지만, 우리가 코드를 수정할 때마다 배포하면 굉장히 귀찮은 작업이기 때문에, 자동화 프로세스를 구축하려합니다.

일단 설치할 라이브러리를 준비합니다. 명령 프롬포트를 열고 pip freeze를 써보면 내 컴퓨터에 설치되어 있는 pip 라이브러리를 확인 할 수 있습니다.

아래 명령어를 작성하면 txt 파일로 저장해줍니다.

하지만, 내 컴퓨터에 있는 모든 라이브러리를 설치할 필요는 없겠죠? 시간 단축을 위해
필요한 라이브러리만 requirements.txt로 저장해둡니다.

저는 Google Drive와 Tenserflow, OpenCV를 사용할 예정이라 위와 같이 적어두었습니다.

특정 버전이 필요하다면 특정 버전을 명시해주면 됩니다.

저는 Tensorflow 2.15.0 버전이 필요해, Tensorflow 2.15.0 버전이 Python 3.11에서만 지원되기 때문에 Python 3.11을 작성해두었습니다.

Dockerfile 작성

Dockerfile을 작성해봅시다.

배포에 필요한 파일들은 COPY 명령어를 사용하고, 마지막엔 CMD ["app.lambda_handler"]
로 작성해주시면 됩니다. 저는 app.py 파일에 lambda_handler함수가 있어서 이렇게 선언했습니다.

각자 lambda_handler가 선언되어 있는 파일명을 적어주시면 됩니다.

# Pull the base image with python 3.11 as a runtime for your Lambda
# Tensorflow 2.15를 쓰기 위한 Python 3.11
FROM public.ecr.aws/lambda/python:3.11

# Copy the earlier created requirements.txt file to the container
COPY requirements.txt ./

RUN yum update -y
RUN yum install -y mesa-libGL

# Install the python requirements from requirements.txt
RUN python3 -m pip config set global.break-system-packages true
RUN python3.11 -m pip install -r requirements.txt

# Copy the earlier created app.py file to the container
COPY app.py ./
COPY cnn_model.py ./
COPY module_import_example.py ./

# Set the CMD to your handler
CMD ["app.lambda_handler"]

이제 하나의 이미지로 배포할 수 있습니다.

하지만, 여기서도 다양한 오류가 있었습니다.

[오류] pip install 오류

가상 환경에서 pip를 하라는 오류이다.

error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
python3-xyz, where xyz is the package you are trying to
install.

If you wish to install a non-Debian-packaged Python package,
create a virtual environment using python3 -m venv path/to/venv.
Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
sure you have python3-full installed.

If you wish to install a non-Debian packaged Python application,
it may be easiest to use pipx install xyz, which will manage a
virtual environment for you. Make sure you have pipx installed.

See /usr/share/doc/python3.11/README.venv for more information.

note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.

아래 코드를 실행시켜 주면 해결 된다.

python3 -m pip config set global.break-system-packages true

[오류] opencv-python 설치 후 오류

이미지를 imread하는 과정에서 발생한 오류다.

return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ImportError: libGL.so.1: cannot open shared object file: No such file or directory

우분투에서는 sudo apt-get install ffmpeg libsm6 libxext6 -y를 설치하면 된다던데,

Lambda 환경에서는 yum install -y mesa-libGLpip install ffmpeg로 해결했다.



Window에서 .bat파일로 배포 하기


이제 배포해야하는데, 아직 수작업으로 Lambda함수를 생성해 주어야 합니다.

그래서 AWS에서는 CLI환경에서 AWS의 다양한 서비스를 관리 할 수 있는 명령어를 만들었습니다.

https://aws.amazon.com/ko/cli/

위 링크로 들어가 AWS CLI를 설치해줍니다.

각자 맞는 환경의 CLI를 설치해줍니다. 저는 Window 환경입니다.

환경

  • OS : Window 11
  • AWS CLI : 2.16.2
  • Lambda : Python 3.11

아래 명령어로 잘 설치 되었는지 확인합니다.

aws --version

잘 되었다면, AWS 계정 설정이 필요합니다.



AWS IAM에 접속합니다.

  • 대시보드 > Quick Links > 내 보안 자격 증명을 클릭합니다.

  • 액세스 키 만들기를 클릭합니다.

  • 엑세스 키와 비밀 액세스키를 복사 해둡니다.

  • CMD를 열어 aws configue를 실행합니다. 여기에 방금 복사한 값들을 넣습니다.

region은 Lambda와 S3가 생성되어 있는 곳을 작성합니다. 저는 서울입니다.

AWS 콘솔에서 각 Region을 확인 할 수 있습니다.

이제 AWS CLI 명령어를 작성해서, 로컬에서 Lambda를 배포할 수 있습니다.

일단 Docker 이미지를 AWS에 업로드 해야합니다.

AWS ECR은 도커 컨테이너를 업로드 할 수 있는 공간입니다. 여기에 도커 이미지를 업로드 할 것입니다.



<전체 코드는 아래 스크롤하면 있어요!>

bat 파일 설명 1

일단 총 8개로 선언했는데, 위 4개는 아래 명령어에서 필요한 변수들이고,
아래 4개는 개인적으로 Lambda에 필요한 환경변수입니다. 개인적으로 각자 세팅할 값들을 지정합시다

계정 ID는 AWS 웹페이지 상단에서 확인 가능합니다~ 12자리 숫자로 구성되어있어요.


S3 버킷 이름은, 아까 생성했던 이름을 넣어줍시자. 필자는 에스파일 뻔... 수수수수퍼노바



bat 파일 설명 2

Rem은 주석이고, echo는 CMD에 출력할 내용입니다. 실제로 실행할 명령어는 작성하면 됩니다.

aws ecr ...어쩌고 들이 우리가 실행할 명령어입니다.

중요한 명령어들은 아니므로, 넘어가겠습니다.

https://awscli.amazonaws.com/v2/documentation/api/latest/index.html

자세히 궁금하다면, 공식 문서를 확인합시다!



bat 파일 설명 3

여기도 그대로 따라하면 된다. Docker이미지를 ECR에 배포하는 그런 명령어 입니다.

배포하고 나서 ECR에 들어가면, 이미지가 생성된 것을 볼 수 있었습니다.



bat 파일 설명 4

마지막이 Lambda 함수를 생성하는 중요한 코드입니다.

공식 문서 예제를 참고했습니다.
https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/create-function.html

Lambda 함수를 생성하는 명령어 입니다.

  1. --function-name : Lambda 함수 이름을 지정해주면 됩니다. 예) --function-name aespa

  2. ECR 이미지 링크 : %ACCOUNT_ID% / %REGION% / %REPOSITORY_NAME%
    상단에 set으로 작성한 변수들의 값이 할당됨.

  3. 역할 지정
    AWS IAM에 접속합니다.

기존에 생성했던 Lambda 역할이 있다면 하나 선택해주세요.

권한을 잘 부여합니다.

이 역할 이름을 역할 이름 부분에 넣어주세요.

  1. 환경 변수
    환경 변수를 Key-Value 값으로 넣어주세요
    --environment Variables={bucket_name=aespa,model_0=abc.keras}
    ❗이때 ,(comma) 사이 띄어쓰기 없이 넣어주세요.

  2. 메모리 공간, 저장 공간, 제한 시간
    저는 메모리 : 3008MB / 제한 시간 : 10분 / 임시 저장 공간(/tmp) : 5000MB로 설정했습니다.



전체 코드

@echo off
setlocal enabledelayedexpansion

set REPOSITORY_NAME=<ECR 이름>
set REGION=ap-northeast-2
set ACCOUNT_ID=<12자리>
set bucket_name=<S3 이름>
set model_0=<자체 환경 변수>

echo Building Docker image...
docker build -t %REPOSITORY_NAME% .

echo Fetching image digests...
for /f "tokens=*" %%i in ('aws ecr list-images --repository-name %REPOSITORY_NAME% --region %REGION% --query "imageIds[*].imageDigest" --output text') do (
    set IMAGE_DIGEST=%%i
    echo Deleting image digest: !IMAGE_DIGEST!
    aws ecr batch-delete-image --repository-name %REPOSITORY_NAME% --region %REGION% --image-ids imageDigest=!IMAGE_DIGEST!
)

echo Deleting ECR repository...
aws ecr delete-repository --repository-name %REPOSITORY_NAME% --region %REGION% --force

echo Creating ECR repository...
aws ecr create-repository --repository-name %REPOSITORY_NAME% --image-scanning-configuration scanOnPush=true --region %REGION%

echo Tagging Docker image...
docker tag %REPOSITORY_NAME%:latest %ACCOUNT_ID%.dkr.ecr.%REGION%.amazonaws.com/%REPOSITORY_NAME%:latest

echo Logging in to ECR...
aws ecr get-login-password --region %REGION% | docker login --username AWS --password-stdin %ACCOUNT_ID%.dkr.ecr.%REGION%.amazonaws.com

echo Pushing Docker image to ECR...
docker push %ACCOUNT_ID%.dkr.ecr.%REGION%.amazonaws.com/%REPOSITORY_NAME%:latest

echo Delete perst1 lambda
aws lambda delete-function --function-name perst1

echo Create perst1 lambda
aws lambda create-function --function-name perst1 --package-type Image --code ImageUri=%ACCOUNT_ID%.dkr.ecr.%REGION%.amazonaws.com/%REPOSITORY_NAME%:latest --role arn:aws:iam::%ACCOUNT_ID%:role/service-role/<역할 이름> --environment Variables={bucket_name=%bucket_name%,model_0=%model_0%} --memory-size 3008 --timeout 600 --ephemeral-storage Size=5000

echo Done.
pause


실행

.bat 파일을 열어 실행하면 CMD가 나오면서 알아서 실행 된다.



다음 편


다음 편은 Github Action을 이용해서, Github로 Push하면 자동으로 배포하는 프로세스를 배워보겠습니다.

또한, API를 완성해서 통신 테스트까지 마무리 하겠습니다 ฅ^•ﻌ•^ฅ

profile
백엔드 개발자 전현준입니다.

0개의 댓글

관련 채용 정보