Go 后端配置管理:环境变量、配置文件与密钥的三层做法
Go 后端配置管理:环境变量、配置文件与密钥的三层做法
点餐小程序的 Go 后端要跑在华为云 Flexus L 上,本地开发又在 Mac 上。同一份代码,两个环境,端口、数据库地址、微信小程序的 AppID 全都不一样。最开始我把这些值直接写死在代码里,改一次配置就要改代码、重新编译,本地一不小心就把生产数据库地址提交进了仓库。后来我把配置拆成三层,开发、测试、生产各走各的路,代码一次编译,到处都能跑。
第一层是默认值。程序启动时先给每个配置项一个开发环境能直接用的默认值,比如监听 127.0.0.1:8080、数据库连本地。默认值写在代码里,好处是新同事拉下代码不用配任何东西就能跑起来;坏处是默认值只能是最安全的开发值,绝不能是生产值。我给自己立了规矩:默认值里不许出现任何真实密钥、真实域名和生产地址,违反这条就等同于把钥匙挂在门上。
第二层是配置文件。不同环境各一份 YAML,比如 config.dev.yaml、config.prod.yaml,服务启动时用一个环境变量 CONFIG_FILE 指定读哪一份。配置文件里放的是随环境变化但不敏感的值:监听端口、日志级别、Redis 地址、接口限流阈值。配置文件要进版本控制,这样每次改配置都有记录,回滚也有据可查。但这里有个坑:配置文件一旦进了仓库,就一定会被人当成放密钥的地方。所以我的做法是配置文件里只留占位符,真正敏感的值一律不写进去。
第三层是环境变量,专门放密钥。数据库密码、微信 AppSecret、JWT 签名密钥这些,只从环境变量读,启动脚本里从服务器上的受保护文件注入,绝不进代码、绝不进仓库。Go 里读环境变量很简单,os.Getenv 配合一个带默认值的辅助函数,读不到就启动失败并明确报错,而不是悄悄用空值跑起来。我还在启动时打一条脱敏后的配置摘要日志:只打印配置项的名字和是否已设置,不打印值本身。这样出问题时一眼就能看出是哪个配置没注入,又不会把密钥写进日志。
三层之间的优先级要固定:环境变量 > 配置文件 > 默认值。启动时按这个顺序逐项合并,最后做一次完整性校验:必填项缺失就拒绝启动,非必填项记一条警告。这个校验帮我拦过两次事故:一次是部署脚本漏传了 Redis 密码,服务在启动阶段就报错退出,而不是上线后下单失败才发现;另一次是配置文件手误写错了端口,校验发现端口与安全组放行规则对不上,部署前就拦住了。
还有一个容易忽略的点:配置变更要和代码变更分开上线。改限流阈值、改日志级别这种事,不应该重新编译发布整个服务。我的做法是把高频调整的配置项做成可热加载:服务监听配置文件的变化,收到信号后重新读取并平滑生效;密钥类配置不参与热加载,改密钥必须重启服务。这样既能在饭点临时调大限流阈值,又不会因为一次配置热加载把密钥暴露在 reload 路径上。
复盘下来,配置管理的核心就三句话:默认值只给开发用,配置文件只放不敏感的值,密钥只走环境变量。再加上一条启动校验,小服务器的部署就稳了一大半。配置看起来是小事,但它是生产事故最常见的入口,花半天把三层搭好,比半夜起来救火划算得多。
- 点赞
- 收藏
- 关注作者
评论(0)