Go程序通过GO_ENV环境变量区分环境,需手动解析并加载对应配置文件,推荐用viper支持多环境配置,小项目可直接用os.Getenv,容器化部署应避免敏感信息泄漏。

Go 程序如何通过 GO_ENV 或 ENV 区分环境
Go 本身不内置环境变量概念,必须靠外部传入 + 自定义解析。最常用且轻量的方式是读取 GO_ENV(或你自定义的 ENV)环境变量,再加载对应配置文件。不要依赖编译标签(//go:build)做运行时环境切换——它只在构建期生效,无法支持同一二进制在不同机器上跑不同环境。
实操建议:
- 启动前设好环境变量:
GO_ENV=production go run main.go,或部署时在 systemd / Docker / k8s 中注入 - 统一用小写值:
development、staging、production,避免大小写判断出错 - 务必设置默认 fallback,比如没设
GO_ENV时自动走development
用 viper 加载不同环境的 YAML/JSON 配置文件
viper 是最常用的 Go 配置库,原生支持多环境文件名匹配(如 config.development.yaml),但要注意它不会自动识别 GO_ENV —— 必须手动调用 viper.SetEnvKeyReplacer 和 viper.AutomaticEnv(),否则读不到环境变量。
关键步骤:
立即学习“go语言免费学习笔记(深入)”;
- 先调用
viper.SetConfigName("config"),再用viper.AddConfigPath(".") - 在读取前执行
viper.SetEnvPrefix("app"),这样APP_HTTP_PORT就能映射到http.port - 用
viper.SetConfigType("yaml")显式指定格式,避免自动推断失败 - 最后调用
viper.ReadInConfig(),它会按顺序尝试config.→GO_ENV.yamlconfig.yaml
viper.SetConfigName("config")
viper.AddConfigPath(".")
viper.SetConfigType("yaml")
viper.SetEnvPrefix("app")
viper.AutomaticEnv()
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
err := viper.ReadInConfig()
if err != nil {
panic(fmt.Errorf("fatal error config file: %w", err))
}
os.Getenv 直接读取环境变量的适用边界
如果项目极小(比如只有 2–3 个配置项),完全没必要引入 viper。直接用标准库 os.Getenv 更清晰、无依赖、启动更快。
但要注意:
-
os.Getenv返回空字符串不代表变量未设置,可能是显式设为空 —— 判断是否存在得用value, ok := os.LookupEnv("DB_HOST") - 敏感配置(如密码、密钥)应始终从环境变量读取,而不是硬编码在 YAML 里
- 别在
init()函数里读环境变量 —— 单元测试时容易被污染,建议封装成函数,在main()开始处集中读取并校验
Docker 和 Kubernetes 中如何安全注入环境并避免泄漏
容器化部署时,GO_ENV 和数据库地址等必须通过 env 注入,但切忌把整个 .env 文件 COPY 进镜像 —— 构建缓存和镜像层会残留敏感信息。
正确做法:
- Docker:用
--env-file启动时传入,或在docker-compose.yml的environment:下明确列出非敏感变量,敏感项用secrets: - Kubernetes:用
ConfigMap存非敏感配置(如GO_ENV: production),用Secret存加密字段;通过envFrom:批量注入,而非单个env:罗列 - 永远在入口脚本里加检查:
if [ -z "$GO_ENV" ]; then echo "GO_ENV is required"; exit 1; fi
环境切换真正的复杂点不在代码,而在于配置来源的一致性:开发用 .env,测试用 ConfigMap,生产用 Secret + 外部 Vault,这些路径必须在代码里抽象掉,不能让业务逻辑感知到底层怎么来的。










