FastAPI + Docker 프로덕션 모범 사례 (Better Stack)
Source: https://betterstack.com/community/guides/scaling-python/fastapi-docker-best-practices/ Type: Article By: Stanley Ulili Valid as of: 2026-04-26
핵심 Takeaway
- Dockerfile 레이어 설계: 변경 빈도가 낮은 것부터 → 의존성 → 코드 (캐싱 최대화)
- Pydantic 설정 +
.env파일: 환경변수로 프로덕션 설정 관리,.env.example로 예시만 git 커밋 - 헬스체크 엔드포인트:
/health구현 후 Docker 헬스체크 설정 → 자동 재시작 .dockerignore:__pycache__,.git,venv/등 불필요 파일 제외로 빌드 시간·크기 단축- 로깅 전략: 구조화된 로그 → stdout (Docker가 수집) + 요청 미들웨어로 성능·에러 추적
- DB 준비 확인:
depends_on+ 헬스체크로 데이터베이스 준비 완료 후 앱 시작 (race condition 방지) - 마이그레이션 관리: 별도 컨테이너 또는 시작 시 실행 (동시 충돌 방지)
- 수평 확장: 단일 프로세스 컨테이너 → Nginx 역프록시로 여러 인스턴스 로드 밸런싱
상세 요약
프로덕션 고려사항 총론
프로덕션 배포는 기본 컨테이너화를 넘어 성능, 보안, 안정성, 운영성을 모두 아우른다. FastAPI + Docker의 실무 체크리스트:
- 성능: 레이어 최적화, 캐싱, 다중 워커
- 보안: 환경변수 관리, 헬스체크, 취약점 스캔
- 안정성: DB 준비 확인, 마이그레이션 관리, 자동 재시작
- 운영성: 구조화된 로깅, 요청 추적, 모니터링
1. Dockerfile 기초: 레이어 최적화
캐싱 전략의 핵심: 변경 빈도 오름차순으로 배열.
나쁜 패턴 (모든 것이 함께):
FROM python:3.9-slim
WORKDIR /app
COPY . . # 코드 + 의존성 모두 복사
RUN pip install -r requirements.txt
CMD ["fastapi", "run", "main.py"]문제: 소스 1줄 변경 → pip install 레이어부터 재실행 (느림)
최적 패턴 (순차적):
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt . # 변경 빈도: 낮음
RUN pip install -r requirements.txt
COPY . . # 변경 빈도: 높음
CMD ["fastapi", "run", "main.py"]효과: 코드 변경 시 pip 재설치 스킵 → 빌드 시간 10배 단축 가능
2. 환경 설정 관리 (Environment Configuration)
Pydantic Settings + .env 패턴:
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str
debug: bool = False
log_level: str = "INFO"
class Config:
env_file = ".env"
settings = Settings()파일 관리:
.env: 실제 설정값 (개발·프로덕션 환경마다 다름) → git 무시.env.example: 필요 키만 예시 → git 커밋
장점:
- 코드 하드코딩 제거
- 환경별 설정 분리 (DEV/PROD/TEST)
- 보안: 민감한 정보(DB 비밀번호, API 키) 코드 외부 보관
3. 헬스체크 엔드포인트 & Docker 헬스 모니터링
헬스체크 엔드포인트 구현:
@app.get("/health")
async def health_check():
# DB 연결 확인, 캐시 상태 등
return {"status": "healthy"}Docker Compose 헬스체크 설정:
services:
app:
build: .
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 10s # 10초마다 확인
timeout: 5s
retries: 3 # 3회 실패 시 컨테이너 재시작
start_period: 40s # 시작 후 40초 대기동작:
- 정기적으로
/health호출 - 실패 시 자동 재시작 → 무중단 서비스
4. .dockerignore로 빌드 최적화
# 불필요한 파일 제외
__pycache__
*.pyc
.pytest_cache
.git
.gitignore
.env # 민감 정보
venv/
.venv/
.DS_Store
tests/ # 또는 멀티스테이지 빌드로 제거
README.md
*.md
효과:
- 빌드 컨텍스트 크기 축소 (50MB → 10MB)
- Docker 데몬과의 전송 시간 단축
- 최종 이미지에 불필요 파일 미포함
5. 데이터베이스 준비 확인 (Database Readiness)
문제: Race Condition
version: '3'
services:
db:
image: postgres:15
app:
build: .
depends_on:
- db # ❌ 불완전: 컨테이너 시작만 확인, 실제 준비 X→ 앱이 시작되어도 DB가 준비 안 됨 → 연결 실패
해결책: depends_on + 헬스체크:
services:
db:
image: postgres:15
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres"]
interval: 10s
retries: 5
app:
build: .
depends_on:
db:
condition: service_healthy # DB 헬스체크 대기또는 스크립트로 확인:
# prestart.sh
until pg_isready -h $DATABASE_HOST -U $DATABASE_USER; do
sleep 2
done
exec "$@" # 앱 시작6. 마이그레이션 관리 (Database Migrations)
문제: 여러 앱 인스턴스가 동시에 마이그레이션 시도 → 충돌
해결책 1: 별도 초기화 컨테이너:
services:
migrate:
build: .
command: "alembic upgrade head"
depends_on:
db:
condition: service_healthy
app:
build: .
depends_on:
migrate:
condition: service_completed_successfully해결책 2: 시작 시 일회 실행:
@app.on_event("startup")
async def startup():
# 마이그레이션 실행 (once 로직 구현)
await run_migrations()7. 로깅 전략 (Structured Logging)
stdout 출력 (Docker 수집):
import logging
import json
class JSONFormatter(logging.Formatter):
def format(self, record):
return json.dumps({
"timestamp": self.formatTime(record),
"level": record.levelname,
"message": record.getMessage(),
"logger": record.name
})
handler = logging.StreamHandler() # stdout
handler.setFormatter(JSONFormatter())
logger.addHandler(handler)요청 로깅 미들웨어:
@app.middleware("http")
async def log_requests(request: Request, call_next):
start = time.time()
response = await call_next(request)
duration = time.time() - start
logger.info(f"method={request.method} path={request.url.path} status={response.status_code} duration={duration:.3f}s")
return response장점:
- Docker 로그 캡처 (stdout/stderr)
- 중앙 로그 수집 (ELK, Splunk 등)
- 성능 메트릭 추적 (응답 시간)
- 에러 추적 및 디버깅
8. 수평 확장 (Horizontal Scaling)
문제: 단일 컨테이너 = 단일 프로세스 = 단일 워커 → 동시성 제한
해결책: 여러 인스턴스 + 리버스 프록시:
services:
nginx:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- app1
- app2
- app3
app1:
build: .
app2:
build: .
app3:
build: .nginx.conf (로드 밸런싱):
upstream fastapi {
server app1:8000;
server app2:8000;
server app3:8000;
}
server {
listen 80;
location / {
proxy_pass http://fastapi;
}
}동작:
- 클라이언트 요청 → Nginx (포트 80)
- Nginx 라운드로빈: app1, app2, app3에 분산
- 각 앱 인스턴스 단독 실행 (프로세스 충돌 없음)
- 인스턴스 추가/삭제 용이
선행 개념
이 개념을 배우기 전에 필수로 알아야 할 것:
- Module 4-2 → fastapi-docker-official-deployment: 기본 FastAPI Dockerfile 구조
- 왜?: 이 소스는 프로덕션 배포를 위한 고급 기법(환경변수, 보안, 성능 튜닝)을 다루므로, 기초를 먼저 알아야 함
- Module 5: ML 파이프라인 및 모델
- 왜?: 실제 프로덕션 사례에서 FastAPI가 훈련된 모델을 서빙하기 때문
후속 개념 (이 개념이 선행)
이 개념을 배운 후 다음 단계:
- Module 6-2 → docker-compose-multi-service-orchestration: Docker Compose로 여러 서비스 통합
- 예: “환경변수, 보안, 성능 설정을 docker-compose.yml에서 관리”
- Module 6-3 → structured-logging-fastapi: 프로덕션 환경에서의 로깅
- 예: “다중 워커 환경에서 로그 수집 및 모니터링”
연결되는 위키 페이지
- containerization — 컨테이너화 및 오케스트레이션
- docker — Docker 도구 및 모범 사례
- fastapi — FastAPI 프레임워크
- nginx — Nginx 리버스 프록시 및 로드 밸런싱
- postgresql — PostgreSQL 데이터베이스
- structured-logging — 구조화된 로깅 전략