正在加载,请稍候…

FastAPI Production Deployment: Docker, Gunicorn, and Nginx

Deploy FastAPI applications to production — async workers, Gunicorn configuration, Docker multi-stage builds, Nginx reverse proxy, health checks, and zero-downtime deployments.

FastAPI in Production

FastAPI is production-ready but needs proper configuration — the right ASGI server, process management, and infrastructure setup.

ASGI Server Options

Uvicorn (Development)

uvicorn main:app --reload --host 0.0.0.0 --port 8000

Gunicorn + Uvicorn Workers (Production)

pip install gunicorn uvicorn[standard]

gunicorn main:app   --workers 4   --worker-class uvicorn.workers.UvicornWorker   --bind 0.0.0.0:8000   --timeout 30   --keep-alive 5   --log-level info

Worker formula: (2 × CPU cores) + 1

Application Structure

fastapi-app/
├── app/
│   ├── __init__.py
│   ├── main.py          # App factory
│   ├── config.py        # Settings (pydantic-settings)
│   ├── database.py      # DB connection
│   ├── dependencies.py  # Shared deps
│   ├── models/
│   ├── schemas/
│   ├── routers/
│   └── services/
├── tests/
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
└── gunicorn.conf.py

Configuration with pydantic-settings

# app/config.py
from pydantic_settings import BaseSettings
from functools import lru_cache

class Settings(BaseSettings):
    app_name: str = "My API"
    debug: bool = False
    database_url: str
    redis_url: str = "redis://localhost:6379"
    secret_key: str
    allowed_hosts: list[str] = ["*"]
    
    class Config:
        env_file = ".env"
        env_file_encoding = "utf-8"

@lru_cache()
def get_settings() -> Settings:
    return Settings()

App Factory Pattern

# app/main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware
from contextlib import asynccontextmanager

from app.config import get_settings
from app.database import init_db
from app.routers import users, products, orders

settings = get_settings()

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup
    await init_db()
    yield
    # Shutdown
    # cleanup connections

def create_app() -> FastAPI:
    app = FastAPI(
        title=settings.app_name,
        debug=settings.debug,
        lifespan=lifespan,
        docs_url="/docs" if settings.debug else None,
    )
    
    app.add_middleware(
        CORSMiddleware,
        allow_origins=settings.allowed_hosts,
        allow_methods=["*"],
        allow_headers=["*"],
    )
    app.add_middleware(TrustedHostMiddleware, allowed_hosts=["*"])
    
    app.include_router(users.router, prefix="/api/v1")
    app.include_router(products.router, prefix="/api/v1")
    
    return app

app = create_app()

Dockerfile (Multi-Stage)

# Build stage
FROM python:3.12-slim as builder

WORKDIR /build
RUN pip install poetry
COPY pyproject.toml poetry.lock ./
RUN poetry export -f requirements.txt --output requirements.txt --without-hashes

# Production stage
FROM python:3.12-slim

WORKDIR /app

# Security: non-root user
RUN addgroup --system app && adduser --system --group app

# Install deps
COPY --from=builder /build/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY --chown=app:app ./app ./app

USER app

EXPOSE 8000

CMD ["gunicorn", "app.main:app", "--workers", "4", 
     "--worker-class", "uvicorn.workers.UvicornWorker",
     "--bind", "0.0.0.0:8000"]

Nginx Configuration

upstream fastapi_backend {
    server app:8000;
    keepalive 32;
}

server {
    listen 80;
    server_name api.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name api.example.com;

    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;

    client_max_body_size 10M;

    location / {
        proxy_pass http://fastapi_backend;
        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_read_timeout 60s;
        proxy_connect_timeout 10s;
    }

    location /health {
        proxy_pass http://fastapi_backend/health;
        access_log off;
    }
}

Health Check Endpoint

from fastapi import APIRouter
from sqlalchemy import text
from app.database import get_db

router = APIRouter()

@router.get("/health")
async def health_check(db=Depends(get_db)):
    try:
        await db.execute(text("SELECT 1"))
        db_status = "healthy"
    except Exception:
        db_status = "unhealthy"
    
    return {
        "status": "healthy" if db_status == "healthy" else "degraded",
        "database": db_status,
        "version": "1.0.0",
    }

Gunicorn Config File

# gunicorn.conf.py
import multiprocessing

bind = "0.0.0.0:8000"
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "uvicorn.workers.UvicornWorker"
worker_connections = 1000
timeout = 30
keepalive = 5
preload_app = True
accesslog = "-"
errorlog = "-"
loglevel = "info"

Zero-Downtime Deployment

# Rolling update with Docker Swarm
docker service update   --image myapp:v2   --update-delay 10s   --update-parallelism 1   myapp

# Or with Kubernetes
kubectl set image deployment/fastapi-app app=myapp:v2
kubectl rollout status deployment/fastapi-app

Performance Tips

  • Enable response compression with GZipMiddleware
  • Use async database drivers (asyncpg, motor)
  • Add Redis caching for expensive queries
  • Profile with py-spy or pyinstrument
  • Set proper connection pool sizes