Docker 컨테이너로 FastAPI 프로덕션 배포하기 - 코드픽 블로그
Docker 컨테이너로 FastAPI 프로덕션 배포하기
기술 가이드

Docker 컨테이너로 FastAPI 프로덕션 배포하기

2026년 3월 11일 45 views by 코드벤터

Docker 컨테이너로 FastAPI 프로덕션 배포하기

안녕하세요, 코드픽(codepick.kr) 독자 여러분!

현대적인 웹 개발에서 빠른 개발 속도와 높은 성능은 필수적인 요소가 되었습니다. 이러한 요구사항을 충족시키기 위해 Python 진영에서는 비동기 웹 프레임워크인 FastAPI가 큰 주목을 받고 있습니다. FastAPI는 Python 3.7+의 표준 타입 힌트를 기반으로 하여 높은 개발 생산성과 뛰어난 성능을 자랑하며, 자동 API 문서 생성 기능까지 제공하여 개발자 경험을 혁신하고 있습니다.

하지만 아무리 훌륭한 애플리케이션이라도 실제 사용자에게 서비스되기 위해서는 안정적이고 효율적인 배포(Deployment) 과정이 필수적입니다. 이때 등장하는 것이 바로 Docker입니다. Docker는 애플리케이션과 그 모든 종속성을 컨테이너라는 독립적인 패키지로 묶어, 어떤 환경에서든 일관되게 실행될 수 있도록 돕는 강력한 도구입니다. 개발 환경과 프로덕션 환경 간의 불일치 문제를 해결하고, 확장성이식성을 크게 향상시킵니다.

이 포스트에서는 FastAPI 애플리케이션을 Docker 컨테이너를 사용하여 프로덕션 환경에 배포하는 실전 가이드를 제공합니다. 단순한 uvicorn 실행을 넘어, GunicornNginx를 조합하여 안정성성능을 극대화하는 방법을 단계별로 살펴보겠습니다. 이 글을 통해 여러분의 FastAPI 서비스를 성공적으로 프로덕션 환경에 안착시킬 수 있기를 바랍니다.

1. 왜 FastAPI와 Docker인가?

본격적인 배포에 앞서, 우리가 왜 이 두 기술 스택을 선택했는지 그 이유를 명확히 이해하는 것이 중요합니다.

1.1. FastAPI의 매력

FastAPI는 다음과 같은 특징으로 인해 현대 웹 서비스 개발에 이상적인 선택지로 떠오르고 있습니다.

  • 높은 성능: Starlette를 기반으로 하여 Node.js나 Go와 견줄 만한 뛰어난 I/O 성능을 제공합니다. 이는 특히 비동기 작업을 많이 수행하는 API 서비스에 큰 장점입니다.
  • 쉬운 사용성 및 생산성: Python 타입 힌트를 적극적으로 활용하여 코드 자동 완성, 타입 체크, 데이터 유효성 검사 등을 지원합니다. 이는 버그를 줄이고 개발 속도를 높여줍니다.
  • 자동 문서화: OpenAPI(Swagger UI) 및 ReDoc 기반의 API 문서를 자동으로 생성해 주어, 백엔드와 프론트엔드 개발 간의 협업을 원활하게 합니다.
  • 견고한 코드: Pydantic을 활용한 데이터 모델링으로 강력한 데이터 유효성 검증 기능을 제공합니다.

1.2. Docker의 힘

Docker는 애플리케이션 배포 및 관리에 혁신을 가져왔습니다.

  • 환경 일관성: "내 컴퓨터에서는 잘 되는데..."라는 말을 과거의 유물로 만듭니다. 개발, 테스트, 프로덕션 환경이 컨테이너 이미지로 동일하게 유지됩니다.
  • 격리성: 각 애플리케이션은 자신만의 컨테이너에서 실행되므로, 다른 애플리케이션과의 종속성 충돌이나 간섭 없이 독립적으로 작동합니다.
  • 이식성: 한 번 빌드된 Docker 이미지는 Docker가 설치된 어떤 시스템에서든 동일하게 실행될 수 있습니다. 클라우드 환경이든 온프레미스든 관계없습니다.
  • 확장성: 컨테이너는 가볍고 빠르게 생성/삭제될 수 있어, 서비스의 부하에 따라 유연하게 인스턴스를 늘리거나 줄일 수 있습니다. 이는 마이크로서비스 아키텍처 구축에도 핵심적인 역할을 합니다.
  • 간소화된 종속성 관리: 애플리케이션이 필요로 하는 모든 라이브러리와 런타임을 컨테이너 이미지 안에 포함시켜 관리합니다.

