跳到主要内容

部署

将 FastAPI 应用部署到生产环境需要考虑性能、安全、可靠性等多个方面。本章介绍常见的部署方式和最佳实践。

运行方式

Uvicorn

Uvicorn 是一个 ASGI 服务器,是运行 FastAPI 应用的标准选择:

# 开发模式(自动重载)
uvicorn main:app --reload

# 生产模式
uvicorn main:app --host 0.0.0.0 --port 8000

# 多 worker(利用多核 CPU)
uvicorn main:app --workers 4

# 完整配置
uvicorn main:app \
--host 0.0.0.0 \
--port 8000 \
--workers 4 \
--loop uvloop \
--http httptools \
--log-config log_config.json

FastAPI CLI

FastAPI 提供了便捷的命令行工具:

# 开发模式
fastapi dev main.py

# 生产模式
fastapi run main.py --workers 4

Gunicorn + Uvicorn

生产环境推荐使用 Gunicorn 作为进程管理器,Uvicorn 作为工作进程:

# 安装
pip install gunicorn uvicorn[standard]

# 运行
gunicorn main:app \
--workers 4 \
--worker-class uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8000 \
--timeout 120 \
--keep-alive 5

Gunicorn 配置文件 gunicorn.conf.py

# gunicorn.conf.py
import multiprocessing

bind = "0.0.0.0:8000"
workers = multiprocessing.cpu_count() * 2 + 1 # 推荐配置
worker_class = "uvicorn.workers.UvicornWorker"
keepalive = 120
timeout = 120

环境变量

使用 pydantic-settings

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
app_name: str = "FastAPI Application"
debug: bool = False
database_url: str
secret_key: str

class Config:
env_file = ".env"

settings = Settings()

.env 文件

# .env
APP_NAME=My API
DEBUG=false
DATABASE_URL=postgresql://user:pass@localhost/db
SECRET_KEY=your-secret-key-here

Docker 中使用环境变量

# Dockerfile
FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

# 环境变量可以在运行时设置
ENV APP_NAME="Production API"

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
# 运行时传入环境变量
docker run -e DATABASE_URL=postgresql://... -e SECRET_KEY=... my-app

Docker 部署

单容器部署

# Dockerfile
FROM python:3.11-slim as builder

WORKDIR /app

# 安装依赖
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt

# 最终镜像
FROM python:3.11-slim

WORKDIR /app

# 复制依赖
COPY --from=builder /root/.local /root/.local
ENV PATH=/root/.local/bin:$PATH

# 复制应用代码
COPY . .

# 非 root 用户
RUN useradd -m appuser
USER appuser

EXPOSE 8000

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

构建和运行:

# 构建镜像
docker build -t my-fastapi-app .

# 运行容器
docker run -p 8000:8000 my-fastapi-app

Docker Compose

# docker-compose.yml
version: '3.8'

services:
web:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://postgres:password@db:5432/mydb
- SECRET_KEY=${SECRET_KEY}
depends_on:
- db
- redis
volumes:
- ./app:/app/app

db:
image: postgres:15
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
- POSTGRES_DB=mydb
volumes:
- postgres_data:/var/lib/postgresql/data

redis:
image: redis:7-alpine

nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- web

volumes:
postgres_data:

启动:

docker-compose up -d

Nginx 反向代理

基本配置

# /etc/nginx/conf.d/fastapi.conf
upstream fastapi {
server 127.0.0.1:8000;
keepalive 32;
}

server {
listen 80;
server_name example.com;

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 Connection "";

# 超时设置
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}

HTTPS 配置

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

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

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;

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;
}
}

使用 Let's Encrypt

# 安装 certbot
sudo apt install certbot python3-certbot-nginx

# 获取证书
sudo certbot --nginx -d example.com -d www.example.com

# 自动续期
sudo certbot renew --dry-run

Systemd 服务

创建系统服务管理 FastAPI 应用:

# /etc/systemd/system/fastapi.service
[Unit]
Description=FastAPI Application
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/fastapi
Environment="PATH=/var/www/fastapi/venv/bin"
Environment="DATABASE_URL=postgresql://..."
Environment="SECRET_KEY=..."
ExecStart=/var/www/fastapi/venv/bin/gunicorn main:app \
--workers 4 \
--worker-class uvicorn.workers.UvicornWorker \
--bind 127.0.0.1:8000
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

管理服务:

# 重新加载配置
sudo systemctl daemon-reload

# 启动服务
sudo systemctl start fastapi

# 开机自启
sudo systemctl enable fastapi

# 查看状态
sudo systemctl status fastapi

# 查看日志
sudo journalctl -u fastapi -f

性能优化

Worker 数量

推荐公式:workers = (2 * CPU核心数) + 1

# 查看核心数
nproc

# 设置 worker 数量
uvicorn main:app --workers 9 # 假设 4 核

使用 uvloop 和 httptools

# 安装
pip install uvicorn[standard]

# 或单独安装
pip install uvloop httptools

Uvicorn 会自动检测并使用这些优化库。

数据库连接池

from sqlalchemy import create_engine
from sqlalchemy.pool import QueuePool

engine = create_engine(
DATABASE_URL,
poolclass=QueuePool,
pool_size=10, # 连接池大小
max_overflow=20, # 最大溢出连接
pool_timeout=30, # 获取连接超时
pool_recycle=1800, # 连接回收时间
)

