Skip to main content
今日目标:在本地跑通代码只是第一步,能在生产环境稳定运行才是真本事。今天我们要学习 12-Factor App 开发原则,学会用 .env 管理敏感配置,并打出你的第一个 Docker 镜像。今天不只是写代码,而是要彻底理解 “为什么不能硬编码配置”“Gunicorn 和 Uvicorn 的关系” 以及 “如何设计生产级的部署方案”

学习内容 (30 mins)

在开始写代码前,先搞懂这些核心概念,否则后面的代码你会看得云里雾里。
什么是 12-Factor App?12-Factor App 是一套构建现代 Web 应用的最佳实践,由 Heroku 提出。它强调应用的可移植性、可扩展性和可维护性。核心原则
  • 配置外置:配置信息(如数据库密码)应该存储在环境变量中,而不是代码里
  • 无状态:应用应该是无状态的,任何状态都应该存储在数据库或缓存中
  • 日志即流:日志应该作为事件流输出,而不是写入文件
为什么配置不能写死在代码里?
  • 安全性:代码可能被提交到 Git,敏感信息会泄露
  • 灵活性:不同环境(开发、测试、生产)需要不同配置
  • 可维护性:修改配置不需要改代码,只需改环境变量
环境变量的优势
  • 安全性:不会出现在代码仓库中
  • 灵活性:不同环境可以有不同的值
  • 标准化:所有配置都通过环境变量管理,统一规范
Uvicorn 是什么?Uvicorn 是一个高性能的 ASGI 服务器,专注于处理 HTTP 请求。
  • 优点:轻量、快速、支持异步
  • 缺点:单进程,如果进程崩溃,服务就停了
  • 适用场景:开发环境、小规模应用
Gunicorn 是什么?Gunicorn 是一个进程管理器,负责管理多个 Worker 进程。
  • 优点:多进程、自动重启、负载均衡
  • 缺点:需要配合 Worker(如 Uvicorn)使用
  • 适用场景:生产环境
为什么生产环境要用 Gunicorn + Uvicorn?
  • 高可用性:一个进程崩溃,其他进程继续工作
  • 性能提升:多个进程可以并行处理请求
  • 自动重启:进程异常退出时自动重启
  • 负载均衡:自动分配请求到不同的 Worker
类比
  • Uvicorn:单个服务员(Worker)
  • Gunicorn:餐厅经理(Manager),管理多个服务员
部署架构
客户端请求

Gunicorn (Manager)

Uvicorn Workers (多个 Worker 进程)

FastAPI 应用

代码任务 (90 mins)

1

环境准备

确保虚拟环境已激活,并安装必要的包:
# 确保虚拟环境已激活
source .venv/bin/activate

# 安装 pydantic-settings(用于读取环境变量)
pip install pydantic-settings

# 安装 python-dotenv(用于读取 .env 文件)
pip install python-dotenv

# 生产环境还需要 Gunicorn
pip install gunicorn
2

任务 A:优雅的配置管理

使用 pydantic-settings 管理配置。任务分解
  1. 创建 .env 文件存储配置
  2. 创建 config.py 读取配置
  3. 在应用中使用配置
# Day 19 - 环境变量配置
# 注意:这个文件应该添加到 .gitignore,不要提交到 Git!

# 应用配置
APP_NAME=Production Ops API
DEBUG=False

# 数据库配置
# 真实的生产库地址(示例)
DATABASE_URL=mysql+aiomysql://root:root@localhost/infra_db

# 服务器配置
HOST=0.0.0.0
PORT=8000
代码解释
  • BaseSettings:Pydantic 提供的配置基类
  • env_file = ".env":优先从 .env 文件读取
  • case_sensitive = False:不区分大小写(APP_NAMEapp_name 都可以)
  • 默认值:如果环境变量不存在,使用默认值
  • 必填字段:没有默认值的字段,如果环境变量不存在会报错
验证步骤
  1. 创建 .env 文件
  2. 创建 config.py 文件
  3. 测试读取配置:
    from config import settings
    print(settings.app_name)
    print(settings.database_url)
    
重要提示
.env 文件必须添加到 .gitignore创建或编辑 .gitignore 文件:
echo ".env" >> .gitignore
原因:.env 文件包含敏感信息(如数据库密码),不应该提交到 Git。
3

任务 B:编写 Dockerfile

创建 Dockerfile,将应用容器化。
# Day 19 - FastAPI 应用 Dockerfile
# 生产级 Docker 镜像构建配置

# ========== 1. 选择基础镜像 ==========
# python:3.10-slim: Python 3.10 的精简版本
# slim 版本去掉了 gcc 等编译工具,体积小(约 150MB)
# 如果应用需要编译 C 扩展,使用 python:3.10
FROM python:3.10-slim

# ========== 2. 设置工作目录 ==========
# 容器内的工作目录,后续命令都在这个目录执行
WORKDIR /app

# ========== 3. 设置环境变量 ==========
# 防止 Python 生成 .pyc 文件
ENV PYTHONDONTWRITEBYTECODE=1
# 防止 Python 缓冲输出(日志实时显示)
ENV PYTHONUNBUFFERED=1

# ========== 4. 先复制依赖清单(利用 Docker 缓存层)==========
# 如果 requirements.txt 没变,这一行和下一行 RUN 只会执行一次
# 这样可以加快构建速度
COPY requirements.txt .

# ========== 5. 安装依赖 ==========
# --no-cache-dir: 不缓存安装包,进一步减小体积
# --upgrade: 升级 pip 到最新版本
RUN pip install --no-cache-dir --upgrade pip && \
    pip install --no-cache-dir -r requirements.txt

