Void 的环境变量分开发和生产两套 / 部署前先确认读的是哪一套

Void 的环境变量分开发和生产两套 / 部署前先确认读的是哪一套

2026-10-20 18:11:30 0点赞 0收藏 0评论

本地连测试库一切正常,部署后连错库或者密钥校验失败,多数情况不是密钥本身写错,而是进程启动时读到的文件与你在本地改的不是同一份。部署前要确认的只有一件事:这个进程的环境变量从哪里来、按什么顺序加载、最终生效的是哪一套。确认方式不复杂:看启动日志里的环境标识,再用一个非敏感变量做一次取值验证,全程不打印任何密钥。

判断路径:先按仓库和部署配置里实际存在的文件清点环境变量来源,不凭印象添加;再在服务启动最早期打印环境标识(如 APP_ENV)和几个非敏感变量,把「读的是哪一套」变成日志里能看到的事实;最后核对生产是读文件还是进程注入,两种情况对应的排查命令不同。全程只观察环境名与非敏感值,形如 KEY、SECRET、连接串的字段都不应出现在日志里。环境标识与预期不符时先停下,不要急着换密钥。

列出项目里所有环境变量文件,标明各自在什么阶段被读取

先不要动任何 .env 文件,先把「有几个来源」列清楚。同一个项目里来源往往不止一个,漏掉一个就会出现「改了没生效」。按下面几类逐项核对,只保留实际存在的:

  • 启动脚本或 shell 配置里 export 的变量(~/.bashrc、.envrc、package.json 的 scripts)

  • 项目根目录的 .env、.env.local、.env.development、.env.production 等文件

  • 构建工具或框架的注入配置(Vite 的 .env.[mode]、Next 的 .env.production 等,通常只暴露带公开前缀的变量)

  • 编辑器调试配置(.vscode/launch.json 的 env / envFile),只影响本地调试进程

  • CI/CD 的变量面板与 Secret 配置

  • 运行时进程注入:容器 env_file / environment、systemd 的 Environment= 与 EnvironmentFile=

加载顺序的一般规律是:shell 已有变量 → 文件加载 → 进程注入显式赋值。dotenv 默认不覆盖已存在的 process.env 键,所以「先出现的值赢了」是很常见的坑;容器编排里通常 environment 会覆盖 env_file。但不要凭规律下结论,要按项目里的实际配置确认。尤其注意相对路径的 .env 是相对进程工作目录解析的,systemd 或容器里的工作目录与本地不同,可能读到另一份文件,或者根本读不到。

在启动日志里打印当前加载的环境名,但不打印任何密钥值

把环境名打成一行日志,比事后翻配置文件可靠得多。这行日志要放在进程最早执行的入口里,早于任何读取 process.env 的业务模块。

Void 的环境变量分开发和生产两套 / 部署前先确认读的是哪一套

// boot-env.ts —— 在应用入口最顶部 import const ALLOWED = ['APP_ENV', 'NODE_ENV', 'LOG_LEVEL', 'SITE_TITLE']; const envName = process.env.APP_ENV ?? process.env.NODE_ENV ?? 'unset'; console.log(`[boot] env=${envName} pid=${process.pid} cwd=${process.cwd()}`); for (const k of ALLOWED) { console.log(`[boot] ${k}=${process.env[k] ?? ''}`); } // 不要写:console.log(process.env)

白名单是硬约束:只输出环境标识、站点标题、日志级别这类非敏感字段。不要图省事整包打印 process.env,也不要在异常堆栈、健康检查接口或构建产物里带出数据库连接串和密钥。cwd 一并打印,是因为相对路径的 .env 依赖工作目录,线上读到错误文件的典型表现就是 cwd 与本地不一致。

如果日志组件在启动早期还没就绪,直接写标准输出或 stderr 即可,容器里一般会被收集。正常部署一次,先看这一行的 env 值是不是预期的 production,再去查别的。

Void 的环境变量分开发和生产两套 / 部署前先确认读的是哪一套

核对生产环境的注入方式:读文件还是进程注入

环境标识对不上时,先分清生产是「读文件」还是「进程注入」。这两种情况查的地方完全不同:读文件要去核对文件路径与工作目录,进程注入要去核对编排配置和 Secret 面板。

读文件

import dotenv from 'dotenv'; import path from 'node:path'; const envName = process.env.APP_ENV ?? 'development'; dotenv.config({ path: path.resolve(process.cwd(), `.env.${envName}`), // 用绝对路径,别依赖隐式 cwd override: false, });

# 确认进程实际工作目录与目标文件 ls -l /proc/$(pgrep -f 'node .*app' | head -n1)/cwd ls -l /srv/app/.env.production grep -E '^(APP_ENV|LOG_LEVEL|SITE_TITLE)=' /srv/app/.env.production

进程注入

services: app: image: registry.example.com/app:tag env_file: .env.production environment: APP_ENV: production LOG_LEVEL: info

[Service] WorkingDirectory=/srv/app EnvironmentFile=/etc/app/production.env Environment=APP_ENV=production

建议同一份变量只保留一种注入来源。Environment= 与 EnvironmentFile= 同时写、env_file 与 environment 同时写,覆盖关系容易记混,出问题时很难一眼看出实际生效值。

# 容器内进程实际环境,只取非敏感键 docker compose exec app printenv APP_ENV NODE_ENV LOG_LEVEL SITE_TITLE # 宿主机上直接读进程环境,只输出匹配行 tr '0' 'n' < /proc/$(pgrep -f 'node .*app' | head -n1)/environ | grep -E '^(APP_ENV|LOG_LEVEL)='

不要在共享终端或 CI 日志里执行不加过滤的 env 命令,输出会包含全部密钥。

Void 的环境变量分开发和生产两套 / 部署前先确认读的是哪一套

用一个非敏感变量做一次取值验证

最后一层验证不需要碰密钥。挑一个非敏感变量,例如站点标题 SITE_TITLE 或日志级别 LOG_LEVEL,把生产环境的文件或注入值改成一个可辨认的值,重启服务,观察前端页面或日志是否跟着变。

  1. 在生产配置里把 SITE_TITLE 改成带标记的值,比如 Acme Console [prod];或者把 LOG_LEVEL 从 info 改成 debug。

  2. 按正常流程重启或重新部署,不手工改运行时进程。

  3. 看浏览器标签页标题、页面头部文案,或检查日志里是否出现 debug 级别条目。

  4. 值生效,说明读到的就是这一份,再回头处理密钥;值没变,说明实际读取的是另一份文件或另一个注入来源,回到上一节继续查。

  5. 验证完把值改回去,不要让调试标记停在生产页面上。

顺序很重要:先确认读的是哪一套,再动密钥。反过来的话,密钥改了不生效,很容易误判成密钥本身有问题,接着反复轮换,排查成本反而更高。

展开 收起
0评论

当前文章无评论,是时候发表评论了
提示信息

取消
确认
评论举报

相关文章推荐

更多精彩文章
更多精彩文章
京东京享红包
距结束::
领京东现金红包 天天领3次 最高26888元
红包按钮
最新文章 热门文章
0
扫一下,分享更方便,购买更轻松