FastAPI Pydantic Settings多环境配置完全指南

📂 所属阶段:第五阶段 — 工程化与部署(实战篇)
🔗 相关章节:FastAPI依赖注入系统 · FastAPI安全认证

目录

环境配置管理概述

为什么需要环境配置管理?

想象一下:开发时用本地SQLite,上线时忘改PostgreSQL密码,结果把测试数据推到了生产库…这就是硬编码配置的噩梦。

错误示例:硬编码配置

# ❌ 千万别这么做!
DATABASE_URL = "sqlite:///dev.db"  # 上线忘改→生产数据丢失
SECRET_KEY = "dev-secret-123"       # 密钥暴露→安全漏洞
DEBUG = True                         # 生产开调试→信息泄露

正确示例:环境驱动的配置

通过不同的.env文件隔离环境,代码完全一致:

.env.development  → 本地开发
.env.testing      → 自动化测试
.env.production   → 生产环境

核心原则与12-Factor App

我们遵循12-Factor App配置原则配置与代码完全分离,通过环境变量注入。这样可以保证:

  • 环境隔离:不同环境互不干扰
  • 敏感信息保护:密钥不进代码仓库
  • 易于部署:无需修改代码即可切换环境

Pydantic Settings基础

安装与基础配置

# 安装依赖
pip install pydantic-settings fastapi

定义基础配置类

BaseSettings定义所有环境通用的配置,并添加类型验证:

# config/base.py
from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import Field, field_validator
from typing import List

class BaseConfig(BaseSettings):
    # 配置文件加载规则
    model_config = SettingsConfigDict(
        env_file=".env",
        env_file_encoding="utf-8",
        extra="forbid"  # 禁止未定义的配置项
    )

    # 应用基本信息
    app_name: str = Field(default="FastAPI App", min_length=1)
    app_version: str = "1.0.0"
    environment: str = Field(
        default="development",
        pattern=r"^(development|testing|production)$"
    )

    # 调试与开发
    debug: bool = False
    host: str = "127.0.0.1"
    port: int = Field(default=8000, ge=1, le=65535)

    # CORS配置
    cors_allow_origins: List[str] = Field(default_factory=list)

    # 环境判断属性
    @property
    def is_production(self) -> bool:
        return self.environment == "production"

    @property
    def is_development(self) -> bool:
        return self.environment == "development"

    # 解析CORS来源(支持逗号分隔的字符串)
    @field_validator("cors_allow_origins", mode="before")
    @classmethod
    def parse_cors_origins(cls, v):
        if isinstance(v, str):
            return [o.strip() for o in v.split(",") if o.strip()]
        return v

环境特定配置

继承BaseConfig,为不同环境覆盖配置:

# config/environments.py
from .base import BaseConfig
from pydantic import Field

class DevelopmentConfig(BaseConfig):
    debug: bool = True
    database_url: str = "sqlite+aiosqlite:///dev.db"
    cors_allow_origins: List[str] = ["http://localhost:3000"]

class ProductionConfig(BaseConfig):
    debug: bool = False
    database_url: str = Field(description="生产数据库URL", min_length=10)
    cors_allow_origins: List[str] = ["https://yourapp.com"]

    @field_validator("database_url")
    @classmethod
    def validate_prod_db(cls, v):
        if "sqlite" in v.lower():
            raise ValueError("生产环境不能使用SQLite")
        return v

配置工厂函数

根据环境变量自动加载对应配置:

# config/__init__.py
import os
from .environments import DevelopmentConfig, ProductionConfig, BaseConfig

def get_config() -> BaseConfig:
    env = os.getenv("ENVIRONMENT", "development").lower()
    config_map = {
        "development": DevelopmentConfig,
        "production": ProductionConfig
    }
    return config_map.get(env, DevelopmentConfig)()

# 全局配置实例(带缓存)
from functools import lru_cache

@lru_cache()
def get_cached_config() -> BaseConfig:
    return get_config()

