环境配置管理Skill env-config

Python/FastAPI项目环境配置管理技能,提供.env文件加载、多环境支持、Pydantic配置验证、密钥安全管理等功能。适用于后端开发中的环境变量管理、应用程序设置配置、开发/测试/生产环境隔离、敏感信息保护等场景。关键词:环境配置、Python配置管理、FastAPI设置、Pydantic BaseSettings、.env文件、多环境支持、密钥安全、配置验证。

后端开发 0 次安装 0 次浏览 更新于 3/2/2026

name: env-config description: | 用于设置环境配置、加载.env文件或跨环境管理应用程序设置时使用。 触发场景:.env设置、加载环境变量、Pydantic BaseSettings、 配置验证或密钥管理。 不适用于:特定代码逻辑、功能标志或运行时功能切换。

环境配置技能

为Python/FastAPI项目提供专业的环境配置管理,支持安全密钥处理和多环境支持。

快速参考

模式 用法
加载.env 在应用启动时使用 load_dotenv()
访问变量 settings.DB_URL, settings.JWT_SECRET
必需变量 Field(..., description="数据库URL")
可选变量 DB_HOST: str = "localhost"
密钥类型 敏感值使用 SecretStr

项目结构

project/
├── .env                  # 本地开发环境(不提交)
├── .env.example          # 包含所有必需变量的模板(提交)
├── .env.staging          # 预发布环境
├── .env.production       # 生产环境(由基础设施管理)
└── config/
    ├── __init__.py
    └── settings.py       # Pydantic BaseSettings

settings.py - 基础配置

# config/settings.py
from functools import lru_cache
from pydantic import Field, SecretStr
from pydantic_settings import BaseSettings, SettingsConfigDict


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

    # 应用程序
    APP_NAME: str = "ERP系统"
    DEBUG: bool = False
    API_V1_PREFIX: str = "/v1"

    # 数据库
    DB_URL: str = Field(
        ...,
        description="PostgreSQL连接URL",
        examples=["postgresql://user:pass@localhost:5432/dbname"],
    )
    DB_POOL_SIZE: int = Field(default=5, ge=1, le=100)

    # JWT认证
    JWT_SECRET_KEY: SecretStr = Field(
        ...,
        description="JWT签名密钥",
    )
    JWT_ALGORITHM: str = "HS256"
    JWT_EXPIRATION_MINUTES: int = Field(default=15, ge=1)

    # Redis(可选)
    REDIS_URL: str | None = None

    # 日志
    LOG_LEVEL: str = Field(default="INFO", pattern="^(DEBUG|INFO|WARNING|ERROR)$")

    # CORS
    CORS_ORIGINS: list[str] = ["http://localhost:3000"]

    @property
    def is_production(self) -> bool:
        return not self.DEBUG


@lru_cache
def get_settings() -> Settings:
    """缓存的设置实例,用于应用生命周期。"""
    return Settings()

.env.example - 模板文件

# .env.example - 复制到.env并填写值
# 不要提交实际密钥!

# 应用程序
APP_NAME="ERP系统"
DEBUG=false
API_V1_PREFIX="/v1"

# 数据库(必需)
DB_URL="postgresql://user:password@localhost:5432/erp_db"

# JWT认证(必需 - 使用openssl rand -hex 32生成)
JWT_SECRET_KEY="your-secret-key-here-generate-with-openssl-rand-hex-32"
JWT_ALGORITHM="HS256"
JWT_EXPIRATION_MINUTES=15

# Redis(可选)
# REDIS_URL="redis://localhost:6379/0"

# 日志
LOG_LEVEL="INFO"

# CORS
CORS_ORIGINS="http://localhost:3000"

.env - 本地开发

# .env - 仅用于本地开发
# 切勿将此文件提交到版本控制

APP_NAME="ERP系统"
DEBUG=true
API_V1_PREFIX="/v1"

# 本地PostgreSQL
DB_URL="postgresql://postgres:postgres@localhost:5432/erp_dev"

# 使用openssl rand -hex 32生成
JWT_SECRET_KEY="local-dev-secret-key-change-in-production"
JWT_ALGORITHM="HS256"
JWT_EXPIRATION_MINUTES=15

# 本地Redis(如果使用)
REDIS_URL="redis://localhost:6379/0"

LOG_LEVEL="DEBUG"

CORS_ORIGINS="http://localhost:3000,http://localhost:5173"

在应用程序中使用

FastAPI应用程序

# main.py
from contextlib import asynccontextmanager
from dotenv import load_dotenv

load_dotenv()  # 加载.env文件

from config.settings import get_settings


@asynccontextmanager
async def lifespan(app):
    settings = get_settings()
    print(f"启动{settings.APP_NAME},模式:{'DEBUG' if settings.DEBUG else 'PROD'}")
    yield
    print("正在关闭...")