2. FastAPI 애플리케이션 준비

가장 먼저, 배포할 간단한 FastAPI 애플리케이션을 준비해봅시다. 프로젝트 루트 디렉토리에 main.pyrequirements.txt 파일을 생성합니다.

2.1. main.py

python
# main.py
from fastapi import FastAPI
import uvicorn

app = FastAPI(
    title="CodePick FastAPI App",
    description="Docker 컨테이너로 배포하는 FastAPI 예제",
    version="0.1.0",
)

@app.get("/", summary="루트 엔드포인트")
async def read_root():
    """
    서비스의 루트 경로입니다.
    간단한 환영 메시지를 반환합니다.
    """
    return {"message": "Hello CodePick! FastAPI is running in Docker."}

@app.get("/items/{item_id}", summary="아이템 조회")
async def read_item(item_id: int, q: str = None):
    """
    특정 ID의 아이템을 조회합니다.
    선택적으로 쿼리 파라미터 `q`를 받을 수 있습니다.
    """
    return {"item_id": item_id, "q": q}

@app.get("/health", summary="헬스 체크 엔드포인트")
async def health_check():
    """
    서비스의 상태를 확인하기 위한 헬스 체크 엔드포인트입니다.
    """
    return {"status": "ok"}

if __name__ == "__main__":
    # 개발 환경에서 직접 실행할 때 사용합니다.
    # 프로덕션 환경에서는 Gunicorn + Uvicorn Worker 조합을 사용합니다.
    uvicorn.run(app, host="0.0.0.0", port=8000)

이 코드는 두 개의 간단한 GET 엔드포인트를 가진 FastAPI 애플리케이션입니다. / 경로에서는 환영 메시지를, /items/{item_id} 경로에서는 경로 파라미터와 쿼리 파라미터를 받아 반환합니다. 또한, /health 엔드포인트를 추가하여 컨테이너의 헬스 체크에 활용할 수 있도록 했습니다.

2.2. requirements.txt

FastAPI와 Uvicorn을 설치하기 위한 의존성 파일을 생성합니다.

code
# requirements.txt
fastapi==0.111.0
uvicorn[standard]==0.29.0

uvicorn[standard]는 Uvicorn 서버를 실행하는 데 필요한 모든 표준 의존성(예: httptools, watchfiles, python-dotenv)을 포함합니다.

3. Dockerfile 작성: 컨테이너 이미지 빌드

이제 FastAPI 애플리케이션을 Docker 이미지로 빌드하기 위한 Dockerfile을 작성할 차례입니다. 프로덕션 환경에서는 이미지 크기를 최적화하고 보안을 강화하기 위해 **멀티스테이지 빌드(Multi-stage Build)**를 사용하는 것이 일반적입니다.

프로젝트 루트 디렉토리에 Dockerfile 파일을 생성합니다.

dockerfile
# Dockerfile

# --- 빌더 스테이지 ---
# Python 런타임이 포함된 이미지 사용 (빌드 환경)
FROM python:3.9-slim-buster AS builder

# 작업 디렉토리 설정
WORKDIR /app

# 시스템 의존성 설치 (필요한 경우)
# RUN apt-get update && apt-get install -y --no-install-recommends \
#     build-essential \
#     # 필요한 다른 라이브러리 추가 \
#     && rm -rf /var/lib/apt/lists/*

# 파이썬 의존성 파일 복사 및 설치
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip \
    && pip install --no-cache-dir -r requirements.txt

# --- 최종 애플리케이션 스테이지 ---
# 더 가볍고 보안에 유리한 런타임 전용 이미지 사용
FROM python:3.9-slim-buster AS final

# 작업 디렉토리 설정
WORKDIR /app

# 빌더 스테이지에서 설치된 파이썬 의존성 복사
COPY --from=builder /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages
COPY --from=builder /usr/local/bin/gunicorn /usr/local/bin/gunicorn
COPY --from=builder /usr/local/bin/uvicorn /usr/local/bin/uvicorn

# 애플리케이션 코드 복사
COPY . .

# 컨테이너 실행 시 사용할 포트 명시
EXPOSE 8000

