영웅은 죽지 않는다

Docker와 Poetry로 FastAPI 개발환경 구성하기 본문

Programming/Web

Docker와 Poetry로 FastAPI 개발환경 구성하기

YoungWoong Park 2024. 6. 15. 21:28

 

제목 그대로, FastAPI 환경 구성을 위한 글입니다.

 

오래전에 FastAPI를 활용해 NLP 모델을 서버에 띄워 활용한 적이 있었는데,

당시엔 on-prem 물리서버에 접속하여 활용을 했던 터라, VM 구성과 이미지 활용에 집중했던 기억이 있습니다.. (해당 글)

그때도 가상환경(venv)을 구성해 의존성 관리를 해주었고, 물리서버 내 이미지를 복제/백업하여 CI에도 힘을 썼습니다.

 

오랜만에 새로운 개발 프로젝트를 진행하게 되며 다시금 FastAPI를 활용할 기회가 생겼는데

이번엔 클라우드를 활용할 계획이었고,

회사 프로젝트가 아닌 외부 토이프로젝트인 김에 새로운 프레임워크와 Tool을 활용해 보고자 하였는데요,

 

그래서 이번 프로젝트에는, 다양한 환경구성 방법 중

도커로 이미지를 생성하여 빌드하고 Poetry를 통해 가상환경 의존성을 구성해보기로 결정하였습니다.

 

그 과정을 담은 첫번째 글입니다.


 

 

1. FastAPI 기본 구성 - Poetry

my_project/
 │── app/
 │    │── __init__.py
 │    │── main.py
 │    │── api/ 
 │    │     │── __init__.py 
 │    │     │── (기타 api 파일)
 │── Dockerfile
 │── poetry.lock
 │── pyproject.toml

 

 

poetry는 파이썬 기반의 프로젝트에 대한 의존성(dependency)을 관리해주는 툴입니다.

흔히 말해 pyCharm의 venv나 requirements.txt 등으로 관리하는 가상환경 또는 의존성 관리에 대해

몇 줄 안되는 명령어들로 통해 조금 더 간편하게 관리할 수 있다는 큰 장점이 있습니다.

 

공식 문서에 나와있는 poetry에 대한 설명은 아래와 같네요

Poetry is a tool for dependency management and packaging in Python. It allows you to declare the libraries your project depends on and it will manage (install/update) them for you. Poetry offers a lockfile to ensure repeatable installs, and can build your project for distribution.

 

일단 먼저 poetry를 설치해줄 건데요, 터미널 혹은 cmd 창에서 본인의 프로젝트 경로에 이동한 뒤

아래 공식문서 기반으로 자신의 운영체제에 맞게 설치를 진행하면 되겠습니다

https://python-poetry.org/docs/#installing-with-the-official-installer

 

Introduction | Documentation | Poetry - Python dependency management and packaging made easy

If you installed using the deprecated get-poetry.py script, you should remove the path it uses manually, e.g. rm -rf "${POETRY_HOME:-~/.poetry}" Also remove ~/.poetry/bin from your $PATH in your shell configuration, if it is present.

python-poetry.org

 

- Linux, MacOS

curl -sSL https://install.python-poetry.org | python3 -

 

- Windows

