[FastAPI] Deployment (Uvicorn, Gunicorn, Docker)
정의
FastAPI 배포는 ASGI 서버 (Uvicorn) + 프로세스 관리자 (Gunicorn 또는 Uvicorn 자체) + 컨테이너 (Docker) + 오케스트레이터 (Kubernetes/ECS) 계층으로 구성됩니다. 각 계층의 역할과 트레이드오프를 정확히 이해해야 프로덕션 안정성이 확보됩니다.
구성 계층
[Client]
↓ HTTPS
[Load Balancer / CDN (ALB, CloudFront, Nginx)]
↓ HTTP
[프로세스 관리자 (Gunicorn or Uvicorn --workers)]
↓ multiple processes
[Uvicorn worker (ASGI server, per process)]
↓
[FastAPI app]
Uvicorn 단독 (개발/간단 프로덕션)
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
--workers: 프로세스 수 (기본 1)--reload: 개발용 자동 재시작 (프로덕션 X)--log-level: debug/info/warning/error/critical--access-log/--no-access-log--proxy-headers --forwarded-allow-ips="*": 리버스 프록시 뒤 배포 시
Uvicorn —workers vs Gunicorn
- Uvicorn —workers: multiprocessing 프로세스 매니저. 단순, 별도 의존성 없음.
- Gunicorn + Uvicorn workers: Gunicorn 이 프로세스 라이프사이클 (graceful reload, timeout, pre-fork) 관리, Uvicorn 이 각 프로세스 안에서 ASGI 서버 역할.
Gunicorn 의 장점: signal 처리 (SIGHUP graceful reload), timeout, memory leak worker 재활용 등 성숙한 기능.
Gunicorn + Uvicorn worker
pip install gunicorn uvicorn[standard]
gunicorn myapp.main:app \
-w 4 \
-k uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8000 \
--timeout 120 \
--graceful-timeout 30 \
--keep-alive 5 \
--access-logfile - \
--error-logfile -
-w: worker 프로세스 수. 일반 공식2 * CPU + 1, async 워크로드는 CPU 개수 근방.-k uvicorn.workers.UvicornWorker: Uvicorn 을 worker 로--timeout: worker 가 응답 못 하면 kill (기본 30초)--graceful-timeout: shutdown 시 진행 중 요청 완료 대기--keep-alive: HTTP keep-alive 연결 유지 초
UvicornWorker vs UvicornH11Worker
- UvicornWorker (기본):
httptools+uvloop로 성능 최적 - UvicornH11Worker: pure Python
h11. 특수 환경 (uvloop 미지원) 용
Worker 수 결정
CPU-bound async 앱:
- 이벤트 루프가 CPU 를 대부분 소비 -> worker 수 = CPU 개수
IO-bound async 앱:
- 이벤트 루프가 대부분 대기 -> worker 수 = CPU 개수 (많이 늘려도 이득 적음)
Sync 앱 (blocking IO):
- Worker 하나에 스레드풀 40 워커. worker 수 = CPU 개수
- 총 동시 요청 처리 = workers × threadpool
메모리 제약:
- 각 worker 는 별도 프로세스 -> 앱 메모리 × worker 수
- ML 모델 로드 앱은 worker 수를 신중히 (또는 shared memory)
신호 처리 & graceful shutdown
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app: FastAPI):
# startup
app.state.db = await create_engine()
app.state.http = httpx.AsyncClient()
yield
# shutdown
await app.state.http.aclose()
await app.state.db.dispose()
app = FastAPI(lifespan=lifespan)
Gunicorn 이 SIGTERM 을 받으면 새 요청 accept 중지, 진행 중 요청 완료 대기, lifespan shutdown 실행.
Kubernetes 는 SIGTERM 을 pod 종료 전에 보냄. terminationGracePeriodSeconds 를 Gunicorn --graceful-timeout 보다 크게.
Docker
최소 Dockerfile
FROM python:3.12-slim
WORKDIR /app
# 의존성 캐시
COPY pyproject.toml uv.lock ./
RUN pip install --no-cache-dir uv && \
uv sync --frozen --no-dev
# 소스
COPY src /app/src
# non-root user
RUN useradd -m app && chown -R app:app /app
USER app
EXPOSE 8000
CMD ["uv", "run", "uvicorn", "myapp.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
Multi-stage build (이미지 크기 최소화)
# --- builder ---
FROM python:3.12-slim AS builder
WORKDIR /app
RUN pip install uv
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev
# --- runtime ---
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /app/.venv /app/.venv
COPY src /app/src
ENV PATH="/app/.venv/bin:$PATH"
CMD ["uvicorn", "myapp.main:app", "--host", "0.0.0.0", "--port", "8000"]
이미지 스택 선택
python:3.12-slim: 표준, ~130 MB base. glibc 사용.python:3.12-alpine: 가벼움 (~55 MB base). musl libc, 일부 wheel 미지원.gcr.io/distroless/python3-debian12: 최소, non-root, shell 없음. 디버그 어려움.
추천: python:3.12-slim + multi-stage 로 100-200 MB 최종 이미지.
Kubernetes 배포
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
terminationGracePeriodSeconds: 60
containers:
- name: app
image: myrepo/myapp:1.0
ports: [{containerPort: 8000}]
resources:
requests: {cpu: 500m, memory: 512Mi}
limits: {cpu: 2, memory: 2Gi}
livenessProbe:
httpGet: {path: /health/live, port: 8000}
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet: {path: /health/ready, port: 8000}
initialDelaySeconds: 5
periodSeconds: 5
startupProbe:
httpGet: {path: /health/startup, port: 8000}
failureThreshold: 30
periodSeconds: 5
lifecycle:
preStop:
exec:
command: ["sleep", "5"] # LB 에서 빠지는 시간
Probe 엔드포인트
@app.get("/health/live", include_in_schema=False)
def liveness():
return {"status": "ok"}
@app.get("/health/ready", include_in_schema=False)
async def readiness(
db: Annotated[AsyncSession, Depends(get_async_db)]
):
await db.execute(text("SELECT 1"))
return {"status": "ok"}
@app.get("/health/startup", include_in_schema=False)
def startup():
return {"status": "ready"}
- Liveness: 앱이 살아있는가 (fail 시 재시작)
- Readiness: 트래픽을 받을 수 있는가 (fail 시 LB 에서 제거)
- Startup: 초기화 중 (다른 probe 지연)
Reverse Proxy 설정
Nginx
upstream fastapi {
server 127.0.0.1:8000;
keepalive 32;
}
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/ssl/cert.pem;
ssl_certificate_key /etc/ssl/key.pem;
location / {
proxy_pass http://fastapi;
proxy_http_version 1.1;
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;
proxy_set_header Upgrade $http_upgrade; # WebSocket
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600;
proxy_buffering off; # SSE
}
}
FastAPI 는 --proxy-headers --forwarded-allow-ips="*" 로 X-Forwarded-* 신뢰.
AWS ALB
- Health check path:
/health/ready - Idle timeout: 60초 (WebSocket 은 확장)
- Stickiness: WebSocket 이 프로세스 로컬 상태 유지 시
로깅 & 관측
구조화 로그
import logging
import json
from pythonjsonlogger import jsonlogger
handler = logging.StreamHandler()
handler.setFormatter(jsonlogger.JsonFormatter(
"%(asctime)s %(name)s %(levelname)s %(message)s"
))
logging.basicConfig(handlers=[handler], level=logging.INFO)
CloudWatch, Datadog, Loki 등이 JSON 로그를 파싱 가능.
OpenTelemetry
pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap -a install
opentelemetry-instrument uvicorn myapp.main:app
FastAPI, SQLAlchemy, HTTP client 등 자동 계측.
Sentry
import sentry_sdk
from sentry_sdk.integrations.fastapi import FastApiIntegration
sentry_sdk.init(
dsn="https://xxx@sentry.io/yyy",
traces_sample_rate=0.1,
integrations=[FastApiIntegration()],
)
환경 변수 & Secrets
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str
secret_key: str
debug: bool = False
class Config:
env_file = ".env"
Kubernetes: Secret 리소스로 마운트. AWS: Secrets Manager / SSM Parameter Store.
성능 팁
--http httptools,--loop uvloop: Uvicorn 기본이지만uvicorn[standard]필요--limit-concurrency N: 동시 요청 상한 (backpressure)--limit-max-requests N: worker 재시작 트리거 (메모리 누수 완화)- Prefork with LazyLoad: ML 모델은 fork 후 로드 (COW 활용) 또는 각 worker 개별 로드
- HTTP/2: Uvicorn 은 미지원. Hypercorn 사용 또는 프록시 (Nginx/Envoy) 에서 종단
함정
WARNING
--reload 는 개발만. 프로덕션에서 코드 변경 감시로 부하 + 리소스 누수.
CAUTION
worker_class 잘못 지정. Sync worker (sync) 는 FastAPI 부적합. 반드시 uvicorn.workers.UvicornWorker.
WARNING
Signal 처리 misconfig. Gunicorn --graceful-timeout < Kubernetes terminationGracePeriodSeconds 여야 in-flight 요청 완료.
IMPORTANT
모델 로딩은 lifespan startup. Endpoint 별 매 요청 로드는 재앙. 앱 시작 시 한 번, app.state 에 저장.
CAUTION
컨테이너 안에서 USER root 유지 위험. non-root user 사용, non-privileged port (8000+) 사용.
관련 위키
- FastAPI - 상위 개요
- FastAPI Async - worker 종류 결정
- FastAPI Middleware - 프록시 헤더
- FastAPI WebSockets - 프록시 설정
- ECS Fargate - 컨테이너 오케스트레이션
- EKS - Kubernetes
- ALB/NLB - 로드밸런서
- Container Image Best Practices
- OCI Image
이 글의 용어 (9개)
- [AWS] ALB vs NLB: L7 vs L4 로드 밸런서cloud
- 정의 | | ALB | NLB | (Classic ELB) | |---|---|---|---| | Layer | L7 (HTTP) | L4 (TCP/UDP) | L4 + L7 (…
- [AWS] ECS + Fargate: 컨테이너 오케스트레이션cloud
- 정의 ECS (Elastic Container Service) = AWS 의 컨테이너 오케스트레이션. K8s 보다 단순하고 AWS 네이티브. Fargate = ECS (또는 EK…
- [AWS] EKS: managed Kubernetescloud
- 정의 EKS (Elastic Kubernetes Service) = AWS 의 managed K8s control plane. worker node 는 사용자 (또는 Fargat…
- [Container] Image Best Practices: 작게, 안전하게virtualization
- 정의 컨테이너 image best practices = 작고 (small), 안전하고 (secure), 재현 가능하고 (reproducible), 서명된 (signed) imag…
- [Container] OCI Image: spec, manifest, layer 표준cloud
- 정의 OCI (Open Container Initiative) = 컨테이너 표준 (Linux Foundation, 2015). image format + runtime + dis…
- [FastAPI] Async / Sync Endpointsfastapi
- 정의 FastAPI 는 ASGI 프레임워크 이므로 endpoint 를 로 정의해 이벤트 루프에서 실행하거나, 로 정의해 스레드풀에서 실행할 수 있습니다. 어느 쪽을 선택하는지가 …
- [FastAPI] Middlewarefastapi
- 정의 FastAPI Middleware 는 모든 요청과 응답을 가로채 로깅, 인증, 압축, CORS 등 공통 처리를 하는 계층입니다. Starlette 의 ASGI middlew…
- [FastAPI] WebSocketsfastapi
- 정의 FastAPI WebSockets 는 Starlette 위에서 양방향 실시간 통신을 제공합니다. HTTP 대신 / 스킴을 쓰며, 한 연결 위에서 서버와 클라이언트가 자유롭게…
- [Python] FastAPIfastapi
- 정의 FastAPI 는 Python 3.8+ 을 위한 ASGI 기반 현대 웹/API 프레임워크 입니다. Sebastián Ramírez (tiangolo) 가 2018년 발표했고…
💬 댓글