# 컨테이너가 시작될 때 실행할 명령어
# Gunicorn을 사용하여 Uvicorn Worker를 관리하고, FastAPI 애플리케이션을 실행
# --workers: 워커 프로세스 수 (CPU 코어 수 * 2 + 1 또는 CPU 코어 수 * 1.5 + 1 이 일반적)
# --worker-class: 사용할 워커 클래스 (FastAPI는 UvicornWorker 사용)
# --bind: Gunicorn이 리스닝할 주소와 포트
CMD ["gunicorn", "main:app", "--workers", "4", "--worker-class", "uvicorn.workers.UvicornWorker", "--bind", "0.0.0.0:8000"]

3.1. .dockerignore 파일 추가

Docker 이미지를 빌드할 때 불필요한 파일(예: .git, __pycache__, .venv)이 이미지에 포함되는 것을 방지하기 위해 .dockerignore 파일을 생성합니다. 이는 이미지 크기를 줄이고 빌드 속도를 향상시킵니다.

code
# .dockerignore
.git
.venv
__pycache__
*.pyc
*.log
.DS_Store
*.env
venv/

3.2. Gunicorn + Uvicorn Workers: 프로덕션 환경을 위한 선택

위 Dockerfile의 CMD 부분을 보면 uvicorn.run() 대신 gunicorn을 사용하고 있습니다. 왜 그럴까요?

  • Uvicorn (단독): Uvicorn은 ASGI(Asynchronous Server Gateway Interface) 서버로서 비동기 웹 애플리케이션을 효율적으로 실행할 수 있습니다. 개발 환경에서는 uvicorn main:app --reload와 같이 사용하기 매우 편리합니다. 하지만 단독으로 사용될 경우, 하나의 프로세스에서만 작동하며 워커 관리, 로드 밸런싱, 요청 재시작 등의 기능을 제공하지 않아 프로덕션 환경에 적합하지 않습니다.
  • Gunicorn + Uvicorn Workers: Gunicorn은 Python WSGI HTTP 서버로, 워커 프로세스를 관리하는 데 특화되어 있습니다. Gunicorn은 마스터 프로세스로서 여러 개의 워커 프로세스를 생성하고 관리하며, 각 워커는 Uvicorn을 사용하여 FastAPI 애플리케이션을 실행합니다. 이 조합은 다음과 같은 이점을 제공합니다.
    • 안정성: 워커 중 하나에 문제가 발생해도 Gunicorn이 자동으로 해당 워커를 재시작하여 서비스 중단을 최소화합니다.
    • 리소스 활용: 여러 개의 워커 프로세스를 통해 서버의 멀티 코어 CPU를 효율적으로 활용하여 동시성 처리 능력을 높입니다.
    • 로드 밸런싱: Gunicorn이 들어오는 요청을 여러 워커에 분산하여 처리합니다.

다음 표는 Uvicorn 단독 실행과 Gunicorn + Uvicorn Worker 조합의 주요 특징을 비교합니다.

특징Uvicorn (단독)Gunicorn + Uvicorn Workers
**용도**개발 환경, 간단한 테스트프로덕션 환경, 높은 동시성 처리
**워커 관리**단일 프로세스 (또는 `reload` 모드)Gunicorn이 여러 Uvicorn 워커 프로세스 관리
**동시성**ASGI 서버 자체의 비동기 처리여러 워커를 통한 병렬 처리 및 비동기 처리
**안정성**워커 장애 시 전체 서비스 영향 가능워커 장애 시 Gunicorn이 자동으로 재시작, 안정성 높음
**리소스 활용**단일 코어 위주멀티 코어 CPU 활용에 최적화
**복잡도**낮음높음 (Gunicorn 설정 추가)
**권장 환경**개발, 소규모 내부 서비스대규모 트래픽이 예상되는 프로덕션 서비스

따라서 프로덕션 환경에서는 Gunicorn과 Uvicorn Worker를 함께 사용하는 것이 일반적이며 권장됩니다.

4. Docker Compose를 이용한 서비스 오케스트레이션

단일 컨테이너로 FastAPI 애플리케이션을 배포할 수도 있지만, 실제 프로덕션 환경에서는 리버스 프록시(Reverse Proxy) 역할을 하는 Nginx와 같은 웹 서버를 함께 사용하는 것이 일반적입니다. Nginx는 SSL/TLS 종료, 로드 밸런싱, 정적 파일 서빙 등의 기능을 제공하여 애플리케이션 서버의 부하를 줄이고 보안을 강화합니다.