# ========== 6. 复制剩下的所有源码 ==========
# 注意:先复制 requirements.txt,再复制源码
# 这样如果源码改了但依赖没变,Docker 可以复用之前的缓存层
COPY . .

# ========== 7. 暴露端口 ==========
# 仅作为文档说明,实际端口映射在 docker run 时指定
EXPOSE 8000

# ========== 8. 启动命令(经理带工人模式)==========
# Gunicorn 作为进程管理器,管理多个 Uvicorn Worker
# -w 4: 启动 4 个 Worker 进程(通常设置为 CPU 核心数 x 2)
# -k uvicorn.workers.UvicornWorker: 指定 Worker 类型为 Uvicorn
# -b 0.0.0.0:8000: 绑定 IP 和端口(0.0.0.0 表示监听所有网络接口)
# --timeout 120: 请求超时时间(秒)
# --access-logfile -: 访问日志输出到标准输出
# --error-logfile -: 错误日志输出到标准错误
CMD ["gunicorn", "19_main:app", \
     "-w", "4", \
     "-k", "uvicorn.workers.UvicornWorker", \
     "-b", "0.0.0.0:8000", \
     "--timeout", "120", \
     "--access-logfile", "-", \
     "--error-logfile", "-"]
Dockerfile 解释
  • 多阶段构建:先复制依赖,再复制源码,利用缓存加速构建
  • slim 镜像:使用精简版 Python 镜像,减小体积
  • 环境变量:设置 Python 相关环境变量
  • Gunicorn:使用 Gunicorn 管理多个 Uvicorn Worker
验证步骤
  1. 创建 requirements.txt 文件
  2. 创建 Dockerfile
  3. 创建 .dockerignore 文件(排除不需要的文件):
    .git
    .venv
    __pycache__
    *.pyc
    .env
    
  4. 构建镜像:
    docker build -t my-fastapi-app .
    
  5. 运行容器:
    docker run -d -p 8000:8000 \
      --name api-prod \
      --env-file .env \
      my-fastapi-app
    
  6. 检查日志:
    docker logs -f api-prod
    
    • 应该看到:[INFO] Booting worker with pid: xxx
常见错误
  • ModuleNotFoundError - requirements.txt 中缺少依赖,检查依赖列表
  • Can't connect to MySQL server - 数据库配置错误,检查 DATABASE_URL
  • Address already in use - 端口被占用,检查端口映射
  • docker: command not found - Docker 未安装,需要先安装 Docker
4

任务 C:Docker Compose 编排

使用 Docker Compose 一键启动应用和数据库。
# Day 19 - Docker Compose 配置
# 一键启动 API 和数据库服务

version: '3.8'

services:
  # ========== 数据库服务 ==========
  db:
    image: mysql:8.0
    container_name: mysql-api
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: infra_db
    ports:
      - "3306:3306"
    volumes:
      # 数据持久化(可选)
      - mysql_data:/var/lib/mysql
    healthcheck:
      # 健康检查:确保数据库完全启动
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

  # ========== API 服务 ==========
  api:
    build: .
    container_name: fastapi-app
    command: uvicorn 19_main:app --host 0.0.0.0 --port 8000
    ports:
      - "8000:8000"
    environment:
      # 使用服务名作为主机名(Docker Compose 自动 DNS 解析)
      DATABASE_URL: mysql+aiomysql://root:root@db/infra_db
      APP_NAME: Production Ops API
      DEBUG: False
    depends_on:
      db:
        condition: service_healthy  # 等待数据库健康检查通过
    restart: unless-stopped  # 自动重启(除非手动停止)

# ========== 数据卷 ==========
volumes:
  mysql_data:
    # 数据持久化,即使容器删除数据也不会丢失
Docker Compose 解释
  • services:定义多个服务(db、api)
  • depends_on:定义服务依赖关系
  • healthcheck:健康检查,确保服务完全启动
  • volumes:数据持久化
验证步骤
  1. 创建 docker-compose.yaml 文件
  2. 启动服务:docker-compose up --build
  3. 等待服务启动完成
  4. 访问 http://localhost:8000/health,应该返回 {"status": "healthy"}

拓展任务 (30 mins)

挑战 1:多环境配置

任务:创建不同环境的配置文件(.env.dev.env.prod),并在 Docker Compose 中使用。提示
env_file:
  - .env.prod

挑战 2:健康检查

任务:在 Dockerfile 中添加健康检查指令。提示
HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost:8000/health || exit 1

今日产出物

  • .env - 环境变量配置文件(不提交到 Git)
  • config.py - 配置管理模块
  • Dockerfile - Docker 镜像构建文件
  • docker-compose.yaml - Docker Compose 编排文件
  • requirements.txt - 生产环境依赖列表

参考代码

查看参考代码

在 GitHub 查看完整的示例代码

在线运行

使用在线编辑器测试代码

实际应用场景

生产部署在运维中的应用

  • 容器化部署:使用 Docker 实现应用的可移植性
  • 环境隔离:不同环境使用不同的配置文件
  • 高可用性:使用 Gunicorn 多进程,提高稳定性
  • 自动化部署:配合 CI/CD 实现自动化部署
  • 资源管理:通过 Docker Compose 管理多个服务

配置管理最佳实践

  • 环境变量优先:敏感信息存储在环境变量中
  • 配置文件分离:不同环境使用不同的配置文件
  • 默认值设置:为可选配置设置合理的默认值
  • 验证配置:启动时验证必填配置是否存在
  • 文档完善:在代码中注释配置的用途和默认值
与 Day 20 的关联:今天学习的配置管理和 Docker 部署,明天会结合所有知识点,开发一个完整的生产级 API 项目。

上一天: 数据库整合

Day 18 | FastAPI 数据库整合

下一天: 阶段实战

Day 20 | 第三阶段综合实战