配置中心与密钥管理
问题
如何管理服务端的配置和密钥?不同环境的配置怎么隔离?
答案
配置管理层级
基础:环境变量
config.ts
import { z } from 'zod';
// 使用 Zod 校验环境变量
const envSchema = z.object({
NODE_ENV: z.enum(['development', 'production', 'test']),
PORT: z.coerce.number().default(3000),
DATABASE_URL: z.string().url(),
REDIS_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
SMTP_HOST: z.string().optional(),
});
// 启动时校验,缺少必要配置立即报错
export const env = envSchema.parse(process.env);
多环境配置
config-by-env.ts
interface AppConfig {
db: { host: string; port: number; database: string };
cache: { ttl: number };
log: { level: string };
}
const configs: Record<string, AppConfig> = {
development: {
db: { host: 'localhost', port: 5432, database: 'app_dev' },
cache: { ttl: 60 },
log: { level: 'debug' },
},
production: {
db: { host: env.DB_HOST, port: 5432, database: 'app_prod' },
cache: { ttl: 3600 },
log: { level: 'warn' },
},
};
export const config = configs[env.NODE_ENV] ?? configs.development;
密钥管理最佳实践
| 级别 | 方案 | 适用场景 |
|---|---|---|
| 基础 | .env + .gitignore | 个人开发 |
| 中级 | CI/CD 加密变量 | 小团队 |
| 高级 | AWS Secrets Manager / Vault | 企业级 |
安全红线
- 永远不将 Secret 提交到 Git
- 不在日志中打印密钥
- 不在前端代码中使用服务端密钥
- 定期轮转密钥
secrets-manager.ts
// AWS Secrets Manager 示例
import { SecretsManager } from '@aws-sdk/client-secrets-manager';
const client = new SecretsManager({ region: 'ap-southeast-1' });
async function getSecret(name: string): Promise<string> {
const response = await client.getSecretValue({ SecretId: name });
return response.SecretString!;
}
// 启动时加载密钥
const dbPassword = await getSecret('prod/db-password');
常见面试问题
Q1: .env 文件和环境变量的优先级?
答案:
一般约定:系统环境变量 > .env.local > .env.[mode] > .env。dotenv 默认不覆盖已有环境变量,所以系统环境变量优先级最高。
Q2: 配置热更新怎么实现?
答案:
- 配置中心:应用定时轮询或通过长连接监听变更
- Feature Flags:LaunchDarkly、ConfigCat 等 SaaS 服务
- K8s ConfigMap:挂载为文件,通过
fs.watch监听变化
Q3: 前端和后端的环境变量有什么区别?
答案:
- 前端:构建时注入,打包后不可变,公开暴露(
NEXT_PUBLIC_、VITE_前缀) - 后端:运行时读取,可动态修改,不暴露给客户端