您现在的位置是:网站首页 > 环境配置与多环境管理文章详情

环境配置与多环境管理

环境配置基础

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'));
}

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步