Docker Compose는 여러 개의 Docker 컨테이너를 함께 정의하고 실행할 수 있도록 돕는 도구입니다. docker-compose.yml 파일을 통해 서비스 간의 종속성, 네트워크, 볼륨 등을 한 번에 설정할 수 있습니다.

4.1. nginx.conf 설정

Nginx 컨테이너가 사용할 설정 파일을 생성합니다. 프로젝트 루트 디렉토리에 nginx라는 새 디렉토리를 만들고 그 안에 nginx.conf 파일을 생성합니다.

nginx
# nginx/nginx.conf
worker_processes auto; # CPU 코어 수에 맞게 워커 프로세스 자동 설정

events {
    worker_connections 1024; # 각 워커가 처리할 수 있는 최대 동시 연결 수
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;

    server {
        listen 80; # 80 포트로 들어오는 요청을 수신
        server_name localhost; # 실제 서비스 도메인으로 변경 (예: api.yourdomain.com)

        # FastAPI 애플리케이션으로 모든 요청을 프록시
        location / {
            # web은 docker-compose.yml에 정의된 FastAPI 서비스 이름입니다.
            # 8000은 FastAPI 애플리케이션이 컨테이너 내부에서 리스닝하는 포트입니다.
            proxy_pass http://web:8000;

            # 클라이언트의 실제 IP 주소 및 호스트 정보를 전달
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;

            # 프록시 응답 시 리다이렉트 URL을 변경하지 않음
            proxy_redirect off;

            # WebSocket 지원을 위한 설정 (필요한 경우)
            # proxy_http_version 1.1;
            # proxy_set_header Upgrade $http_upgrade;
            # proxy_set_header Connection "upgrade";
        }

        # 정적 파일 서빙 (필요한 경우)
        # location /static/ {
        #     alias /app/static/; # FastAPI 컨테이너 내의 정적 파일 경로
        #     expires 30d;
        #     add_header Cache-Control "public, no-transform";
        # }
    }
}

이 Nginx 설정은 80번 포트로 들어오는 모든 HTTP 요청을 web이라는 이름의 서비스(FastAPI 컨테이너)의 8000번 포트로 전달(프록시)합니다. 또한, 클라이언트의 실제 IP 주소와 호스트 정보를 FastAPI 애플리케이션으로 전달하여 로깅 및 보안 분석에 활용할 수 있도록 합니다.

4.2. docker-compose.yml 작성

이제 FastAPI 애플리케이션과 Nginx 리버스 프록시를 함께 실행하기 위한 docker-compose.yml 파일을 프로젝트 루트 디렉토리에 생성합니다.

yaml
# docker-compose.yml
version: 3.8 # Docker Compose 파일 형식 버전 지정

services:
  # FastAPI 애플리케이션 서비스 정의
  web:
    build: . # 현재 디렉토리의 Dockerfile을 사용하여 이미지 빌드
    container_name: fastapi_app_container # 컨테이너 이름 지정
    env_file: # 환경 변수 파일을 사용하여 컨테이너 내 환경 변수 설정
      - ./.env
    # ports:
    #   - "8000:8000" # Nginx가 리버스 프록시하므로, 외부로 직접 포트를 노출하지 않습니다.
    networks: # 컨테이너가 연결될 네트워크 지정
      - app_network
    restart: always # 컨테이너가 종료되면 항상 재시작

  # Nginx 리버스 프록시 서비스 정의
  nginx:
    image: nginx:latest # Docker Hub에서 최신 Nginx 이미지 사용
    container_name: nginx_proxy_container # 컨테이너 이름 지정
    ports:
      - "80:80" # 호스트의 80번 포트를 컨테이너의 80번 포트와 연결 (외부 접근용)
      # - "443:443" # HTTPS를 위해 443 포트도 필요할 수 있습니다.
    volumes: # 호스트 파일 시스템의 디렉토리를 컨테이너 내부로 마운트
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro # Nginx 설정 파일 마운트 (읽기 전용)
      # - ./certbot/conf:/etc/letsencrypt # Lets Encrypt 인증서 마운트 (HTTPS용)
      # - ./certbot/www:/var/www/certbot # Lets Encrypt 챌린지용
    depends_on: # web 서비스가 먼저 시작된 후 nginx 서비스가 시작되도록 종속성 설정
      - web
    networks: # 컨테이너가 연결될 네트워크 지정
      - app_network
    restart: always # 컨테이너가 종료되면 항상 재시작

