배포 준비
배포는 보통 개발 주기에서 가장 마지막 단계다. 애플리케이션을 배포하기 전에 배포를 위한 설정이 모두 제대로 준비됐는지 확인해야 한다. 이 설정에는 의존 라이브러리가 정의된 requirements.txt 파일과 환경 변수 설정 등이 포함된다.
beanie와 pytest 같은 라이브러리를 설치했다. 하지만 의존성 관리자 역할을 하는 requirements.txt 파일에는 포함되어 있지 않으니 추가해야 한다.
requirements.txt 파일은 항상 최신 상태로 유지해야 한다.
파이썬에서는 pip freeze 명령을 사용해 개발 환경에 사용된 패키지들을 추출할 수 있다. 이 명령을 사용하면 설치된 모든 패키지의 목록뿐만 아니라 해당 패키지와 연관된 서브 패키지의 목록도 볼 수 있다.
requirements.txt 파일을 수동으로 관리할 수 있으므로 주요 패키지만 나열하는 것이 가능하다.
많은 의존 라이브러리가 pip freeze 명령어를 실행하면 표시되지만 몇몇 라이브러리는 애플리케이션에서 직접적으로 사용되지 않는다. 수동으로 변경해서 우리가 사용할 라이브러리로만 채운다.
도커는 컨테이너화에 사용되는 기술이다. 컨테이너는 패키지, 코드, 의존 라이브러리로 구성된 하나의 시스템으로, 실행 환경에 의존하지 않는다. 따라서 다른 환경에서도 쉽게 애플리케이션을 배포할 수 있다. 도커는 도커파일을 사용해서 컨테이너화한다.
도커는 로컬 개발 환경 뿐만 아니라 프로덕션 애플리케이션을 배포할 때도 사용된다.
애플리케이션 컨테이너와 데이터베이스 컨테이너처럼 여러 컨테이너를 사용해서 애플리케이션을 구성하는 경우에는 도커 구성 도구를 활용한다. 도커 구성 도구는 여러 컨테이너로 구성된 도커 애플리케이션을 설정 파일(일반적으로 docker-compose.yml) 안에 정의할 때 사용되며 도커 엔진 설치 시 함께 설치된다.
도커파일은 도커 이미지를 빌드하기 위한 몇 가지 명령셋으로 구성된다. 빌드된 도커 이미지는 개인 또는 공개 레지스트리에 등록된 후 AWS나 구글 클라우드 같은 클라우드 서버에 배포된다.
프로젝트의 루트 디렉토리에 Dockerfile이라는 파일을 생성한다.
touch Dockerfile
Dockerfile에 다음과 같은 내용을 추가한다:
FROM python:3.10
// 도커파일에서 가장 먼저 할 일은 FROM 키워드를 사용해 기본 이미지를 지정하는 것이다. 여기서는 파이썬 이미지를 사용하여 여러 가지 버전을 사용할 수 있다.
WORKDIR /app
// 작업 디렉토리를 /app으로 설정한다. 작업 디렉토리는 이미지로 빌드될 프로젝트 구조를 정리할 때 도움이 된다.
COPY requirements.txt /app/requirements.txt
// requirements.txt 파일을 로컬 디렉토리에서 도커 컨테이너의 작업 디렉토리로 복사한다.
RUN pip install --upgrade pip && pip install -r /app/requirements.txt
// pip 패키지를 업그레이드 하고 requirements.txt 파일을 기반으로 의존 라이브러리를 설치한다.
EXPOSE 8000
// 로컬 네트워크에서 애플리케이션에 접속할 수 있는 포트 번호를 설정한다.
COPY ./ /app
// 나머지 파일을 도커 컨테이너의 작업 디렉토리로 복사한다.
CMD ["python", "main.py"]
// 애플리케이션 실행
도커파일의 각 명령은 개별 레이어로 빌드되며, 도커는 각 레이어에 캐시를 적용해서 빌드 시간을 줄이고 반복을 제거한다.
예를 들어 아주 중요한 명령으로 구성된 레이어에 변경 사항이 없다면 해당 레이어를 빌드하지 않고 이전에 빌드된 것을 사용한다. 즉, 이미지를 빌드할 때 캐시 시스템을 사용한다.
이미지를 빌드하기 전에 .dockerignore 파일 생성:
touch .dockerignore
다음 내용 추가:
venv
.env
.git
.dockerignore 파일: 도커파일에 정의된 명령을 실행할 때 제외할 파일과 폴더를 지정한다.
애플리케이션 이미지를 빌드하려면 루트 디렉터리에서 다음 명령 실행:
docker build -t event-planner-api .
위 명령은 event-planner-api라는 태그를 사용해 현재 디렉토리를 이미지로 빌드하라는 의미다.
명령을 실행하면 빌드 프로세스가 시작되며 도커파일에 정의한 명령셋이 실행된다.
애플리케이션 이미지를 빌드했으니 다음 명령을 사용해 몽고DB 이미지를 pull 한다.
docker pull mongo
몽고DB 이미지를 풀해서 API container가 접근할 수 있는 데이터베이스 컨테이너를 생성했다.
도커 컨테이너는 별도의 네트워크 설정을 사용하며 기본 설정에서는 localhost 주소로 연결하는 것을 허용하지 않는다.
docker pull: 레지스트리에서 이미지를 다운로드한다. 별도로 지정하지 않으면 도커 허브 레지스트리에서 이미지를 다운로드한다.
애플리케이션 배포를 담당하는 파일 생성. 여기서 사용할 docker-compose.yml은 API 서비스와 몽고DB 데이터베이스 서비스로 구성된다. 먼저 루트 디렉토리에 다음과 같이 구성 파일을 만든다.
touch docker-compose.yml
내용 추가:
version: "3"
services:
api:
build: .
image: event-planner-api:latest
// build 필드는 현재 디렉토리에 있는 도커파일을 기준으로 event-planner-api:latest 이미지를 빌드한다.
ports:
- "8000:8000"
// 8000번 포트를 노출해서 HTTP를 통해 API에 접근하도록 한다.
evn_file:
- .env.prod
// .env.prod 파일을 환경 파일로 설정한다. 다음과 같이 환경파일 대신 환경변수를 설정할 수도 있다.
environment:
- DATABASE_URL=mongodb://database:27017/planner
- SECRET_KEY = secretkey
// 환경 변수를 배포 서비스에 추가할 때 일반적으로 사용되는 방식
database:
image: mongo:5.0.15
ports:
- "27017"
volumes:
- data:/data/db
volumes:
data:
services 섹션에 API 서비스(api)와 데이터베이스 서비스(database)를 정의한다.
데이터베이스 서비스의 설정 내용:
DATABASE_URL=mongodb://database:27017/planner
SECRET_KEY = NOTSTRONGENOUGH!
앞서 구성 파일에서 정의한 몽고DB 서비스명(database)을 사용해 DATABASE_URL을 설정한다.docker-compose up -d
명령을 입력하면 서비스가 분리 모드(detached mode)로 실행된다. 애플리케이션 서비스가 생성 및 배포됐다.docker ps
명령을 입력하면 실행되고 있는 컨테이너가 포트 번호와 함께 표시된다. GET 요청을 보내 애플리케이션이 제대로 실행되는지 확인:curl -X 'GET' 'http://localhost:8000/event/' -H 'accept: application/json'
요청에 대한 응답이 오므로 배포한 애플리케이션이 제대로 실행되고 있음을 알 수 있다.계정 생성으로 데이터베이스가 제대로 작동하는지 확인:
curl -X 'POST' 'http://localhost:8000/user/signup' -H 'accet: application/json' -H 'Content-Type: application/json' -d '{ "email": "fastapi@packt.com", "password": "strong!!!" }'
서버 중단하려면 루트 디렉토리에서 다음 명령 실행
docker-compose down
빌드하고 배포한 도커 이미지를 다른 가상 머신이나 클라우드, AWS 같은 서버리스 플랫폼에 배포할 수 있다.
이때 일반적으로 필요한 것이 바로 서버리스 플랫폼이 제공하는 개인 레지스트리이다. 이 레지스트리에 도커 이미지를 푸시해서 사용하면 된다. 서버리스 플랫폼(클라우드 서비스)에 따라 배포 과정이 다르다.
앞서 나온 배포 방법은 도커 이미지를 일반 PC나 물리 서버에 설치할 때 사용된다.
구글 클라우드나 AWS 같은 플랫폼은 데이터베이스 컨테이너 운영을 위한 서비스를 제공하지만 비용이 많이 들고 관리가 어렵다.