云函数 / 云托管的密钥与环境变量分层管理
一句话定义:把云函数 / 云托管 / 前端三层的环境变量用 dev / staging / prod 三套配置隔开,本地走
.env+.gitignore、CI 走tcb login --apiKeyId/--apiKey注入永久密钥 + 流水线变量、生产走控制台环境变量,确保任何一层 secret 都不会进入 git 历史或前端 bundle。预计耗时:30 分钟 | 难度:中级
适用场景
- 已经部署了云函数 / 云托管服务,里面有 LLM API key、数据库密码、第三方服务凭证
- 想把团队从"本地
.env散乱、生产环境依赖人工维护控制台"升级到一套规范流程 - 准备接 CI 流水线(工蜂 / 腾讯 CODING / GitHub Actions),需要让流水线能拿到密钥但又不让密钥进仓库
不适用:
- 完全个人项目,团队就一个人,且没有合规要求——那
.env+.gitignore就够了,不用上 CI - 用了第三方专门的 secret manager(HashiCorp Vault、AWS Secrets Manager),那有自己的整套规范
不该做的事
先列清楚什么不能做,比怎么做更重要:
| 反模式 | 为什么不行 |
|---|---|
| 把 secret 写进代码里 | git 历史里永远删不掉,rotate 一次也救不回;commit 内网仓库一样有风险 |
把 secret 写进 package.json 或 package-lock.json | 同上,且这些文件通常都会被提交 |
在 .env.example 里留真实值当"参 考" | 经常有人 cp 完忘了改,真值进了 git |
| 微信小程序 / Web 前端代码里放 API key | 前端代码用户本地都能拿到,反编译/F12 一看就漏;前端任何变量都视作公开 |
用 console.log(process.env.OPENAI_KEY) 调试,提交时没删 | 日志会进监控系统,谁有日志权限谁有 key |
| 给整个团队共用同一个生产 key | 一旦某人离职或离场,整套 key 都要 rotate |
| 在 commit message / PR description 里贴 key 说"修了下这个" | review 一搜 sk- 就能挖出来 |
把 .env 提交到私有仓库觉得"反正没公开" | 团队成员变动、仓库迁移、备份外泄都是入口;私有不等于安全 |
三层环境的分工
┌─────────────────────┐
│ 本地开发(dev) │ .env(本地) + .gitignore + .env.example(模板)
└─────────────────────┘
↓
┌─────────────────────┐
│ CI 流水线 │ 流水线变量(工蜂/CODING) + tcb login --apiKeyId/--apiKey
└─────────────────────┘
↓
┌─────────────────────┐
│ 生产(prod) │ CloudBase 控制台「环境变量」
└─────────────────────┘
下面分层讲。
第一层:本地开发
.env + .gitignore
项目根目录建:
# 本地开发配置(永远不提交)
touch .env
echo ".env" >> .gitignore
echo ".env.local" >> .gitignore
echo "*.env" >> .gitignore
.env 内容(具体 key 名按你的项目):
# 本地开发用的 LLM 网关代理 token
PROXY_ACCESS_TOKEN=local-dev-token-xxxxxxxxxxxxxxxx
# 上游 LLM 真 key
UPSTREAM_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx
# 本地开发的 PostgreSQL 连接(本机 docker 起的实例)
PG_HOST=127.0.0.1
PG_PASSWORD=local-pg-password
提交一份模板给团队成员复用,不写真值:
touch .env.example
.env.example:
# 复制本文件为 .env 并填入真实值
PROXY_ACCESS_TOKEN=
UPSTREAM_API_KEY=
PG_HOST=
PG_PASSWORD=
.env.example 是允许提交的——它只是文件骨架,不含值。新成员 clone 仓库后第一件事是 cp .env.example .env,然后去找内部文档填值。
Node.js 代码里读 .env
云函数和云托管的 Node 项目里装 dotenv:
npm install --save-dev dotenv
入口文件最顶部:
// 仅本地开发加载 .env;部署到云函数后,环境变量由平台注入,不需要 dotenv
if (process.env.NODE_ENV !== 'production') {
require('dotenv').config();
}
const apiKey = process.env.UPSTREAM_API_KEY;
部署到 CloudBase 后 NODE_ENV=production 是平台默认,不会再尝试读 .env——这一点重要,避免误把 .env 一起打包上去后被加载。
.gitignore 检查清单
提交前一定看一眼 .gitignore 至少包含这些(按需增减):
# 环境变量
.env
.env.*
!.env.example
*.env
# CloudBase CLI 缓存
.cloudbase/
cloudbaserc.json.local
# 各种自动生成的密钥文件
tcb_custom_login.json
*.pem
*.key
firebase-adminsdk-*.json
service-account*.json
!.env.example 是允许 example 模板进 git 的例外。
第二层:CI 流水线
CI 不能拿本地 .env,因为 CI 跑在另一台机器上,且仓库里没有 .env。要走流水线变量。
配置流水线变量
以工蜂(CODING / GitLab 同理)为例:
- 仓库 → 「设置 → 流水线 → 流水线变量」
- 加变量,例如
TCB_DEPLOY_TOKEN、PROD_UPSTREAM_API_KEY、PROD_PG_PASSWORD - 勾「保护」+「掩码」(前者限制只在受保护分支生效,后者让日志里这些值显示成
***)
CloudBase CLI 在 CI 里的非交互登录
CloudBase CLI 在 CI 里不能用浏览器登录(没人按确认按钮)。改用 tcb login --apiKeyId/--apiKey 显式注入腾讯云永久密钥(区别于本地 tcb login 的浏览器登录):
# 1. 用永久密钥非交互登录
tcb login --apiKeyId $TENCENT_SECRET_ID --apiKey $TENCENT_SECRET_KEY
# 2. 部署
tcb fn deploy llm-proxy --httpFn -e your-env-id
TENCENT_SECRET_ID / TENCENT_SECRET_KEY 在腾讯云访问管理 CAM 控制台 创建,仅用于 CI 部署,不要在开发者本地使用(开发者本地用 tcb login 浏览器登录拿临时密钥,遵循最小权限)。
CI 步骤里绝对不要 echo $TENCENT_SECRET_KEY——一旦日志被外人看到等于 key 公开。流水线掩码只是兜底。