ASGI 웹 프레임워크 FastAPI 를 시작하며
Python, Backend 를 시작하는 데 부족한 부분들을 정리해 보고자 한다.
1. Backend 구성 (feat. WSGI 와 ASGI)
Python 의 일반적인 웹서비스에 아키텍쳐를 이해하기 위해 아래와 같은 그림을 그려봤다.
1) Web Server
웹서버의 역할은 두개로 나누어 볼 수 있다. 정적인 요청의 서빙이나 Reverse Proxy를 이용하여 어플리케이션 서버로 동적인 요청을 전달하는 용도이다. (로드밸런싱의 역할도 가능하겠다.) Apache 나 Nginx 는 워낙 유명하고 근래에는 Go 로 개발된 Caddy라는 서버도 드물게 쓰는 모양이다.
Python 이 아닌 다른 환경의 웹서버와 FastCGI 서버와의 연결이 그러하듯 웹서버와 TCP 로 확장하거나 Unix Socket 으로 연결하여 성능을 높이기도 한다.
2) WSGI 와 ASGI
Python 의 경우에도 어플리케이션 서버와의 통신을 위해 Gateway Interface 가 존재하고 있고 초기에 나온 웹 어플리케이션과의 인터페이스가 WSGI (Web Server Gateway Interface)이다.
그러면 ASGI(Asynchronous Server Gateway Interface) 는 무엇일까? ASGI 문서에 다음과 같이 WSGI 와 ASGI에 대해 설명하고 있다.
ASGI( Asynchronous Server Gateway Interface )는 WSGI의 정신적 계승자로, 비동기 가능 Python 웹 서버, 프레임워크 및 애플리케이션 간의 표준 인터페이스를 제공하기 위한 것입니다.
WSGI가 동기 Python 앱에 대한 표준을 제공했다면 ASGI는 WSGI 이전 버전과의 호환성 구현과 여러 서버 및 애플리케이션 프레임워크를 통해 비동기 및 동기 앱 모두에 대한 표준을 제공합니다.
위 그림을 보면 의아스러운 부분이 있다. gunicorn(WSGI) 과 uvicorn(ASGI) 을 같이 사용하고 있는데 왜 그럴까?
여기서 gunicorn은 프로세스 매니저로서 동작하게 된다. 프로세스 매니저로서 Master 프로세스를 띄우고 Worker 프로세스로, 단일 프로세스로 실행하는 uvicorn (uvicorn 에는 Gunicorn-호환 worker class 가 존재한다) 프로세스를 여러개 미리 띄워 요청을 처리하는 구조이다.
3) Application Framework
파이썬에도 여러가지 프레임워크가 존재 하고 있고 용도가 Full Stack (Django 등) 인지 LightWeight (Flask, bottle 등) 인지 그리고 오로지 API 만을 위한 것 (FastAPI, falcon 등) 인지에 따라 원하는 프레임워크를 선택하면 되겠다.
2.FastAPI 살펴보기
FastAPI는 현대적이고, 빠르며(고성능), 파이썬 표준 타입 힌트에 기초한 Python3.6+의 API를 빌드하기 위한 웹 프레임워크입니다.
FastAPI 는 API 개발을 위한 Starlette 과 Pydantic을 기반으로 한 웹 프레임워크이며, 그 성능이 NodeJS 나 Go 에 필적할만한 성능(uvicorn+starlette 구성이 이를 가능하게 해주는 듯)이라고 말하고 있다.
1) 개발환경 구성
아래와 같이 conda로 가상환경을 새로 생성 하기로 했다.
conda create -n web python=3.9
패키지 관리를 위해 poetry 를 사용하고 기본 세팅을 해봤다.
conda install poetry
poetry new hello-api
2) fastapi, uvicorn 설치
이제 예제를 만들어 보기 위해 fastapi 와 uvicorn 을 패키지에 추가 해보자.
poetry add fastapi 'uvicorn[standard]'
pyproject.toml 를 확인해보면 fastapi 와 uvicorn 의 의존성이 추가 된 것을 볼 수 있다.
3) 예제 API 만들기
./hello_api/main.py 에 개발문서를 참고하여 아래 코드를 만들어줬다.
from typing import Optional
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Optional[str] = None):
return {"item_id": item_id, "q": q}
uvicorn 을 실행하고 동작을 확인했다.
uvicorn hello_api.main:app --reload
4) Swagger 문서 확인
아래와 같이 ~/docs 를 보면 swagger UI를 확인 할 수 있다.
5) Dockerfile
FROM python:3.9.2-slim as requirements-stage
WORKDIR /tmp
RUN pip install poetry
COPY ./pyproject.toml ./poetry.lock* /tmp/
RUN poetry export -f requirements.txt --output requirements.txt --without-hashes
FROM python:3.9.2-slim
WORKDIR /code
COPY --from=requirements-stage /tmp/requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
- 참고