FastAPI 系列

  1. FastAPI 系列:从第一个接口到生产部署

  2. FastAPI 为什么适合做 JSON API

  3. 第一个接口怎么跑起来

  4. 路径、查询和请求体为什么要分开声明

  5. Pydantic 模型怎么当契约

  6. 路由怎么拆

  7. 依赖注入到底省了什么

  8. 数据库会话怎么进接口

  9. 状态码和异常为什么不要裸 raise

  10. 鉴权怎么接到 Depends 上

  11. 中间件、CORS 和后台任务分别解决什么

  12. 测试怎么写才不连真实库

  13. 生产怎么部署(当前篇)

开发用 fastapi dev,生产不要用。生产要解决三件事:多个 worker 吃满 CPU、进程挂了能被探活、配置和日志能被编排系统收集。官方已经不推荐旧的 tiangolo/uvicorn-gunicorn-fastapi 镜像,改为自己写 Dockerfile,用 Uvicorn 的 --workers

反向代理后面是多个 Uvicorn worker,并暴露健康检查与结构化日志

启动命令

uv run fastapi run app/main.py --host 0.0.0.0 --port 8000 --workers 4
# 或
uv run uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4

worker 数量通常接近 CPU 核数,再按内存和数据库连接上限往下调。每个 worker 都是独立进程,内存里的 dict 缓存不会共享。

健康检查

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


@app.get("/ready")
async def ready(db: DbDep):
    db.execute(text("SELECT 1"))
    return {"status": "ready"}

存活探针打 /health,就绪探针打 /ready。不要把就绪检查做成很重的全表扫描。

配置与密钥

from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env", extra="ignore")

    secret_key: str
    database_url: str
    cors_origins: list[str] = []


settings = Settings()

镜像里不要带 .env。编排系统注入环境变量。调试接口、Swagger 在生产可以关:

app = FastAPI(docs_url=None, redoc_url=None, openapi_url=None)

内部环境再打开文档。

Dockerfile 要点

FROM python:3.12-slim
WORKDIR /code
COPY ./requirements.txt /code/requirements.txt
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
COPY ./app /code/app
CMD ["fastapi", "run", "app/main.py", "--host", "0.0.0.0", "--port", "80", "--workers", "4"]

前面放反向代理做 TLS 和静态资源。应用只监听内网端口。日志打 stdout JSON,由平台收集,不要在容器里轮转本地文件还当唯一审计。

常见误区

生产开 --reload

热重载额外进程、文件监听,既慢也不安全。

一个容器里既跑数据库又跑 API

本地 compose 可以,生产把状态放到托管数据库,应用保持无状态。

用线程数硬扛阻塞 ORM

先减少阻塞调用,再加 worker。把同步 SQLAlchemy 丢进 async def 再加 worker,只会让事件循环一起堵。

小结

生产形态是:无状态应用 + Uvicorn workers + 健康检查 + 环境变量配置 + stdout 日志。至此,从第一个 /docs 到可编排部署的链路已经齐了。回到总目录按需复习。

参考资料