多环境配置策略

环境变量优先级

Pydantic Settings遵循以下优先级(从高到低):

  1. 直接传递给配置类的参数
  2. 系统环境变量
  3. .env文件
  4. 字段默认值

环境配置文件

创建不同环境的.env文件,并添加到.gitignore

# .gitignore
.env*
!env.example
# .env.development
ENVIRONMENT=development
DEBUG=true
DATABASE_URL=sqlite+aiosqlite:///dev.db
CORS_ALLOW_ORIGINS=http://localhost:3000
# .env.production
ENVIRONMENT=production
DEBUG=false
DATABASE_URL=postgresql+asyncpg://user:pass@prod-db:5432/app
CORS_ALLOW_ORIGINS=https://yourapp.com

Docker多环境部署

# Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN useradd -m app && chown -R app:app /app
USER app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
# docker-compose.yml
version: '3.8'
services:
  app:
    build: .
    environment:
      - ENVIRONMENT=${ENVIRONMENT:-development}
    env_file:
      - .env.${ENVIRONMENT:-development}
    ports:
      - "8000:8000"
    depends_on:
      - db
  db:
    image: postgres:15
    environment:
      - POSTGRES_DB=app
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=pass

配置验证与类型安全

Pydantic的核心优势就是类型安全和验证,我们可以利用枚举和字面量类型进一步增强:

# config/validated.py
from .base import BaseConfig
from enum import Enum
from typing import Literal

class LogLevel(str, Enum):
    DEBUG = "DEBUG"
    INFO = "INFO"
    WARNING = "WARNING"
    ERROR = "ERROR"

class ValidatedConfig(BaseConfig):
    log_level: LogLevel = LogLevel.INFO
    cache_strategy: Literal["memory", "redis"] = "memory"

敏感信息安全管理

密钥管理

敏感信息(如数据库密码、JWT密钥)绝对不能进代码仓库,我们可以:

  1. 使用环境变量
  2. 使用Docker Secrets/Kubernetes Secrets
  3. 本地用.env文件,生产用云服务商的密钥管理服务

环境变量安全处理

# config/security.py
import os
from typing import Optional

def get_secret(key: str, default: Optional[str] = None) -> Optional[str]:
    # 优先从Docker Secrets获取
    secret_path = f"/run/secrets/{key}"
    if os.path.exists(secret_path):
        with open(secret_path, "r") as f:
            return f.read().strip()
    # 其次从环境变量获取
    return os.getenv(key, default)

生产部署最佳实践

安全检查清单

部署前务必确认:

  • DEBUG=False
  • 使用强密钥(至少64字符)
  • 限制CORS来源为具体域名
  • 数据库使用PostgreSQL/MySQL+SSL
  • 日志级别为WARNING或更高
  • 敏感信息通过Secrets注入

部署脚本

#!/bin/bash
set -e

# 验证必需变量
REQUIRED_VARS=("PROD_DATABASE_URL" "PROD_JWT_SECRET")
for var in "${REQUIRED_VARS[@]}"; do
  if [ -z "${!var}" ]; then
    echo "❌ 缺失必需变量: $var"
    exit 1
  fi
done

# 构建并部署
docker build -t my-fastapi-app .
docker-compose -f docker-compose.prod.yml up -d

# 检查健康状态
sleep 10
if curl -f http://localhost/health >/dev/null; then
  echo "✅ 部署成功"
else
  echo "❌ 部署失败"
  exit 1
fi

总结

FastAPI + Pydantic Settings提供了完整的配置管理解决方案:

  • 环境隔离:通过.env文件或环境变量切换
  • 类型安全:完整的类型提示和验证
  • 安全保护:敏感信息不进代码仓库
  • 云原生友好:完美适配Docker/Kubernetes

💡 关键要点:配置管理是应用工程化的基础,投资在配置管理上的时间会带来长期收益。


🔗 扩展阅读