生产配置的目标不是把开发 appsettings 填满,而是明确每个值的所有者、来源、变更方式和失败策略。静态默认值可以进仓库,Secret 进入 Secret Store,动态非敏感策略进入受控配置中心,基础设施连接由部署平台注入。
配置进入运行时
配置归属
| 类别 | 例子 | 推荐来源 |
|---|---|---|
| 环境标识 | ASPNETCORE_ENVIRONMENT | 部署声明 |
| Provider 与非敏感策略 | Persistence:Provider、限流窗口 | 版本化配置/AgileConfig |
| 数据库、Redis、RabbitMQ | Connection String、账号密码 | Secret Store 或托管连接注入 |
| 应用签名与厂商凭据 | JWT、SCIM、对象存储 Access Key | Secret Store/KMS |
| 遥测出口 | OTEL_EXPORTER_OTLP_ENDPOINT | 部署环境配置 |
环境变量使用双下划线表示层级,例如 ConnectionStrings__Default。不要把解析后的 Secret 写入 .env 并提交,也不要通过 AgileConfig 下发其自身的认证 Secret。
# ① 仅展示变量名和 Secret 引用;真实值由部署平台在运行时解析。export ASPNETCORE_ENVIRONMENT=Productionexport Persistence__Provider=SqlSugarexport ConnectionStrings__Default='<secret-store:sql-connection>'export ConnectionStrings__redis='<secret-store:redis-connection>'export RabbitMq__Password='<secret-store:rabbitmq-password>'
# ② 部署前构建同一 Release 制品,不在目标环境重新编译不同二进制。dotnet build BitzOrcas.Modern.slnx --configuration Release --no-restore启动守卫
生产启动至少验证:
- 必需连接、签名密钥和 Provider 已配置,长度与格式满足要求。
- API 与 JobHost 选择相同数据库、消息、缓存和数据保护语义。
- 关键 Port 解析到生产实现,而不是
InMemory*、Null*或Unavailable*。 - 文件、邮件、Webhook 等可选能力在未就绪时失败关闭,不产生假成功。
- Health Readiness 能反映必要依赖,但响应不回显连接串或 Secret。
动态配置并不适合所有键。数据库连接池、ORM Provider、密钥环位置和中间件组合通常需要重启;Feature、限流等策略可以热更新,但必须带版本、校验、审计与失败回退。
滚动发布检查
| 场景 | 必须验证 |
|---|---|
| 新旧副本并存 | Schema、消息契约、Data Protection 密钥与缓存 Key 兼容 |
| Secret 轮换 | 重叠窗口、撤销旧值、失败回滚和审计 |
| Provider 切换 | 双 ORM 合同、数据一致性、迁移和回退计划 |
| 配置中心不可用 | 启动/运行采用既定 fail-fast、fail-closed 或最后已知良值策略 |
| JobHost 滞后 | API 写入不依赖未启动后台进程完成同步业务事务 |
API 与 JobHost 的真实启动门禁
Production 与 Staging 都被视为类生产环境。API 缺数据库、RabbitMQ、Redis、OTLP、生产文件存储、Webhook 安全策略或正式 License 时抛 InvalidOperationException 并拒绝启动;Audit:StoreProvider=None 同样被拒绝。
JobHost 至少要求数据库、Redis、RabbitMQ、OTLP、审计存储与正式 License。数据库还承担 Quartz persistent store、Workflow store 与 Audit store,因此“只跑少数作业”也不能省略。
# ① 只设置变量名;值由 Secret Store 注入。TrustedPublicKeys 是数组配置。export Licensing__Runtime__Enabled=trueexport Licensing__Runtime__ProductId=BitzOrcas.Modernexport Licensing__Runtime__DeploymentIdentityPath=/var/lib/bitzorcas/deployment-idexport Licensing__Runtime__CachePath=/var/lib/bitzorcas/license-cacheexport Licensing__Runtime__TrustedPublicKeys__0='<secret-store:license-public-key>'
# ② 类生产环境用实际制品启动;守卫失败必须阻断发布。ASPNETCORE_ENVIRONMENT=Staging dotnet BitzOrcas.Api.dll诊断输出只描述配置是否存在、长度或安全摘要,不应回显 Secret。门禁通过也不代表依赖可达,后续仍由 readiness 验证连通性。
Forwarded Headers 拓扑
API 位于反向代理后时,应显式配置 KnownProxies/KnownNetworks;若直接公网暴露,则使用受控的 ForwardedHeaders:DirectExposure 模式。错误配置会影响客户端 IP、HTTPS 判断、限流、审计归因,以及登录 Cookie 的 Origin 校验(依赖中间件回写后的 Host)。
在 Staging 用真实代理链验证 X-Forwarded-For、X-Forwarded-Proto、X-Forwarded-Host、X-Forwarded-Port 与 WebSocket Upgrade。OpenResty/Nginx 应把入口 Host 和 X-Forwarded-Host 设为 $http_host,以保留非默认端口;OpenResty → Gateway 两跳时 ForwardLimit 通常为 2。不要为了“先启动”而信任任意代理。
生产文档面的配置边界
非 Development 环境默认不映射 /openapi/v1.json 与 /scalar/v1。若内网预览确实需要文档面,同时配置 OpenApi__Enabled=true 与 OpenApi__RequireAuthentication=true:前者映射路由,后者要求正式账号换取 HttpOnly 文档会话。应用访问门只是一层纵深防御,入口仍应使用 HTTPS,并按环境风险叠加 VPN、IP 白名单或网关 SSO。
# 仅示意非敏感配置;公开入口由环境清单管理。export OpenApi__Enabled=trueexport OpenApi__RequireAuthentication=trueexport OpenApi__Servers__0__Url=https://api-preview.example.comexport OpenApi__Servers__0__Description='受保护的预览网关'export OpenApi__PersistAuthentication=falseProduction 不需要交互文档时应保持关闭,并验证两个路由返回 404。共享预览应关闭 Scalar 认证持久化,避免调试令牌留在浏览器本地存储;文档访问会话本身是 HttpOnly Cookie,不写入页面脚本。配置中不得写入账号、密码、API Key、HMAC Secret 或 Token。Server 地址、登录路由和可信代理细节见 OpenAPI 与 Scalar 文档面。
缓存启动预热(Cache:Warmup)
业务读路径默认 Cache-Aside,不依赖启动预热。需要压低字典 / 多语言 / 设置等首包延迟时,可打开 Cache:Warmup(绑定 CacheWarmupOptions,默认关闭)。
| 键 | 默认 | 说明 |
|---|---|---|
Cache:Warmup:EnabledOnStartup | false | 为 true 时 Host 启动后后台预热一次 |
Cache:Warmup:AreaKeys | [] | 空 = 所有 SupportsWarmup 区域;建议显式列出 |
Cache:Warmup:TenantIds | [] | identity-* 等租户区域必须配置;空则只适合平台键区域 |
Cache:Warmup:StartupMode | FillMissing | 启动仅建议补洞;全量重建走 Host API |
Cache:Warmup:MaxDegreeOfParallelism | 2 | 并行区域数 |
Cache:Warmup:AreaTimeout | 00:02:00 | 单区域超时 |
# 环境变量示例:仅预热平台键区域,不扫全部租户。export Cache__Warmup__EnabledOnStartup=trueexport Cache__Warmup__AreaKeys__0=master-dataexport Cache__Warmup__AreaKeys__1=translationsexport Cache__Warmup__AreaKeys__2=settingsexport Cache__Warmup__AreaKeys__3=deliveryexport Cache__Warmup__StartupMode=FillMissing运维侧也可在 Host 控制台对单区域执行 WARM / REBUILD(POST /api/operations/cache/rebuild),无需改配置重启。细节见缓存指南与 Operations 缓存治理。
配置变更流程
- 为键指定 owner、敏感级别、默认值、校验与重启语义。
- 在 Staging 使用与生产相同的注入方式,不粘贴 Secret。
- 运行启动门禁、readiness、配置诊断与核心 smoke。
- 轮换时保留最短必要重叠窗口,再撤销旧值。
- 保存配置版本与审批证据,不保存明文。
完成清单
- API 与 JobHost 的配置矩阵分别验证;
- Production/Staging 都无法落入 Shell、Null 或 None 实现;
- License DeploymentId 与 cache 使用持久、受限路径;
- OTLP、代理、对象存储和 Webhook 策略经过环境验收;
- OpenAPI/Scalar 在生产关闭,或在外层保护后开启且不持久化认证状态;
- Secret 轮换、配置中心不可用和旧副本并存均有回滚路径;
- 若启用
Cache:Warmup:AreaKeys / TenantIds 已收口,并在 Staging 验证启动时延与数据库压力。
配置清单模板
每个环境维护不含值的配置清单:键名、Host、必需/可选、来源、Secret 标志、热更新/重启、Owner、最后验证日期。API 与 JobHost 共用键也要分别验证,避免只在一个 Host 注入。
对布尔开关记录缺失、false、true 三种语义;对 Provider 记录允许值与大小写;对数组记录索引/绑定方式。配置改名必须保留兼容窗口或提供迁移检查。
发布审查只比较清单与版本,不导出环境变量全量值。诊断工具发现未知、缺失或危险组合时应阻断,而不是自动猜测默认值。
- 删除键前先证明所有副本不再读取;
- Secret 版本与应用版本可以独立回滚;
- 配置中心变更必须进入审计;
- 紧急变更在事后补齐评审与演练。