[FastAPI] 15. FastAPI를 Serverless로 배포하는 방법 - Mangum
이번 포스트에서는 FastAPI와 Serverless(서버리스) 조합에 대해 알아보도록 하겠습니다. 제가 초기 서버리스를 배웠을 때는 AWS의 Lambda라는 서비스가 나왔을 때였는데요. 당시 사용할 때는 별도의 프레임워크 필요없이 함수의 구현만으로 API 서비스를 만들 수 있는 Cloud Native(클라우드 네이티브) 개발 모델이었습니다.
Serverless
Serverless(서버리스)라는 단어를 일반적으로 해석하면 '서버 없는 애플리케이션', '서버 없는 서비스'라는 뜻이 됩니다.
하지만 실제로 서버리스는 서버 없이 서비스를 운영하거나 애플리케이션을 배포하는 것은 아닙니다.
실제 서버리스는 개발자가 별도로 서버 관리라는 비용없이 애플리케이션을 빌드하고 실행할 수 있도록 하는 개발 모델로써 대표적으로 아래의 클라우드 벤더 서비스가 있습니다.
- AWS Lambda
- AWS Fargate
- GCP Functions
실제로 우리는 API 애플리케이션 하나를 배포하기 위해 컴퓨팅 환경 구축(k8s), 네트워크 환경 구축(istio, gateway) 등을 수동으로 진행해야 합니다. 하지만 Serverless는 이러한 리소스 구축 비용을 들이지 않고도 API 코드만 가지고 있다면 얼마든지 API 서비스를 배포할 수 있습니다.
이를 사용할 수 있는 대표적인 방법에는 AWS ECS + AWS Fargate 조합의 컨테이너 오케스트레이션 배포 방식(CaaS) 서버리스와 AWS Lambda + AWS API Gateway 조합의 코드 베이스 배포 방식(FaaS) 서버리스가 있습니다.
Mangum
이번 글에서는 코드 베이스 배포 방식인 AWS Lambda + AWS API Gateway 조합으로 FastAPI 애플리케이션을 배포하는 방법에 대해 알아보겠습니다.
Mangum은 AWS API Gateway에서 AWS Lambda의 Python ASGI Application Framework을 처리하기 위해 개발된 어댑터로 Starlette와 FastAPI 호환성이 좋은 녀석입니다.
https://github.com/jordaneremieff/mangum
GitHub - jordaneremieff/mangum: AWS Lambda support for ASGI applications
AWS Lambda support for ASGI applications. Contribute to jordaneremieff/mangum development by creating an account on GitHub.
github.com
우리는 FastAPI + Mangum 조합으로 코드 배포 방식 서버리스 애플리케이션을 개발해보고자 합니다.
Create Project
먼저 FastAPI 프로젝트를 만들기 위해 아래의 빌드 도구와 패키지 관리 도구 등을 사용할 것입니다.
- Poetry
- Virtualenv
- PyCharm
이 글에서는 PyCharm을 이용해 FastAPI 프로젝트를 만들어보겠습니다.
New Project를 클릭한 후 왼쪽 사이드바에서 FastAPI를 클릭합니다. 그리고 Poetry 환경에 Python 버전은 3.9 버전을 사용하도록 하겠습니다.
그런 다음 pyproject.toml 파일을 열면 기본적으로 uvicorn과 fastapi 패키지가 설치되어 있는 것을 보실 수 있습니다. 우리는 여기에 Mangum을 추가할 것입니다.
$ poetry add mangum
poetry shell에서 위 명령어를 입력하면 mangum 패키지가 설치됩니다.
Create API
다음은 Serverless(서버리스)에 사용할 API를 만들어 보겠습니다. 이를 위해 프로젝트 구조를 아래와 같이 구축할 것입니다.
상위 패키지 이름으로 api를 한 다음 order.py라는 파일을 만들어줍니다. 우리는 Mock Order API를 만들어볼 것입니다.
실제로 우리는 AWS 서버리스 환경에서 API가 잘 호출되는지만을 보기 위한 것이므로 간단히 JSON 값만을 반환하는 API를 만들 것입니다.
기본으로 만들어졌던 main.py를 위와 같이 수정하여 healthcheck API를 만들고, 앞서 만든 Order API를 api prefix router 내에 추가해줍니다.
그런 다음, Mangum 인스턴스에 FastAPI app을 주입해줍니다.
먼저 uvicorn을 이용해 로컬에서 API 서버가 잘 동작하는지 확인합니다.
Swagger에 접속하여 실제로 API가 의도한대로 반환하는지 확인해줍시다.
AWS Serverless 환경 만들기
자 이제 애플리케이션 코드 작성은 모두 끝났습니다. 이제 우리가 작성한 코드를 업로드하면 바로 서버가 배포될 수 있도록 AWS 환경을 만들어야 하는데요. 여기서 필요한 환경은 아래와 같습니다.
- AWS Lambda
- AWS API Gateway
이렇게 두 가지가 필요한데, AWS Lambda는 우리가 작성한 애플리케이션 코드를 올리는 곳이고, API Gateway는 AWS에서 SaaS 환경에 엔드포인트를 제공하기 위한 곳입니다.
우리는 먼저 Lambda에 들어가서 코드를 업로드 하도록 하겠습니다.
함수 이름을 입력하고, Runtime 환경은 아까 맞췄던 Python 3.9로 맞춰주도록 합니다.
그런 다음, Create function을 클릭해 함수를 생성합니다.
함수를 생성했으면 최상단에 함수 생성에 성공했다는 메시지가 등장합니다.
이제 우리는 코드를 업로드하기 위해 아까 만들어뒀던 API 코드를 zip 파일로 압축시키도록 하겠습니다.
OS X 혹은 Linux 운영체제를 사용하고 계신다면 위 화면에 나온 것처럼 zip 명령어를 이용해서 프로젝트의 모든 파일을 zip으로 압축할 수 있습니다. Windows 운영체제를 사용하고 계신다면 반디집 등의 유틸리티를 이용하셔서 압축을 진행하시면 됩니다.
압축이 모두 끝났다면 Lambda 함수 페이지에서 Upload From 버튼을 클릭한 뒤, .zip file을 눌러 압축한 파일을 업로드 해줍니다.
그러면 아까 만들어뒀던 코드들이 aws lambda 내로 업로드 된 것을 확인할 수 있습니다. 이제 API가 잘 동작하는지를 테스트 해보도록 하죠. Test 버튼 오른쪽에 있는 화살표 아이콘을 클릭해 테스트 환경을 만들어보겠습니다.
Create new event를 선택하시고, 적당한 이벤트 이름을 지정한 다음, Template을 클릭하여 API Gateway AWS Proxy 템플릿을 만들어줍니다.
그럼 코드가 만들어지는데요. 여기서 맨 위쪽에 있는 path를 우리가 테스트하고자 하는 URI를 넣습니다. 여기서는 /healthcheck 엔드포인트를 테스트해보도록 하겠습니다. method가 GET이므로 method도 GET으로 지정해줍니다.
마지막으로 Runtime settings에서 Edit 버튼을 클릭해 Handler 함수를 lambda_function.py 파일이 아닌 main.py에 있는 handler 객체로 바꿔주시면 됩니다.
자 이제 Test 버튼을 클릭해보면...
어라...? fastapi 모듈을 찾을 수 없다는 오류 메시지를 볼 수 있을 것입니다.
우리는 작성한 코드만 넣어줬고, 모듈 위치를 지정해주지는 않았습니다. 기본적으로 Python에서는 현재 경로를 기준으로 모듈을 찾기 때문에 우리가 압축을 하기 전 모듈들을 해당 위치에 넣어둔 다음 압축을 하면 문제를 해결할 수 있습니다.
하지만 그렇게 하게 되면 업로드 용량이 많이 커져 Lambda 에디터에서 코드를 즉석으로 수정할 수가 없는 불편함이 생기는데요. 이럴 때는 Lambda Layer를 사용해 볼 수 있습니다.
AWS Lambda 메인 페이지로 넘어와 왼쪽 사이드바를 보면, Layers라는 항목을 보실 수 있습니다. 들어가신 후 Create Layer 버튼을 클릭합니다.
Layer 이름을 입력하면 .zip 파일을 업로드할 수 있는 구간이 나옵니다. 이제 PyCharm으로 넘어가서 다운로드 받았던 모듈들을 압축해 넣어보도록 하겠습니다.
$ cd "`poetry env info -p`"/lib/python3.9
우리는 Python 3.9 버전을 사용했으므로 lib/pythpn3.9 경로로 들어가면 되겠습니다. 또한 Poetry를 사용했기 때문에 poetry 명령어를 이용해서 virtualenv 경로를 쉽게 정의할 수 있습니다.
AWS Lambda Layers를 만들 때는 정해진 폴더 이름을 준수해야 합니다. 따라서 우리는 모듈이 담겨진 폴더의 이름을 아래 매뉴얼대로 진행해야 합니다.
https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html#configuration-layers-path
Creating and sharing Lambda layers - AWS Lambda
Each time that you call publish-layer-version, you create a new version of the layer.
docs.aws.amazon.com
AWS 공식 문서에 의하면 Python 환경의 경우에는 python 폴더를 압축 파일의 최상위 루트 폴더로 지정하고, 해당 폴더에 모듈들을 넣는 것을 권장하고 있습니다.
$ mkdir python && cp -rf site-packages/* ./python
따라서 python 폴더를 먼저 생성해 준 다음, site-packages 내에 있는 모듈들을 python 폴더로 복사해줍니다.
$ zip -r fastapi-serverless.zip python/
그런 다음 zip 명령어를 이용해서 python 디렉터리에 있는 모든 모듈들을 zip으로 압축합니다.
이렇게 해서 만들어진 압축 파일을 위에 Create Layer에 있는 Upload File 버튼을 통해 업로드 시켜줍니다.
그런 다음 Compatible runtimes를 Python 3.9 버전으로 맞춰줍니다.
업로드에 성공했으면 이렇게 Layer가 성공적으로 생성되었다는 메시지와 함께 Version 1이 추가됩니다.
다시 Lambda의 Functions로 넘어가면 맨 아래쪽에 Layers라는 항목이 보입니다. 여기서 Add a layer를 클릭합니다.
우리는 fastapi-serverless를 위한 Layer를 별도로 만들었으므로 Custom layers를 클릭해 아까 만들었던 Custom Layers를 선택한 다음 Add를 클릭합니다.
그러면 위에서 만들었던 fastapi-serverless라는 이름의 layer가 추가된 것을 볼 수 있습니다.
자 이제 Test 버튼을 클릭하면... 위 화면처럼 200 코드와 함께 healthcheck api 코드로 정의한 것처럼 message: OK가 나오는 것을 볼 수 있습니다. 성공적으로 동작하는 것을 확인했으므로 Deploy 버튼까지 클릭해주면 Lambda 함수 업로드는 모두 끝납니다.
이젠 우리가 만든 이 애플리케이션 코드를 특정 API Gateway에 연결해 외부에서 호출할 수 있도록 연결해주면 됩니다.
먼저 API Gateway에 들어가보도록 하겠습니다.
Create API를 클릭하게 되면 위 화면이 나타나는데, 우리는 여기서 REST API용 게이트웨이를 만들 것입니다. REST API 항목에서 Build를 클릭합니다.
적당한 API 이름을 지정한 후 Create API를 클릭합니다.
API가 만들어졌으면 상단 Actions 버튼을 누르면 메뉴가 나오는데, Create Method를 클릭해 메서드를 생성합니다.
최상위 Path가 나오면서 메서드를 선택할 수 있는 목록 박스가 생깁니다. 박스를 누르면 REST API의 기본 메서드인 GET, POST, PUT, PATCH, DELETE, OPTIONS와 함께 전체 메서드인 ANY가 보입니다. 우리는 애플리케이션 코드에 있는 모든 메서드를 한 포인트에 담을 것이므로 ANY를 선택합니다.
이 엔드포인트를 호출하면 아까 구현한 코드를 읽을 수 있도록 위와 같이 Lambda Function을 선택해줍니다. 그리고 반드시 이 API는 Lambda Proxy로 불러지도록 Use Lambda Proxy Integration에 체크 버튼을 활성화 하도록 합니다.
이렇게 생성을 시도하게 되면 API Gateway가 Lambda function에 있는 리소스에 접근하기 위해 권한을 요청하는 화면이 나옵니다. OK 버튼을 클릭합니다.
하지만 이 작업만으로는 FastAPI로 만든 모든 Router를 호출할 수는 없습니다. 이를 위해서 API Gateway가 새로운 엔드포인트를 요청했을 때 이를 Lambda 함수로 요청할 수 있도록 리소스를 생성해야 합니다.
Actions 버튼을 눌러 Create Resource를 클릭하면 위와 같이 New Child Resource를 만들 수 있습니다. 여기서 Configure as proxy resource 버튼을 활성화 한 다음 Create Resource 버튼을 클릭합니다.
역시 Lambda 함수를 넣고 저장합니다.
API Gateway에 대한 모든 설정이 끝났습니다. 이제 마지막으로 이 환경이 외부에 배포될 수 있도록 Actions 버튼을 눌러 Deploy API를 클릭합니다.
새로운 스테이지를 만들 것이므로 Stage name을 v1으로 지정하여 version 1 api임을 명시해준 다음 배포를 진행합니다.
그러면 호출할 수 있는 URL이 생깁니다. 해당 URL을 이용해 접속을 시도해보면...
이렇게 orders API 호출이 성공하고,
healthcheck API도 잘 동작하는 모습을 볼 수 있습니다.
마치며...
쓰다보니 이미지가 엄청 많이 붙여진 글이 되었습니다. 그런데, 서두와 달리 AWS의 서버리스 환경을 구축하는데, 사실상 서버 하나를 만드는 것만큼 드는 것 같은데요? 라는 느낌을 받으실 수도 있을 것입니다.
다음 포스트에서는 위의 FastAPI 코드를 그대로 이용하여 AWS 서버리스 환경을 Python 코드로 구축하는 과정을 진행해보도록 하겠습니다.
'Programming > Python' 카테고리의 다른 글
[FastAPI] 16. Pulumi를 이용해 FastAPI Serverless 환경 구축해보기 (0) | 2022.05.22 |
---|---|
[FastAPI] 14. SQLAlchemy의 One-to-Many, Many-to-Many, Self referential relationship (0) | 2022.04.24 |
[Python] Python Database API - Python에서는 어떻게 DB와 연결할까? (2) | 2022.02.25 |
[Python] anyio - 한 층 더 강화된 비동기 패러다임 (4) | 2022.01.09 |
[FastAPI] 13. SQLAlchemy와 Pydantic을 이용한 관계 데이터 매핑 (0) | 2021.11.09 |