AWS/Serverless Lambda

[AWS Lambda] Python Serverless 서비스 (FastAPI + Mangum)

yubi5050 2023. 6. 6. 19:56

개요

AWS Lambda를 이용해 Python 기반의 Serverless 서비스를 개발 및 운영할 때, 주로 쓰이는 것으로 보이는 여러 방법(조합)이 있는데,

 

주로 많이 사용되는 방법은 다음과 같습니다.

  • FastAPI + Magnum
  • Chalice 
  • Flask + zappa

 

해당 글에서는 FastAPI + Magnum에 서비스 구조에 대해 이해한 바를 작성하며, 다음 내용을 주로 작성해 보려고 합니다.

  • FastAPI + Magnum 해당 방법의 특징
  • Magnum에 대한 이해
  • 구현 방법에 대한 개괄적인 설명

 

Serverless FaaS 서비스인 AWS Lambda 에 대한 이해는 이전글을 참고

 

Fastapi + mangum 사전 조사 정보

FastAPI가 등장한 이후로, FastAPI를 Serverless로 구현해 보려는 시도가 생기면서, 국내 블로그 글이나 스택오버플로우에도 정보가 종종 보이는 것 같으며, 단순 구현에 대한 시도를 넘어, production에 적용해보 더라도 괜찮을 것 같다는 생각을 가졌습니다.

 

관련 사례 블로그/링크

 

Mangum 이란?

여기서 mangum이 등장하는데, 공식문서에서는 한줄로는 다음과 같이 설명합니다.

ASGI Application와 AWS 서비스(Lambda / API Gateway / ALB / Lambda Edge Events) 간의 어댑터 역할 수행

 

주요 기능

  • API Gateway HTTP or REST API / ALB / Function URLs / Cloud Front 등의 서비스에 대한 이벤트 핸들러 역할
  • Starlette, FastAPI, Quart, Django 프레임워크 들과의 호환성
  • payload 압축이나, 이진 미디어에 대한 압축 가능 (GZip, Brotli 활용)

 

만약 mangum을 가볍게 이해하고 넘어 가고 싶다면,

일반적으로 Python Framework를 배포할 때 Gunicorn, Uvicorn 등 을 사용해서 WAS에 배포하는 것처럼
Lambda 서비스에서도, WAS(Web Application Server)를 구성하는데 있어, mangum을 같이 사용한다.

 

정도로 생각 하고 넘어 갈 수 있을 것 같습니다.

 

Mangum 이 FastAPI에 필요한 이유

사실 그렇구나.. 하고 넘어가려다가 다음과 같은 의문이 들었고,

gunicorn은 프로세스 들을 prefork 하여, 대기 큐에 넣는 매니징하는 역할을 하기에 존재하는데,
lambda에서는 요청별로 독립적인 인스턴스 환경에서 실행되기에, 한 인스턴스(코드뭉치)에 mangum 까지 필요한가?

 

ref ) labmda의 요청에 대한 실행 과정 설명 (with. 인스턴스 생성)

 

따라서 mangum이 존재 의의에 대해 좀더 찾아보게 되었고, 해답은 다음 github issue 토론 내용 에서 발견 할 수 있었습니다.

토론 내용이 길어서, 내용을 정리하면 다음과 같습니다.

  • 질문 : FastAPI 배포시 적절한 ASGI 서버는 무엇인가요? (+일반 배포 or 서버리스 경우)
  • 답변 : tjango (FastAPI 만든 사람)의 FastAPI 설계 원리에 대한 설명글

 

토론 내용 정리

  • Uvicorn, Hypercorn 및 Daphne은 모두 ASGI 약관에 따른 서버 애플리케이션으로 HTTP를 처리하고, ASGI 프레임워크로 구축된 애플리케이션에 매개변수를 전달함
  • FastAPI는 ASGI 프레임워크로 HTTP와 직접적으로 통신하지 않고, 사용되는 app 객체가 앞선 서버에서 전달받은 매개변수를 바탕으로 실행됨
  • 따라서 HTTP를 통해 들어온 인자들을 FastAPI 앱에 전달하려면 무언가가 필요하다! (ex. FastAPI app 실행시 uvicorn app.py 하는 이유)
  • 일반적인 배포에서는 Uvicorn, Hypercorn, Daphne을 활용하여 처리할 수 있지만, Lambda 환경에서는 CloudFront / API Gateway 등을 통해 들어온 HTTP 인수 들에 대해 추가적인 처리가 필요하고, 이를 구현 해놓은 것이 Mangum 이다.
  • 따라서 Lambda 환경에서는 Mangum 만 사용한다.

 

구현 방법

구현 방법의 상세한 설명은 앞서 소개해드린 블로그들에 상세히 나와 있기에, 개괄적으로만 설명합니다.

 

1. 새로운 프로젝트 폴더, 가상환경에 package 설치

- pip install fastapi, mangum, boto3(필요시), 필요 패키지

 

2. aws 환경 준비

- iam 생성 및 권한 부여 / s3 bucket 생성 등 / 로컬에 aws 관련 config key 등록

 

3. 서버리스 코드 작성 (.py)

- fastapi 기반의 코드 작성 (저는 signed 된 s3 업로드 path를 가져오는 기능이 필요해서 다음과 같이 작성하였습니다)

from fastapi import FastAPI, APIRouter
from mangum import Mangum
from pydantic import BaseModel
import boto3

app = FastAPI()

MY_ACCESS_KEY = '<your aws access key>'
MY_SECRET_KEY = '<your aws secret key>'

BUCKET = '<your aws s3 bucket name>'

s3_client = boto3.client('s3', region_name='ap-northeast-2',
                         aws_access_key_id=MY_ACCESS_KEY, aws_secret_access_key=MY_SECRET_KEY
                         )
expiration=3600

class FileInfo(BaseModel):
    file_name:str

@app.get("/")
async def health_check():
    return {"message": "OK"}

@app.post(
    path="/get-upload-url", summary="get upload url"
)
async def get_upload_url(payload:FileInfo):
    object_name = payload.file_name
    response = s3_client.generate_presigned_post(BUCKET, object_name, ExpiresIn=expiration)
    return response

handler = Mangum(app)

 

4. Library zip 파일 만들기

- 구동에 필요한 패키지 라이브러리 들을 layer로 업로드 하기 위함

- labmda는 최종적으로 <패키지 layer> - <코드 layer> 로 구성됩니다.

$ mkdir python && cp -rf venv3/lib/python3.11/site-packages/* ./python
$ zip -r serverless-fastapi.zip python/

 

5. Lambda 만들기 (코드 파일 .py / .zip 업로드)

- 해당 코드 파일은 .py 내용만 복사하거나, 용량이 많다면 압축된 파일로 업로드

 

6. Layer 만들기 lambda 연결하기

- 앞서 압축한 Library zip을 layer로 등록하고 lambda와 연결해 줍니다.

 

7. api-gateway 만들기 (api 연결하기)

- aws의 api-gateway 서비스를 이용해 api를 구성하며 자원과 메소드를 생성해주고 lambda 함수와 연결해 줍니다.

- 메소드 생성시 프록시와 통합이란 체크 버튼 (체크)

 

8. Api 배포하기 (API 요청을 통해 테스트)

- 제가 사용한 body는 다음과 같습니다. 

- body : { "file_name":"image.png" }

 

9. Lambda 함수 로그 확인

- Lambda 에서 모니터링 탭을 누르면 자동으로 Cloudwatch에서 로그가 확인 가능합니다.