(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | py -

 

설치를 마쳤다면, poetry --version을 통해 잘 설치가 되었는지 확인합니다.

 

 

이제 다양한 명령어를 통해 프로젝트에서 활용하고자 하는 라이브러리, 패키지들을 관리할 수 있습니다.

 

먼저 프로젝트 초기 세팅을 진행합니다.

 

poetry init

 

패키지 이름, 버전, 설명 등을 입력해야 하는데 필수값이 아닌 것들은 넘어가도 됩니다

This command will guide you through creating your pyproject.toml config.

Package name [your_project]: my_project
Version [0.1.0]: 
Description []: 
Author [Your Name <your.email@example.com>, n to skip]: 
License []: 
Compatible Python versions [^3.8]:

Would you like to define your dependencies (require) interactively? (yes/no) [no]: 
Would you like to define your dev dependencies (require-dev) interactively? (yes/no) [no]:

 

그러면 pyproject.toml 파일이 아래와 같이 생성됩니다

[tool.poetry]
name = "my_proejct"
version = "0.1.0"
description = ""
authors = ["YoungWoongPark <assayw119@naver.com>"]
readme = "README.md"

[tool.poetry.dependencies]
python = "3.12.3"

 

여기에, poetry add 명령어를 통해 필요한 라이브러리를 설치합니다.

그러면 패키지의 정보가 담긴 poetry.lock 파일이 생성되게 됩니다.

poetry add fastapi
poetry add uvicorn
...

 

저는 필요한 라이브러리를 설치하니 아래와 같이 pyproject.toml이 변경되었습니다.

[tool.poetry]
name = "my_project"
version = "0.1.0"
description = ""
authors = ["YoungWoongPark <assayw119@naver.com>"]
readme = "README.md"

[tool.poetry.dependencies]
python = "3.12.3"
fastapi = "^0.111.0"
mangum = "^0.17.0"
SQLAlchemy = "^2.0.30"
pymysql = "^1.1.0"
pydantic = "^2.7.1"
pydantic-core = "^2.18.2"
uvicorn = "^0.29.0"
python-jose = "^3.3.0"
pydantic-settings = "^2.2.1"
sqlmodel = "^0.0.18"


[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

 

이제 이 패키지들을 설치합니다.

 

poetry install --no-root

 

여기서 --no-root를 넣는 이유는, 프로젝트 자체를 가상환경에 포함하지 않도록 하기 위함입니다.

프로젝트 내 코드나 파일이 변경될 수 있으므로, 패키지에 대한 의존성만 설치하여 가상환경을 경량화합니다.

 

이제 아래 명령어를 통해 가상환경을 활성화 합니다.

(가상환경에서 나오고자 할 경우 poetry exit 명령어를 입력합니다)

poetry shell

 


2. FastAPI 기본 구성 - Docker

이제 가상환경 구성이 끝났으니, fastapi를 로컬에서 띄우는 테스트를 진행해본 후

이를 도커 환경에서 이미지로 띄울 준비를 합니다.

 

my_project/app 하위에 main.py 파일을 만들고

루트에서 간단하게 출력하는 문구를 만들어봅니다

 

from fastapi import FastAPI

app = FastAPI()

@app.get('/')
def welcome_root():
    return {"message": "Hello FastAPI"}

 

이후 아래 명령어를 통해 서버에 잘 접속되는지 확인해봅니다

 

uvicorn app.main:app --reload

 

 

 

이후 my_project 경로에 Dockerfile을 생성 후 아래와 같이 입력합니다

(DockerFile, Dockerfiles 등으로 입력하면 안되고, 정확하게 "Dockerfile"이라고 입력해야 추후 오류가 나지 않습니다!)

 

# Python 람다 함수를 위한 Dockerfile(Linux/ARM64)
FROM python:3.12.3

# Poetry 설치
RUN pip install -U poetry

# 경로 정의
WORKDIR /workdir

# 로컬에 있는 pyproject.toml, poetry.lock 파일을 컨테이너로 복사
COPY poetry.lock pyproject.toml /workdir/

# Poetry를 이용하여 의존성 설치 
RUN poetry config virtualenvs.create false \
 && poetry install --no-root --no-interaction 

# 로컬에 있는 소스코드를 컨테이너로 복사
COPY . /workdir

# Python 경로 설정
ENV PYTHONPATH=/usr/local/bin/python3.12

# Poetry 바이너리 권한 확인 및 설정
RUN chmod +x /usr/local/bin/poetry

# Poetry가 설치된 Python을 사용하도록 설정
RUN sed -i '1s|^.*$|#!/usr/local/bin/python3.12|' /usr/local/bin/poetry

# 권한과 바이너리 위치 확인
RUN ls -l /usr/local/bin/poetry

WORKDIR /workdir/app
CMD ["poetry", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

 

생략되었지만.. 도커 빌드시 무수한 에러를 맞이했는데

이미지 내에서 파이썬 경로를 찾지 못하거나.. arm64용 아키텍처 바이너리를 인식하지 못하는 등..

다양한 에러를 하나씩 해결하다 보니 위같은 Dockerfile이 완성되었습니다..

 

각자 편의에 맞게끔 수정해가며 작성하면 될것 같네요!

 

이제 Docker 이미지 빌드 테스트를 진행합니다

 

docker build -t test:latest .

 

 

빌드가 잘 되었다면, 이미지를 기반으로 생성된 가상화 컨테이너 환경에 접속/실행해봅니다

 

docker run -it test

 

-it 옵션은 터미널을 interactive 모드로 실행하는 옵션입니다.

-rm 옵션을 넣어 컨테이너 종료시 자동 삭제되게끔 할 수도 있습니다.

 

 

잘 실행되는 것을 볼 수 있습니다.

 

 

이제, 다양한 형태의 API를 만들고 Docker를 사용하여 컨테이너 이미지를 빌드하고 배포하는 프로세스를 구축할 건데요,

저는 AWS ECR에 이미지를 띄우고 ECS를 통해 배포할 예정입니다.

 

해당 내용은 이후 과정에 담겠습니다.