app = FastAPI(
    title=get_settings().APP_NAME,
    lifespan=lifespan,
)


# 包含路由
from app.routers import fees, students
app.include_router(fees.router, prefix=get_settings().API_V1_PREFIX)
app.include_router(students.router, prefix=get_settings().API_V1_PREFIX)

数据库连接

# database.py
from sqlmodel import create_engine, Session
from config.settings import get_settings

settings = get_settings()

engine = create_engine(
    settings.DB_URL.get_secret_value() if hasattr(settings.DB_URL, 'get_secret_value') else settings.DB_URL,
    pool_size=settings.DB_POOL_SIZE,
    max_overflow=10,
)


def get_session():
    with Session(engine) as session:
        yield session

JWT配置

# auth/jwt.py
from datetime import timedelta
from config.settings import get_settings

settings = get_settings()

JWT_SECRET = settings.JWT_SECRET_KEY.get_secret_value()
JWT_ALGORITHM = settings.JWT_ALGORITHM
ACCESS_TOKEN_EXPIRE_MINUTES = settings.JWT_EXPIRATION_MINUTES


def create_access_token(data: dict, expires_delta: timedelta | None = None) -> str:
    # ... 令牌创建逻辑
    pass

多环境支持

环境特定配置

# config/settings.py

class Settings(BaseSettings):
    # ... 共享设置

    @classmethod
    def from_env(cls, env: str = "development") -> "Settings":
        """加载特定环境的设置。"""
        env_file = {
            "development": ".env",
            "staging": ".env.staging",
            "production": ".env.production",
        }.get(env, ".env")

        return cls(_env_file=env_file)

生产环境覆盖

# 生产环境应使用环境变量,而非.env文件
# 在部署平台(Docker、K8s、Cloud Run等)中设置这些变量

export DB_URL="postgresql://prod_user:prod_pass@prod-db.example.com:5432/erp_prod"
export JWT_SECRET_KEY="production-secret-key-from-secrets-manager"
export DEBUG=false
export LOG_LEVEL="WARNING"

密钥管理

生成密钥

# 生成安全随机密钥
openssl rand -hex 32  # 用于JWT_SECRET_KEY

# 生成数据库密码
openssl rand -base64 32

密钥轮换脚本

# scripts/rotate_secret.py
"""在所有环境中轮换密钥。"""
import os
import re

def rotate_secret(env_file: str, key: str, new_value: str):
    """在.env文件中替换密钥值。"""
    with open(env_file, "r") as f:
        content = f.read()

    # 匹配KEY=value的模式
    pattern = f"^{key}=.*$"
    replacement = f"{key}={new_value}"

    new_content = re.sub(pattern, replacement, content, flags=re.MULTILINE)

    with open(env_file, "w") as f:
        f.write(new_content)

    print(f"已在{env_file}中轮换{key}")


if __name__ == "__main__":
    import sys
    if len(sys.argv) != 4:
        print("用法:rotate_secret.py <env_file> <key> <new_value>")
        sys.exit(1)

    rotate_secret(sys.argv[1], sys.argv[2], sys.argv[3])

质量检查清单

  • [ ] 加载时验证:所有必需字段都有Field(..., ...)验证
  • [ ] 日志无泄露:敏感值使用SecretStr,永不打印设置
  • [ ] 提交.env.example:模板显示所有必需变量
  • [ ] .env被git忽略:本地开发文件从版本控制中排除
  • [ ] 生成密钥:JWT密钥使用openssl rand -hex 32
  • [ ] 环境隔离:开发/预发布/生产环境有不同的配置
  • [ ] 类型安全:所有设置都有正确的类型注解

与其他技能的集成

技能 集成点
@jwt-auth 从设置中获取JWT_SECRET_KEY
@sqlmodel-crud 从设置中获取DB_URL
@fastapi-app 从设置中获取所有应用设置
@db-migration 用于迁移的数据库URL
@api-route-design API前缀、CORS来源

安全最佳实践

应该做

  • 密码、API密钥、令牌使用SecretStr
  • 定期轮换密钥
  • 每个环境使用不同的密钥
  • 将生产密钥存储在密钥管理器中
  • 在启动时验证所有必需设置

不应该做

  • 切勿将.env文件提交到版本控制
  • 切勿在源代码中硬编码密钥
  • 切勿记录设置或环境变量
  • 切勿在生产中使用默认/占位符密钥
  • 切勿在错误消息中暴露配置详情

启动验证

# config/validate.py
"""在启动时验证必需配置。"""
from pydantic import ValidationError
from config.settings import Settings


def validate_settings() -> bool:
    """确保所有必需设置都已配置。"""
    try:
        settings = Settings()
        return True
    except ValidationError as e:
        print("配置验证失败:")
        for error in e.errors():
            print(f"  - {error['loc'][0]}: {error['msg']}")
        return False


if __name__ == "__main__":
    if not validate_settings():
        exit(1)