Skip to content

环境与配置管理

"在我本地是好的"是工程史上最经典的借口。它之所以反复出现,根源是环境不一致:开发用一套依赖、测试用一套、生产又是另一套,配置散落各处,密钥混进代码仓库。环境与配置管理的目标就一句话——让同一段代码在不同环境里行为可预期、可复现,且密钥绝不外泄。这件事不做扎实,所有的 CI、测试、回滚都建立在流沙上。

多环境:dev / staging / prod

不同环境承担不同职责,不要混用:

环境作用数据稳定性要求
dev本地开发、调试mock 或脱敏样本不要求,常崩无妨
staging上线前验证、集成测试类生产的脱敏数据尽量稳定,接近生产
prod真实用户真实数据最高,变更受控

关键原则:staging 要尽可能像 prod——同样的镜像、同样的配置结构、同样的依赖版本,只是值不同。staging 和 prod 差异越大,staging 的验证价值越低,上线就是在赌。

常见反模式:staging 连的是测试库的旧数据、跑的是不同的依赖版本、甚至没有和 prod 一样的中间件。这种 staging 过不了的东西,上线照样崩。

配置与代码分离

配置(数据库地址、第三方 key、功能开关、阈值)不应该写死在代码里。Twelve-Factor App 的核心一条:配置通过环境变量注入,代码保持环境无关

text
错误:
  const dbUrl = "mysql://prod-user:pwd@10.0.0.5/orders"

正确:
  const dbUrl = process.env.DB_URL

这样同一个镜像可以跑在所有环境,只是注入的 .env 不同。发布物和环境解耦,是可重复部署的基础。

配置类型存放位置
业务参数、阈值、开关配置中心 / 环境变量
数据库、缓存地址环境变量
API 密钥、证书密钥管理服务(见下)
框架默认值、不环境相关的常量代码里没关系

密钥管理:绝不能进代码仓库

把密钥提交到 git 是工程上的大忌,而且一旦提交,"删除"也无济于事——历史里永远在。正确做法:

text
密钥的生命周期:
  生成 → 存进密钥服务(KMS / Vault / 云厂商 Secret Manager)
       → 运行时按需拉取或注入环境变量
       → 轮换(定期更换)
       → 吊销(泄露时立即失效)

务实的底线:

  • 仓库里加 .env.example 只放占位符,真值永不提交。
  • CI/CD 从密钥服务注入,不在脚本里硬编码。
  • 定期轮换密钥,泄露时能快速吊销而不是全员改密码。

Feature Flag:把发布和解耦分开

传统发布是"上线即生效",风险集中在一刻。Feature Flag(功能开关)把"代码部署"和"功能开启"拆开:

text
代码合并 → 部署到生产(功能默认关闭)
        → 小比例打开开关灰度
        → 观察,有问题一键关闭,无需回滚代码
        → 全量放开
场景Flag 价值
高风险新功能灰度发布,出问题秒级关闭
A/B 测试不同用户走不同逻辑
紧急止血不重新部署就能关掉出问题的功能
未完成功能主干开发,功能未完成也能合并

Flag 不是越多越好。每个 flag 都是技术债——长期不清理的 flag 让代码充满死分支。上线稳定后要及时清理过期 flag。

环境一致性:容器化

保证环境一致最有效的手段是容器化(Docker)。镜像把操作系统、依赖、运行时打包在一起,"在我机器上能跑"变成"在哪里都能跑"。

text
开发:docker-compose up 起一套本地依赖
CI:用同一个 Dockerfile 构建镜像跑测试
生产:部署同一个镜像

要点:dev / staging / prod 用同一个镜像,差异只在配置。不要每个环境自己拼一套 Dockerfile,那样又回到不一致的老路。

常见错误:配置漂移

环境管理最隐蔽的问题是"配置漂移"——时间久了,各环境手工改过一些东西,没人记得改了什么,prod 成了一个谁也不敢动的黑箱。

text
症状:
  - "这个服务器的某个参数当年谁手动调过,别动"
  - 重建一台机器,跑起来行为不一样
  - 灾难恢复时发现没人能从零搭出当前的生产环境

正确做法:所有环境变更必须走代码(基础设施即代码,IaC)。手动 SSH 上去改的东西不进版本控制,就是漂移的源头。无论是 Nginx 配置、防火墙规则还是环境变量,都要能从一份声明文件 + 一条命令完整重建。

务实的检查清单

  • dev / staging / prod 是否用同一个镜像或构建产物,只差配置?
  • 所有配置是否都从环境变量或配置中心读取,没有硬编码?
  • 密钥是否从未进过 git 仓库,是否定期轮换?
  • 是否用 Feature Flag 把高风险发布改成灰度?
  • 环境能否从一份 IaC 声明完整重建,还是依赖某人的记忆?
  • 是否禁止手工 SSH 改配置,所有变更走代码评审?

为复用而记录,为理解而整理。