开发与生产环境的差异

开发时一条命令 `uvicorn app.main:app --reload` 就能把服务跑起来,但生产环境要面对的是真实流量、宕机风险和运维边界。开发模式默认单进程、自动重载,方便改代码即时生效;生产模式要的是多进程、稳定运行、可观测和可回滚。

上线前首先要补几件基础能力:日志要有明确的格式和轮转、配置要与代码分离、进程要被守护、请求要有超时上限。缺少任何一项,服务一遇到流量峰值就容易出问题,而且出了问题还查不到原因。

FastAPI 本身只是 Web 框架,它不负责进程管理、不负责反向代理、也不负责容器打包。把这些能力组合起来,才构成完整的生产部署方案。下面按一条从开发机到生产服务器的路径,逐步把它搭起来。

部署的本质是把「能跑的代码」变成「能扛住流量、能查问题、能快速恢复的服务」。
本文核心知识点
  • uvicorn多 worker 并发模型。
  • Gunicorn成熟稳定的进程托管。
  • Docker多阶段构建镜像。
  • Nginx反向代理与 HTTPS。
FastAPI 生产化部署配图:蓝调等距插画中大型服务器主机与多台工作站通过发光线路连接,底部传送带运送模块的科技场景
一台主服务配多个工作节点,再通过统一入口对外提供能力,是生产部署的基本形态。

uvicorn 多 worker 与进程模型

uvicorn 是 FastAPI 官方推荐的 ASGI 服务器。单 worker 只能用一个 CPU 核心,对现代多核服务器是浪费。加 `--workers` 参数就能起多个进程,每个进程各自处理请求,对外共享同一个端口。

uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4

worker 数量一般按 CPU 核心数设定,常见做法是 2 到 4 倍核心数,视接口的 CPU 密集程度调整。进程之间不共享内存状态,需要共享的会话、计数等数据要放到 Redis 这类外部存储里,不能在进程内存里做文章。

多进程也意味着每个 worker 各自维护自己的日志和连接,排查问题时要注意把日志按 worker 维度聚合起来看。uvicorn 自带的 worker 管理比较简单,生产上更推荐交给 Gunicorn 托管。

Gunicorn 托管与配置

Gunicorn 是 Python 世界成熟的 WSGI 进程管理器,通过 `uvicorn.workers.UvicornWorker` 也能托管 ASGI 应用。它的优势是进程管理成熟:master 进程负责调度,worker 崩溃会自动重启,还能优雅处理信号。

配置项含义
workersworker 进程数量
timeoutworker 无响应超时(秒)
graceful_timeout优雅停机等待时间
max_requests单个 worker 处理请求数上限
gunicorn app.main:app -k uvicorn.workers.UvicornWorker \
  --workers 4 --timeout 60 --graceful-timeout 30 \
  --max-requests 5000 --max-requests-jitter 500

`max_requests` 配合 jitter 可以让 worker 定期轮换,缓解内存缓慢增长的问题。配置写成文件放在仓库里,比命令行参数更可维护,也方便多环境复用。

Docker 镜像与多阶段构建

容器化让部署环境与代码一起交付,避免「在我机器上能跑」的问题。多阶段构建先用完整镜像安装依赖,再在精简运行镜像里只保留依赖和应用,显著缩小体积、减少攻击面。

FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --prefix=/install -r requirements.txt

FROM python:3.12-slim
COPY --from=builder /install /usr/local
COPY . /app
WORKDIR /app
EXPOSE 8000
CMD ["gunicorn", "app.main:app", "-k", "uvicorn.workers.UvicornWorker", "-c", "gunicorn.conf.py"]

运行容器时要用 `--restart unless-stopped` 让 Docker 守护进程拉起异常退出的容器;镜像要打上版本标签而不是一律用 latest,回滚时才能精确切到上一个版本。

配置管理与密钥安全

数据库地址、密钥、令牌这些信息不能写死在代码里,也不能打进镜像。FastAPI 官方推荐用 Pydantic 的 BaseSettings 从环境变量读取配置,运行时再注入。

from pydantic_settings import BaseSettings

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

    model_config = {"env_file": ".env"}

settings = Settings()

容器环境下通过 `-e` 参数或密钥管理服务注入环境变量,`.env` 文件只留在本地开发环境,绝不提交进仓库。日志里也不得打印密钥,避免敏感信息随日志外泄。