配置管理(Configuration Management)用于跨环境加载、校验和管理应用配置。其核心目标是让配置随部署环境变化,而代码保持不变,并在启动时快速失败以暴露无效配置。
一、配置层级 优先级从高到低(高者胜出):
- 命令行参数
- 环境变量
- .env.local(被 git 忽略,供单个开发者使用)
- .env.{environment}(例如 .env.production)
- .env(共享默认值)
- 配置文件(config.yaml、settings.toml)
- 应用默认值
二、Python:Pydantic Settings 使用 pydantic_settings 的 BaseSettings 定义配置类,通过 model_config 设置 env_prefix 与 env_file。示例中定义了必填项 database_url、带默认值的 redis_url、使用 SecretStr 的密钥(api_key、db_password,在日志中会被遮蔽)、带类型与默认值的 debug、log_level、workers(Field 限制 1 到 32)、allowed_origins 列表。实例化 Settings() 时会从环境变量与 .env 文件加载。
环境特定覆盖:env_file 可传入列表,如 [".env", f".env.{os.getenv('APP_ENV', 'development')}"]。
三、环境变量约定 命名:使用应用名前缀避免冲突,例如 APP_DATABASE_URL、APP_REDIS_URL、APP_LOG_LEVEL。嵌套配置使用双下划线,例如 APP_AUTH__SECRET_KEY、APP_AUTH__TOKEN_TTL。
.env 文件:.env 为提交到仓库的共享默认值(如 APP_LOG_LEVEL=INFO、APP_WORKERS=4、APP_REDIS_URL);.env.local 被 git 忽略,供开发者覆盖(如 APP_DATABASE_URL、APP_DEBUG=true);.env.production 为提交的生产默认值(如 APP_LOG_LEVEL=WARNING、APP_WORKERS=8、APP_DEBUG=false)。
.gitignore 规则:忽略 .env.local、.env..local、.secret。
四、配置校验 启动时快速失败:validate_config 函数收集错误,例如 debug 模式与 WARNING 日志级别同时出现、非 debug 模式下 database_url 含 localhost,出现错误时打印到 stderr 并以 SystemExit(1) 退出。
配置文件模式校验:使用 yaml.safe_load 读取配置,用 jsonschema.validate 依据 JSON Schema 校验。
五、YAML/TOML 配置 YAML 锚点:用 &defaults 定义默认值,用 <<: *defaults 合并到 development 与 production 段,production 中 database_url 使用 ${DATABASE_URL} 在运行时解析。
TOML(兼容 pyproject.toml):如 [tool.myapp] 下设置 log_level、workers,[tool.myapp.database] 下设置 pool_size、timeout。
六、密钥管理 运行时解析:resolve_secret 根据前缀处理,op:// 走 1Password(调用 op read),file:// 读取文件(Docker secrets),env:// 读取指定环境变量,否则原样返回。
Docker Secrets:load_docker_secret 优先读取 /run/secrets/{name},否则回退到对应大写环境变量。
七、特性开关 使用 dataclass 定义 FeatureFlags(如 new_dashboard、v2_api、experimental_search),from_env 类方法从 FF_ 前缀的环境变量读取,值为 "true" 时启用。
八、12-Factor 配置原则 配置存储在环境中,而非代码中;严格分离——配置随部署变化,代码不变;不使用配置分组(dev/staging/prod),每次部署独立配置;密钥也是配置,视为环境变量,绝不提交。
九、反模式 硬编码配置(应外置到环境变量或配置文件);代码或 git 中出现密钥(应使用密钥管理器或环境变量);不做校验(配置无效时应在启动时快速失败);按环境名写 if/else 代码分支(应由配置改变行为);配置过于复杂(很少变化的值用合理默认值优于可配置);缺少 .env.example(应始终提供展示必填变量的模板)。