异步数据库

from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker

async_engine = create_async_engine(
"postgresql+asyncpg://user:pass@localhost/db",
pool_size=10,
max_overflow=20,
)

AsyncSessionLocal = sessionmaker(
async_engine,
class_=AsyncSession,
expire_on_commit=False
)

缓存

使用 Redis 缓存:

from fastapi import FastAPI
from fastapi_cache import FastAPICache
from fastapi_cache.backends.redis import RedisBackend
from fastapi_cache.decorator import cache
from redis import asyncio as aioredis

app = FastAPI()

@app.on_event("startup")
async def startup():
redis = aioredis.from_url("redis://localhost")
FastAPICache.init(RedisBackend(redis), prefix="fastapi-cache")

@app.get("/items/")
@cache(expire=60) # 缓存 60 秒
async def get_items():
return expensive_db_query()

监控与日志

结构化日志

import logging
import json
from fastapi import FastAPI, Request
import time

app = FastAPI()

class JSONFormatter(logging.Formatter):
def format(self, record):
log_data = {
"timestamp": self.formatTime(record),
"level": record.levelname,
"message": record.getMessage(),
"module": record.module,
}
return json.dumps(log_data)

# 配置日志
logger = logging.getLogger("uvicorn.access")
handler = logging.StreamHandler()
handler.setFormatter(JSONFormatter())
logger.handlers = [handler]

@app.middleware("http")
async def log_requests(request: Request, call_next):
start_time = time.time()
response = await call_next(request)

logger.info(
f"{request.method} {request.url.path}",
extra={
"method": request.method,
"path": request.url.path,
"status_code": response.status_code,
"duration": time.time() - start_time
}
)

return response

健康检查

@app.get("/health")
async def health_check():
return {"status": "healthy"}

@app.get("/ready")
async def readiness_check(db: Session = Depends(get_session)):
# 检查数据库连接
try:
db.execute("SELECT 1")
return {"status": "ready"}
except Exception:
raise HTTPException(status_code=503, detail="Not ready")

Prometheus 指标

from prometheus_fastapi_instrumentator import Instrumentator

app = FastAPI()

Instrumentator().instrument(app).expose(app)

访问 /metrics 获取 Prometheus 格式的指标。

完整部署示例

项目结构

myapp/
├── app/
│ ├── __init__.py
│ ├── main.py
│ ├── config.py
│ ├── models.py
│ └── routers/
├── tests/
├── Dockerfile
├── docker-compose.yml
├── nginx.conf
├── requirements.txt
├── gunicorn.conf.py
└── .env.example

requirements.txt

fastapi>=0.109.0
uvicorn[standard]>=0.27.0
gunicorn>=21.0.0
sqlmodel>=0.0.14
asyncpg>=0.29.0
pydantic-settings>=2.0.0
python-jose[cryptography]>=3.3.0
passlib[bcrypt]>=1.7.4
python-multipart>=0.0.6
redis>=5.0.0

Dockerfile(生产)

# 构建阶段
FROM python:3.11-slim 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 wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt

# 运行阶段
FROM python:3.11-slim

WORKDIR /app

# 安装运行时依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
&& rm -rf /var/lib/apt/lists/*

# 复制依赖
COPY --from=builder /app/wheels /wheels
RUN pip install --no-cache-dir /wheels/* && rm -rf /wheels

# 创建非 root 用户
RUN useradd -m -r appuser && chown -R appuser /app

# 复制应用
COPY --chown=appuser:appuser . .

USER appuser

EXPOSE 8000

HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1

CMD ["gunicorn", "app.main:app", "-c", "gunicorn.conf.py"]

docker-compose.yml(生产)

version: '3.8'

services:
web:
build: .
restart: always
environment:
- DATABASE_URL=postgresql://postgres:${DB_PASSWORD}@db:5432/myapp
- SECRET_KEY=${SECRET_KEY}
- REDIS_URL=redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
networks:
- backend
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3

db:
image: postgres:15-alpine
restart: always
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=myapp
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5

redis:
image: redis:7-alpine
restart: always
networks:
- backend

nginx:
image: nginx:alpine
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- web
networks:
- backend

networks:
backend:

volumes:
postgres_data:

小结

本章我们学习了:

  1. 运行方式:Uvicorn、FastAPI CLI、Gunicorn
  2. 环境变量:使用 pydantic-settings 管理配置
  3. Docker 部署:Dockerfile 和 Docker Compose
  4. Nginx 反向代理:配置 HTTPS 和负载均衡
  5. Systemd 服务:将应用注册为系统服务
  6. 性能优化:Worker 数量、连接池、缓存
  7. 监控日志:结构化日志、健康检查、Prometheus

部署最佳实践:

  • 使用环境变量管理配置
  • 非 root 用户运行
  • 启用 HTTPS
  • 配置健康检查
  • 收集日志和监控指标
  • 使用缓存减轻数据库压力

练习

  1. 编写一个完整的 Dockerfile,使用多阶段构建
  2. 配置 Nginx 反向代理和 HTTPS
  3. 创建 Systemd 服务配置文件
  4. 添加 Prometheus 指标收集