您现在的位置是:网站首页 > 环境配置与多环境管理文章详情
环境配置与多环境管理
陈川
【
Node.js
】
32820人已围观
9120字
环境配置基础
Express应用的环境配置通常从.env
文件开始。这个文件用于存储敏感信息和环境变量,比如数据库连接字符串、API密钥等。使用dotenv
包可以轻松加载这些变量:
require('dotenv').config();
const express = require('express');
const app = express();
const dbConnection = process.env.DB_CONNECTION;
const apiKey = process.env.API_KEY;
// 使用环境变量
app.get('/', (req, res) => {
res.send(`DB连接: ${dbConnection}`);
});
.env
文件内容示例:
DB_CONNECTION=mongodb://localhost:27017/myapp
API_KEY=your_api_key_here
PORT=3000
多环境管理策略
实际开发中通常需要区分开发、测试和生产环境。Express应用可以通过NODE_ENV
环境变量来识别当前运行环境:
const environment = process.env.NODE_ENV || 'development';
if (environment === 'development') {
console.log('运行在开发环境');
// 加载开发专用中间件
app.use(require('morgan')('dev'));
} else if (environment === 'production') {
console.log('运行在生产环境');
// 启用生产环境优化
app.enable('trust proxy');
}
环境特定配置文件
更复杂的项目可能需要为不同环境维护不同的配置文件。可以创建config
目录,包含多个环境配置文件:
/config
├── default.js
├── development.js
├── test.js
└── production.js
config/default.js
示例:
module.exports = {
appName: 'My Express App',
port: 3000,
logging: {
level: 'info'
}
};
config/production.js
示例:
const config = require('./default');
module.exports = {
...config,
port: process.env.PORT || 80,
logging: {
level: 'error'
},
db: {
url: process.env.DB_URL,
poolSize: 10
}
};
动态配置加载
创建一个配置加载器可以智能地合并默认配置和环境特定配置:
const _ = require('lodash');
const defaultConfig = require('./config/default');
const environment = process.env.NODE_ENV || 'development';
const environmentConfig = require(`./config/${environment}`);
const config = _.merge({}, defaultConfig, environmentConfig);
module.exports = config;
中间件环境适配
某些中间件可能需要根据环境进行不同配置。例如,静态文件服务在生产环境和开发环境可能有不同行为:
const path = require('path');
const express = require('express');
const app = express();
if (process.env.NODE_ENV === 'production') {
// 生产环境使用CDN或优化配置
app.use('/static', express.static(path.join(__dirname, 'dist'), {
maxAge: '1y',
immutable: true
});
} else {
// 开发环境使用更频繁的刷新
app.use('/static', express.static(path.join(__dirname, 'src'), {
maxAge: 0
});
}
数据库连接配置
数据库连接通常是环境敏感的。可以使用工厂模式创建不同环境的数据库连接:
class DatabaseFactory {
static createConnection(env) {
switch(env) {
case 'test':
return new MockDatabase();
case 'development':
return new Database({
host: 'localhost',
port: 27017,
dbName: 'dev_db'
});
case 'production':
return new Database({
host: process.env.DB_HOST,
port: process.env.DB_PORT,
dbName: process.env.DB_NAME,
ssl: true
});
default:
throw new Error(`未知环境: ${env}`);
}
}
}
const db = DatabaseFactory.createConnection(process.env.NODE_ENV);
日志配置
日志系统通常需要根据环境调整详细程度:
const winston = require('winston');
const logger = winston.createLogger({
level: process.env.NODE_ENV === 'development' ? 'debug' : 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.Console(),
process.env.NODE_ENV === 'production' &&
new winston.transports.File({ filename: 'combined.log' })
].filter(Boolean)
});
// 使用示例
logger.info('应用启动', { environment: process.env.NODE_ENV });
测试环境特殊处理
测试环境通常需要模拟服务和特殊配置:
if (process.env.NODE_ENV === 'test') {
// 使用内存数据库
const { MongoMemoryServer } = require('mongodb-memory-server');
before(async () => {
const mongod = new MongoMemoryServer();
process.env.DB_URL = await mongod.getConnectionString();
});
// 禁用某些中间件
app.use((req, res, next) => {
if (req.path === '/metrics') return res.status(403).end();
next();
});
}
部署配置自动化
在CI/CD流程中自动设置环境变量:
# .github/workflows/deploy.yml 示例
name: Deploy to Production
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: npm install
- run: npm test
- run: npm run build
- env:
NODE_ENV: production
DB_URL: ${{ secrets.PRODUCTION_DB_URL }}
API_KEY: ${{ secrets.PRODUCTION_API_KEY }}
run: npm start
环境变量验证
使用envalid
包验证环境变量:
const { cleanEnv, str, port, bool } = require('envalid');
const env = cleanEnv(process.env, {
NODE_ENV: str({ choices: ['development', 'test', 'production'] }),
PORT: port({ default: 3000 }),
DB_URL: str(),
ENABLE_CACHE: bool({ default: false })
});
// 使用验证后的变量
app.listen(env.PORT, () => {
console.log(`Server running in ${env.NODE_ENV} mode on port ${env.PORT}`);
});
前端环境集成
前后端分离项目中,前端也需要知道当前环境:
// Express API端点返回环境信息
app.get('/api/env', (req, res) => {
res.json({
env: process.env.NODE_ENV,
apiBaseUrl: process.env.API_BASE_URL,
features: {
analytics: process.env.FEATURE_ANALYTICS === 'true'
}
});
});
// 前端使用
fetch('/api/env')
.then(res => res.json())
.then(envConfig => {
if (envConfig.env === 'development') {
console.log('开发模式调试信息');
}
});
环境切换工具
开发时可能需要快速切换环境,可以创建命令行工具:
#!/usr/bin/env node
// scripts/env-switch.js
const fs = require('fs');
const args = process.argv.slice(2);
const validEnvs = ['dev', 'test', 'prod'];
if (!args.length || !validEnvs.includes(args[0])) {
console.error(`用法: env-switch [${validEnvs.join('|')}]`);
process.exit(1);
}
const envMap = {
dev: 'development',
test: 'test',
prod: 'production'
};
fs.writeFileSync('.env', `NODE_ENV=${envMap[args[0]]}`);
console.log(`切换到 ${envMap[args[0]]} 环境`);
配置加密与安全
敏感配置应该加密存储:
const crypto = require('crypto');
const algorithm = 'aes-256-cbc';
const key = Buffer.from(process.env.CONFIG_ENCRYPTION_KEY, 'hex');
const iv = Buffer.from(process.env.CONFIG_ENCRYPTION_IV, 'hex');
function encrypt(text) {
const cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
return encrypted;
}
function decrypt(encrypted) {
const decipher = crypto.createDecipheriv(algorithm, key, iv);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
// 加密数据库配置
const encryptedDBConfig = encrypt(JSON.stringify({
host: 'db.example.com',
user: 'admin',
password: 'secret'
}));
// 运行时解密
const dbConfig = JSON.parse(decrypt(encryptedDBConfig));
环境感知错误处理
根据环境返回不同详细程度的错误信息:
app.use((err, req, res, next) => {
const isProduction = process.env.NODE_ENV === 'production';
res.status(err.status || 500).json({
error: isProduction ? '服务器错误' : err.message,
stack: isProduction ? undefined : err.stack,
details: isProduction ? undefined : err.details
});
});
配置版本控制
.env
文件不应该提交到版本控制,但可以提交示例文件:
# .env.example
DB_HOST=localhost
DB_PORT=27017
DB_NAME=example
API_KEY=your_key_here
然后在项目文档中说明:
# 首次设置
cp .env.example .env
# 然后编辑.env文件填入实际值
环境相关脚本
package.json
中可以定义环境相关的脚本:
{
"scripts": {
"start": "node app.js",
"dev": "NODE_ENV=development nodemon app.js",
"test": "NODE_ENV=test jest",
"start:prod": "NODE_ENV=production node app.js",
"debug": "NODE_ENV=development node --inspect app.js"
}
}
容器化环境配置
使用Docker时可以通过环境变量文件:
# Dockerfile
FROM node:14
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "start"]
# docker-compose.yml
version: '3'
services:
app:
build: .
ports:
- "3000:3000"
env_file:
- .env.docker
environment:
- NODE_ENV=production
.env.docker
文件示例:
DB_HOST=postgres
DB_PORT=5432
DB_USER=app_user
DB_PASSWORD=app_password
配置热重载
开发环境下可以监听配置文件变化:
const fs = require('fs');
const path = require('path');
const configPath = path.join(__dirname, 'config.json');
let config = JSON.parse(fs.readFileSync(configPath));
if (process.env.NODE_ENV === 'development') {
fs.watchFile(configPath, (curr, prev) => {
if (curr.mtime !== prev.mtime) {
try {
config = JSON.parse(fs.readFileSync(configPath));
console.log('配置已重新加载');
} catch (err) {
console.error('配置重载失败:', err);
}
}
});
}
环境标记中间件
为所有响应添加环境标记:
app.use((req, res, next) => {
res.set('X-Environment', process.env.NODE_ENV);
res.set('X-App-Version', process.env.APP_VERSION || '1.0.0');
next();
});
功能开关配置
基于环境配置功能开关:
const features = {
newDashboard: process.env.FEATURE_DASHBOARD === 'true',
experimentalAPI: process.env.NODE_ENV !== 'production'
&& process.env.FEATURE_EXPERIMENTAL_API === 'true'
};
app.get('/features', (req, res) => {
res.json(features);
});
// 使用功能开关
if (features.newDashboard) {
app.use('/dashboard', require('./routes/new-dashboard'));
} else {
app.use('/dashboard', require('./routes/legacy-dashboard'));
}
上一篇: 项目结构与目录组织规范
下一篇: 路由分层与模块化设计