# 사용자 정의 네트워크 정의
networks:
  app_network:
    driver: bridge # 브리지 네트워크 드라이버 사용

4.3. 환경 변수 관리 (.env)

프로덕션 환경에서는 데이터베이스 연결 정보, API 키 등 민감한 정보를 코드에 직접 포함하지 않고 환경 변수를 통해 관리해야 합니다. docker-compose.yml에서 env_file 옵션을 사용하여 .env 파일에 정의된 환경 변수를 컨테이너에 주입할 수 있습니다.

프로젝트 루트 디렉토리에 .env 파일을 생성합니다.

code
# .env
APP_ENV=production
DATABASE_URL=postgresql://user:password@db_host:5432/dbname
API_KEY=your_secret_api_key_here

FastAPI 애플리케이션 내에서는 os.getenv("APP_ENV")와 같이 이 환경 변수들에 접근할 수 있습니다.

5. 배포 및 테스트

이제 모든 준비가 끝났습니다. Docker Compose를 사용하여 서비스를 빌드하고 실행해봅시다.

5.1. 서비스 빌드 및 실행

프로젝트 루트 디렉토리에서 다음 명령어를 실행합니다.

bash
docker-compose up --build -d
  • up: docker-compose.yml 파일에 정의된 서비스를 시작합니다.
  • --build: 이미지 캐시가 있더라도 모든 서비스의 이미지를 다시 빌드합니다. (첫 실행 시 또는 Dockerfile 변경 시 유용)
  • -d: 서비스를 백그라운드에서 데몬 형태로 실행합니다.

명령어를 실행하면 Docker 이미지가 빌드되고, fastapi_app_containernginx_proxy_container 두 개의 컨테이너가 시작될 것입니다.

5.2. 서비스 상태 확인

컨테이너가 정상적으로 실행 중인지 확인합니다.

bash
docker-compose ps

출력 예시:

code
      Name                     Command               State         Ports
---------------------------------------------------------------------------------
fastapi_app_container   gunicorn main:app --wor ...   Up      8000/tcp
nginx_proxy_container   /docker-entrypoint.sh ngi ...   Up      0.0.0.0:80->80/tcp

두 컨테이너 모두 StateUp으로 표시되면 정상적으로 실행 중인 것입니다.

5.3. 로그 확인

컨테이너의 로그를 확인하여 애플리케이션이 정상적으로 작동하는지 검증할 수 있습니다.

bash
docker-compose logs -f web
docker-compose logs -f nginx

5.4. 웹 브라우저에서 접속

웹 브라우저를 열고 http://localhost/ 또는 여러분의 서버 IP 주소로 접속해 보세요. FastAPI 애플리케이션의 환영 메시지를 볼 수 있을 것입니다.

  • http://localhost/
  • http://localhost/items/123?q=example
  • http://localhost/docs (자동 생성된 Swagger UI 문서)
  • http://localhost/redoc (자동 생성된 ReDoc 문서)
  • http://localhost/health

모든 엔드포인트가 정상적으로 작동하고, Nginx를 통해 요청이 프록시되고 있음을 확인할 수 있습니다.

5.5. 서비스 중지 및 삭제

서비스를 중지하려면:

bash
docker-compose stop

서비스를 중지하고 모든 컨테이너, 네트워크, 볼륨을 삭제하려면:

bash
docker-compose down

이미지까지 삭제하려면 docker-compose down --rmi all을 사용합니다.

6. 고급 배포 전략 및 최적화

FastAPI와 Docker Compose를 이용한 기본 배포는 완료했지만, 실제 프로덕션 환경에서는 몇 가지 추가적인 고려사항과 최적화 기

개발 의뢰 상담

AI 서비스나 플랫폼 개발을
고민 중이신가요?

CodePick에서는
기획 → 개발 → 운영까지 함께합니다.
아이디어만 있어도 상담 가능합니다.

CodeVenter 개발팀이 직접 담당 · 1~2 영업일 내 회신

✓ 스타트업 MVP 개발✓ AI 서비스 개발✓ 웹 플랫폼 개발✓ 기업 시스템 구축✓ 모바일 앱 개발

AI Development Studio

코드픽 by 코드벤터

  • 대표: 윤승환 · 사업자등록번호: 121-57-64983
  • 대구광역시 중구 국채보상로 586, 16층 · info@codeventer.com

© 2025 코드벤터. All rights reserved.