Heroku 서버에 배포를 해보겠습니다.
Gunicorn
프로덕션급 WSGI 서버인 Gunicorn을 사용하려면 먼저 requirements.txt 파일에 라이브러리와 버전을 명시합니다.
gunicorn==21.0.1
Gunicorn과 Uvicorn을 함께 사용하는 이유는 동시성과 병렬성의 장점을 모두 활용하기 위해서입니다.
Gunicorn은 여러 Uvicorn 프로세스를 동시에 관리하여 이를 달성합니다.
Dockerfile.prod 라는 Dockerfile를 새로게 작성해줍니다.
# pull official base image
FROM python:3.12.1-slim-bookworm
# create directory for the app user
RUN mkdir -p /home/app
# create the app user
RUN addgroup --system app && adduser --system --group app
# create the appropriate directories
ENV HOME=/home/app
ENV APP_HOME=/home/app/web
RUN mkdir $APP_HOME
WORKDIR $APP_HOME
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV ENVIRONMENT prod
ENV TESTING 0
# install system dependencies
RUN apt-get update \
&& apt-get -y install netcat-traditional gcc postgresql \
&& apt-get clean
# install python dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt .
RUN pip install -r requirements.txt
RUN pip install "uvicorn[standard]==0.26.0"
# add app
COPY . .
# chown all the files to the app user
RUN chown -R app:app $APP_HOME
# change to the app user
USER app
# run gunicorn
CMD gunicorn --bind 0.0.0.0:$PORT app.main:app -k uvicorn.workers.UvicornWorker
Gunicorn 실행을 위한 CMD 추가: Docker 컨테이너가 시작될 때 자동으로 실행될 명령을 정의합니다. 여기서는 Gunicorn을 Uvicorn worker class(-k uvicorn.workers.UvicornWorker)와 함께 실행하여 ASGI 애플리케이션을 호스팅하는 구성입니다. 이를 통해 비동기 Python 애플리케이션을 효율적으로 관리하고 실행할 수 있습니다.
새로운 환경 변수 구성: Dockerfile에는 ENVIRONMENT와 TESTING이라는 두 개의 환경 변수가 추가되었습니다. ENVIRONMENT 변수는 애플리케이션이 실행되는 환경(예: prod로 설정된 경우 운영 환경)을 지정하고, TESTING 변수는 애플리케이션이 테스트 모드에서 실행되고 있는지 여부를 나타내는 데 사용됩니다(0은 비활성화, 1은 활성화).
이러한 설정은 Docker 컨테이너 내에서 Python 웹 애플리케이션을 실행할 때 필요한 환경을 구축하고, Gunicorn을 사용하여 여러 Uvicorn 인스턴스를 관리함으로써 동시성과 병렬성을 효과적으로 활용할 수 있도록 합니다. Gunicorn은 여러 워커를 관리하여 부하 분산과 프로세스 관리를 담당하며, Uvicorn은 비동기 프로그래밍을 지원하는 가벼운 ASGI 서버로 각 워커에서 실행됩니다.
ENV ENVIRONMENT prod
ENV TESTING 0
위 문장에서는 Docker 컨테이너를 구성하면서 취한 두 가지 중요한 보안 및 호환성 조치를 설명하고 있습니다.
1. 비루트 사용자(non-root user) 생성 및 전환: Docker 컨테이너에서 실행되는 애플리케이션의 보안을 강화하기 위해, 루트 사용자가 아닌 비루트 사용자를 생성하고 이 사용자로 전환하는 작업을 진행합니다. 이는 Heroku와 같은 플랫폼에서 권장하는 보안 사례입니다. 루트 사용자로 애플리케이션을 실행할 경우, 애플리케이션이 노출될 수 있는 보안 위험이 증가하기 때문에, 비루트 사용자를 사용하여 이러한 위험을 줄이는 것입니다.
2. $PORT 환경 변수: Gunicorn 명령어에서 `$PORT` 환경 변수의 사용은 특히 중요합니다. 이 환경 변수는 Heroku와 같은 클라우드 플랫폼에서 애플리케이션이 반드시 듣고 있어야 하는 포트 번호를 지정합니다. 즉, Heroku는 동적으로 포트 번호를 할당하고, 이 할당된 포트 번호는 `$PORT` 환경 변수를 통해 애플리케이션에 제공됩니다. 따라서, 애플리케이션은 이 환경 변수를 통해 제공된 포트에서 리스닝해야 하며, Gunicorn 설정에서 이를 반영하기 위해 `--bind 0.0.0.0:$PORT` 옵션을 사용합니다.
이 두 조치는 보안을 강화하고, 클라우드 플랫폼에서의 애플리케이션 배포를 위한 호환성을 보장하는 데 중요합니다. 비루트 사용자로의 전환은 보안 리스크를 줄이는 한편, `$PORT` 환경 변수의 사용은 Heroku와 같은 클라우드 서비스에서 요구하는 동적 포트 할당을 지원하여, 애플리케이션이 문제 없이 서비스될 수 있도록 합니다.
Heroku
헤로쿠 계정에 가입한 다음 Heroku CLI를 설치합니다.
그리고 새로운 앱을 만듭니다.
$ heroku create
Creating app... done, ⬢ quiet-citadel-80656
https://quiet-citadel-80656-d411fa0f58d4.herokuapp.com/ | https://git.heroku.com/radiant-everglades-49858.git
Heroku container registry에 로그인합니다.
$ heroku container:login
mini plan으로 postgres database에 프로비저닝 합니다.
$ heroku addons:create heroku-postgresql:mini --app quiet-citadel-80656
프로덕션 이미지를 빌드하고 태그를 지정합니다.
registry.heroku.com/<app>/<process-type>
막 생성한 헤로쿠 앱의 이름으로 `<app>` 부분에 지정합니다. `<process-type>`은 web으로 지정합니다.
$ docker build -f project/Dockerfile.prod -t registry.heroku.com/quiet-citadel-80656/web ./project
로컬 테스트를 하려면 컨테이너를 기동해보겠습니다.
$ docker run --name fastapi-tdd -e PORT=8765 -e DATABASE_URL=sqlite://sqlite.db -p 5003:8765 registry.heroku.com/quiet-citadel-80656/web:latest
아래와 같은 로그를 확인 할 수 있어야 합니다.
[2024-02-06 12:22:08 +0000] [8] [INFO] Starting gunicorn 21.0.1
[2024-02-06 12:22:08 +0000] [8] [INFO] Listening at: http://0.0.0.0:8765 (8)
[2024-02-06 12:22:08 +0000] [8] [INFO] Using worker: uvicorn.workers.UvicornWorker
[2024-02-06 12:22:08 +0000] [10] [INFO] Booting worker with pid: 10
[2024-02-06 12:22:08 +0000] [10] [INFO] Started server process [10]
[2024-02-06 12:22:08 +0000] [10] [INFO] Waiting for application startup.
[2024-02-06 12:22:08 +0000] [10] [INFO] Application startup complete.
http://localhost:5003/ping/ 엔드포인트를 확인하면 아래와 같이 확인 할 수 있습니다.
{
"ping": "pong!",
"environment": "prod",
"testing": false
}
아래 명령어로 특정 컨테이너를 강제 삭제합니다.
$ docker rm fastapi-tdd -f
레지스트리에 이미지를 푸시합니다.
$ docker push registry.heroku.com/quiet-citadel-80656/web:latest
이미지를 배포합니다.
$ heroku container:release web --app quiet-citadel-80656
quiet-citadel-80656 로 앱 이름을 대체하였지만 본인의 앱이름을 확인하여서 이에 맞게 명령어를 수행해주세요.
컨테이너가 실행되고 https://APP_NAME-IDENTIFIER.herokuapp.com/ping 엔드포인트를 통해서 정상적으로 통신이 이루어 짐을 확인 할 수 있습니다.
M1 or M2 사용자의 경우 Exec format error가 발생 할 수 있으므로 buildx 명령어를 사용해야 할 수 있습니다.
$ docker buildx build --platform linux/amd64 -f project/Dockerfile.prod -t registry.heroku.com/quiet-citadel-80656/web ./project
이번에는 마이그레이션을 적용하겠습니다.
$ heroku run aerich upgrade --app quiet-citadel-80656
'summaries' 엔드포인트가 잘 작동되는지 확인해봅니다.
/summaries | GET | READ | get all summaries |
/summaries/:id | GET | READ | get single summary |
/summaries | POST | CREATE | add a summary |
HTTPie를 이용하여 HTTP POST 요청을 통해 새로운 리소스를 만들어봅니다.
$ http --json POST https://quiet-citadel-80656-d411fa0f58d4.herokuapp.com/summaries/ url=https://testdriven.io
정상적으로 201 반환값을 볼 수 있습니다.
HTTP/1.1 201 Created
Connection: keep-alive
Content-Length: 38
Content-Type: application/json
Date: Sun, 30 Oct 2022 14:15:53 GMT
Server: uvicorn
Via: 1.1 vegur
{
"id": 1,
"url": "https://testdriven.io"
}
https://APP_NAME-IDENTIFIER.herokuapp.com/docs 에서도 잘 작동하는지 확인합니다.
'프로그래밍 언어 > 파이썬' 카테고리의 다른 글
코루틴이란? (0) | 2024.03.20 |
---|---|
List와 Tuple의 차이 (0) | 2024.03.20 |
FastAPI를 이용한 TDD 개발 - 8 (0) | 2024.03.14 |
FastAPI를 이용한 TDD 개발 - 6 (0) | 2024.03.14 |
FastAPI를 이용한 TDD 개발 - 5 (0) | 2024.03.14 |