Skip to content

环境管理指南

本指南介绍如何在 Trae IDE 中有效管理开发环境,包括环境变量、配置管理和部署环境设置。

概述

环境管理是现代软件开发的重要组成部分。本指南涵盖:

  • 环境变量管理
  • 配置文件管理
  • 多环境部署
  • 环境隔离策略
  • 安全最佳实践

环境变量管理

基本概念

环境变量是在操作系统级别定义的动态值,可以被运行的程序访问。它们用于:

  • 存储配置信息
  • 管理敏感数据
  • 区分不同环境
  • 控制应用程序行为

环境变量类型

开发环境变量

bash
# .env.development
NODE_ENV=development
API_URL=http://localhost:3001
DEBUG=true
LOG_LEVEL=debug
DATABASE_URL=postgresql://localhost:5432/myapp_dev
REDIS_URL=redis://localhost:6379

测试环境变量

bash
# .env.test
NODE_ENV=test
API_URL=http://localhost:3001
DEBUG=false
LOG_LEVEL=error
DATABASE_URL=postgresql://localhost:5432/myapp_test
REDIS_URL=redis://localhost:6379

生产环境变量

bash
# .env.production
NODE_ENV=production
API_URL=https://api.myapp.com
DEBUG=false
LOG_LEVEL=info
DATABASE_URL=${DATABASE_URL}
REDIS_URL=${REDIS_URL}

环境变量加载

使用 dotenv

javascript
// 安装 dotenv
npm install dotenv

// 在应用程序入口点加载
require('dotenv').config();

// 或指定环境文件
require('dotenv').config({
  path: `.env.${process.env.NODE_ENV || 'development'}`
});

// 使用环境变量
const config = {
  port: process.env.PORT || 3000,
  apiUrl: process.env.API_URL,
  dbUrl: process.env.DATABASE_URL,
  logLevel: process.env.LOG_LEVEL || 'info'
};

环境变量验证

javascript
// config/env.js
const joi = require('joi');

const envSchema = joi.object({
  NODE_ENV: joi.string()
    .valid('development', 'test', 'production')
    .default('development'),
  PORT: joi.number().default(3000),
  API_URL: joi.string().uri().required(),
  DATABASE_URL: joi.string().required(),
  JWT_SECRET: joi.string().min(32).required(),
  LOG_LEVEL: joi.string()
    .valid('error', 'warn', 'info', 'debug')
    .default('info')
}).unknown();

const { error, value: envVars } = envSchema.validate(process.env);

if (error) {
  throw new Error(`配置验证错误: ${error.message}`);
}

module.exports = {
  env: envVars.NODE_ENV,
  port: envVars.PORT,
  apiUrl: envVars.API_URL,
  database: {
    url: envVars.DATABASE_URL
  },
  jwt: {
    secret: envVars.JWT_SECRET
  },
  logging: {
    level: envVars.LOG_LEVEL
  }
};

Trae IDE 环境变量配置

项目级环境变量

json
// .trae/environment.json
{
  "environments": {
    "development": {
      "variables": {
        "API_URL": "http://localhost:3001",
        "DEBUG": "true",
        "LOG_LEVEL": "debug"
      }
    },
    "staging": {
      "variables": {
        "API_URL": "https://staging-api.myapp.com",
        "DEBUG": "false",
        "LOG_LEVEL": "info"
      }
    },
    "production": {
      "variables": {
        "API_URL": "https://api.myapp.com",
        "DEBUG": "false",
        "LOG_LEVEL": "warn"
      }
    }
  },
  "secrets": [
    "DATABASE_URL",
    "JWT_SECRET",
    "API_KEY"
  ]
}

全局环境变量

json
// ~/.trae/global-env.json
{
  "variables": {
    "EDITOR": "trae",
    "BROWSER": "chrome",
    "TERM": "xterm-256color"
  },
  "paths": [
    "/usr/local/bin",
    "~/.local/bin",
    "./node_modules/.bin"
  ]
}

配置文件管理

配置文件结构

分层配置

config/
├── default.json          # 默认配置
├── development.json      # 开发环境配置
├── test.json            # 测试环境配置
├── staging.json         # 预发布环境配置
├── production.json      # 生产环境配置
└── local.json           # 本地覆盖配置(不提交到版本控制)

默认配置

json
// config/default.json
{
  "app": {
    "name": "MyApp",
    "version": "1.0.0",
    "port": 3000
  },
  "database": {
    "host": "localhost",
    "port": 5432,
    "name": "myapp",
    "pool": {
      "min": 2,
      "max": 10
    }
  },
  "redis": {
    "host": "localhost",
    "port": 6379,
    "db": 0
  },
  "logging": {
    "level": "info",
    "format": "json"
  },
  "security": {
    "jwt": {
      "expiresIn": "24h"
    },
    "bcrypt": {
      "rounds": 12
    }
  }
}

环境特定配置

json
// config/development.json
{
  "app": {
    "port": 3000
  },
  "database": {
    "name": "myapp_dev",
    "logging": true
  },
  "logging": {
    "level": "debug",
    "format": "pretty"
  },
  "security": {
    "bcrypt": {
      "rounds": 1
    }
  }
}
json
// config/production.json
{
  "app": {
    "port": 80
  },
  "database": {
    "pool": {
      "min": 5,
      "max": 20
    },
    "ssl": true
  },
  "logging": {
    "level": "warn"
  },
  "security": {
    "jwt": {
      "expiresIn": "1h"
    }
  }
}

配置加载器

javascript
// config/index.js
const config = require('config');
const path = require('path');

class ConfigManager {
  constructor() {
    this.config = config;
    this.environment = process.env.NODE_ENV || 'development';
  }

  get(key, defaultValue = null) {
    try {
      return this.config.get(key);
    } catch (error) {
      if (defaultValue !== null) {
        return defaultValue;
      }
      throw error;
    }
  }

  has(key) {
    return this.config.has(key);
  }

  getAll() {
    return this.config.util.toObject();
  }

  validate() {
    const requiredKeys = [
      'app.name',
      'app.port',
      'database.host',
      'database.name'
    ];

    const missing = requiredKeys.filter(key => !this.has(key));
    
    if (missing.length > 0) {
      throw new Error(`缺少必需的配置键: ${missing.join(', ')}`);
    }
  }

  isDevelopment() {
    return this.environment === 'development';
  }

  isProduction() {
    return this.environment === 'production';
  }

  isTest() {
    return this.environment === 'test';
  }
}

module.exports = new ConfigManager();

多环境部署

环境定义

开发环境

yaml
# docker-compose.dev.yml
version: '3.8'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
      - DEBUG=true
    volumes:
      - .:/app
      - /app/node_modules
    depends_on:
      - postgres
      - redis

  postgres:
    image: postgres:13
    environment:
      - POSTGRES_DB=myapp_dev
      - POSTGRES_USER=dev
      - POSTGRES_PASSWORD=dev123
    ports:
      - "5432:5432"
    volumes:
      - postgres_dev_data:/var/lib/postgresql/data

  redis:
    image: redis:6-alpine
    ports:
      - "6379:6379"

volumes:
  postgres_dev_data:

测试环境

yaml
# docker-compose.test.yml
version: '3.8'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.test
    environment:
      - NODE_ENV=test
      - DATABASE_URL=postgresql://test:test123@postgres:5432/myapp_test
    depends_on:
      - postgres

  postgres:
    image: postgres:13
    environment:
      - POSTGRES_DB=myapp_test
      - POSTGRES_USER=test
      - POSTGRES_PASSWORD=test123
    tmpfs:
      - /var/lib/postgresql/data

生产环境

yaml
# docker-compose.prod.yml
version: '3.8'
services:
  app:
    image: myapp:latest
    ports:
      - "80:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=${DATABASE_URL}
      - REDIS_URL=${REDIS_URL}
      - JWT_SECRET=${JWT_SECRET}
    restart: unless-stopped
    depends_on:
      - postgres
      - redis

  postgres:
    image: postgres:13
    environment:
      - POSTGRES_DB=${POSTGRES_DB}
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
    volumes:
      - postgres_prod_data:/var/lib/postgresql/data
    restart: unless-stopped

  redis:
    image: redis:6-alpine
    restart: unless-stopped

volumes:
  postgres_prod_data:

部署脚本

环境部署脚本

bash
#!/bin/bash
# deploy.sh

set -e

ENVIRONMENT=${1:-development}
VERSION=${2:-latest}

echo "正在部署到 $ENVIRONMENT 环境..."

case $ENVIRONMENT in
  "development")
    echo "启动开发环境..."
    docker-compose -f docker-compose.dev.yml up -d
    ;;
  "test")
    echo "运行测试..."
    docker-compose -f docker-compose.test.yml up --abort-on-container-exit
    docker-compose -f docker-compose.test.yml down
    ;;
  "staging")
    echo "部署到预发布环境..."
    docker-compose -f docker-compose.staging.yml up -d
    ;;
  "production")
    echo "部署到生产环境..."
    docker-compose -f docker-compose.prod.yml up -d
    ;;
  *)
    echo "未知环境: $ENVIRONMENT"
    echo "用法: $0 [development|test|staging|production] [version]"
    exit 1
    ;;
esac

echo "部署到 $ENVIRONMENT 完成!"

环境切换脚本

bash
#!/bin/bash
# switch-env.sh

ENVIRONMENT=$1

if [ -z "$ENVIRONMENT" ]; then
  echo "用法: $0 <environment>"
  echo "可用环境: development, test, staging, production"
  exit 1
fi

# 检查环境文件是否存在
if [ ! -f ".env.$ENVIRONMENT" ]; then
  echo "环境文件 .env.$ENVIRONMENT 未找到!"
  exit 1
fi

# 备份当前环境文件
if [ -f ".env" ]; then
  cp .env .env.backup
fi

# 切换到新环境
cp ".env.$ENVIRONMENT" .env

echo "已切换到 $ENVIRONMENT 环境"
echo "当前环境变量:"
cat .env

环境隔离策略

数据库隔离

数据库命名约定

javascript
// config/database.js
const environments = {
  development: {
    database: 'myapp_development',
    username: 'dev_user',
    password: 'dev_password',
    host: 'localhost',
    port: 5432,
    dialect: 'postgres',
    logging: console.log
  },
  test: {
    database: 'myapp_test',
    username: 'test_user',
    password: 'test_password',
    host: 'localhost',
    port: 5432,
    dialect: 'postgres',
    logging: false
  },
  staging: {
    database: 'myapp_staging',
    username: process.env.DB_USERNAME,
    password: process.env.DB_PASSWORD,
    host: process.env.DB_HOST,
    port: process.env.DB_PORT,
    dialect: 'postgres',
    logging: false
  },
  production: {
    database: 'myapp_production',
    username: process.env.DB_USERNAME,
    password: process.env.DB_PASSWORD,
    host: process.env.DB_HOST,
    port: process.env.DB_PORT,
    dialect: 'postgres',
    logging: false,
    pool: {
      max: 20,
      min: 5,
      acquire: 30000,
      idle: 10000
    }
  }
};

module.exports = environments[process.env.NODE_ENV || 'development'];

服务隔离

Docker 网络隔离

yaml
# docker-compose.yml
version: '3.8'

networks:
  development:
    driver: bridge
  staging:
    driver: bridge
  production:
    driver: bridge

services:
  app-dev:
    build: .
    networks:
      - development
    environment:
      - NODE_ENV=development

  app-staging:
    build: .
    networks:
      - staging
    environment:
      - NODE_ENV=staging

  app-prod:
    build: .
    networks:
      - production
    environment:
      - NODE_ENV=production

资源隔离

Kubernetes 命名空间

yaml
# k8s/namespaces.yml
apiVersion: v1
kind: Namespace
metadata:
  name: myapp-development
---
apiVersion: v1
kind: Namespace
metadata:
  name: myapp-staging
---
apiVersion: v1
kind: Namespace
metadata:
  name: myapp-production
yaml
# k8s/deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: myapp-production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: myapp:latest
        env:
        - name: NODE_ENV
          value: "production"
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: myapp-secrets
              key: database-url
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"

安全最佳实践

敏感信息管理

使用密钥管理服务

javascript
// utils/secrets.js
const AWS = require('aws-sdk');
const secretsManager = new AWS.SecretsManager({
  region: process.env.AWS_REGION || 'us-east-1'
});

class SecretsManager {
  async getSecret(secretName) {
    try {
      const result = await secretsManager.getSecretValue({
        SecretId: secretName
      }).promise();
      
      return JSON.parse(result.SecretString);
    } catch (error) {
      console.error(`获取密钥 ${secretName} 失败:`, error);
      throw error;
    }
  }

  async getDatabaseCredentials() {
    const secrets = await this.getSecret('myapp/database');
    return {
      host: secrets.host,
      port: secrets.port,
      username: secrets.username,
      password: secrets.password,
      database: secrets.database
    };
  }

  async getJWTSecret() {
    const secrets = await this.getSecret('myapp/jwt');
    return secrets.secret;
  }
}

module.exports = new SecretsManager();

环境变量加密

bash
# 使用 sops 加密环境变量
# 安装 sops
brew install sops

# 创建加密的环境文件
sops -e .env.production > .env.production.encrypted

# 解密环境文件
sops -d .env.production.encrypted > .env.production
yaml
# .sops.yaml
creation_rules:
  - path_regex: \.env\.production$
    kms: 'arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012'
  - path_regex: \.env\.staging$
    kms: 'arn:aws:kms:us-east-1:123456789012:key/87654321-4321-4321-4321-210987654321'

访问控制

基于角色的访问控制

javascript
// middleware/rbac.js
const roles = {
  developer: {
    environments: ['development', 'test'],
    permissions: ['read', 'write', 'deploy']
  },
  tester: {
    environments: ['test', 'staging'],
    permissions: ['read', 'deploy']
  },
  devops: {
    environments: ['development', 'test', 'staging', 'production'],
    permissions: ['read', 'write', 'deploy', 'admin']
  },
  admin: {
    environments: ['development', 'test', 'staging', 'production'],
    permissions: ['read', 'write', 'deploy', 'admin', 'manage_users']
  }
};

function checkEnvironmentAccess(userRole, environment) {
  const role = roles[userRole];
  if (!role) {
    throw new Error('无效的用户角色');
  }
  
  return role.environments.includes(environment);
}

function checkPermission(userRole, permission) {
  const role = roles[userRole];
  if (!role) {
    throw new Error('无效的用户角色');
  }
  
  return role.permissions.includes(permission);
}

module.exports = {
  checkEnvironmentAccess,
  checkPermission
};

审计日志

javascript
// utils/audit.js
const winston = require('winston');

const auditLogger = winston.createLogger({
  level: 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json()
  ),
  transports: [
    new winston.transports.File({ 
      filename: 'logs/audit.log',
      maxsize: 10485760, // 10MB
      maxFiles: 5
    }),
    new winston.transports.Console()
  ]
});

function logEnvironmentAccess(user, environment, action, result) {
  auditLogger.info('环境访问', {
    user: user.id,
    username: user.username,
    environment,
    action,
    result,
    timestamp: new Date().toISOString(),
    ip: user.ip,
    userAgent: user.userAgent
  });
}

function logConfigurationChange(user, environment, changes) {
  auditLogger.info('配置变更', {
    user: user.id,
    username: user.username,
    environment,
    changes,
    timestamp: new Date().toISOString()
  });
}

module.exports = {
  logEnvironmentAccess,
  logConfigurationChange
};

环境监控

健康检查

javascript
// routes/health.js
const express = require('express');
const router = express.Router();
const config = require('../config');

// 基本健康检查
router.get('/health', (req, res) => {
  res.json({
    status: 'healthy',
    timestamp: new Date().toISOString(),
    environment: config.get('app.env'),
    version: config.get('app.version')
  });
});

// 详细健康检查
router.get('/health/detailed', async (req, res) => {
  const checks = {
    database: await checkDatabase(),
    redis: await checkRedis(),
    external_api: await checkExternalAPI(),
    disk_space: await checkDiskSpace(),
    memory: checkMemoryUsage()
  };

  const allHealthy = Object.values(checks).every(check => check.status === 'healthy');
  
  res.status(allHealthy ? 200 : 503).json({
    status: allHealthy ? 'healthy' : 'unhealthy',
    timestamp: new Date().toISOString(),
    environment: config.get('app.env'),
    checks
  });
});

async function checkDatabase() {
  try {
    // 数据库连接检查
    await db.raw('SELECT 1');
    return { status: 'healthy', response_time: '< 100ms' };
  } catch (error) {
    return { status: 'unhealthy', error: error.message };
  }
}

async function checkRedis() {
  try {
    // Redis 连接检查
    await redis.ping();
    return { status: 'healthy', response_time: '< 50ms' };
  } catch (error) {
    return { status: 'unhealthy', error: error.message };
  }
}

function checkMemoryUsage() {
  const used = process.memoryUsage();
  const total = used.heapTotal;
  const usage = (used.heapUsed / total) * 100;
  
  return {
    status: usage < 90 ? 'healthy' : 'warning',
    heap_used: `${Math.round(used.heapUsed / 1024 / 1024)} MB`,
    heap_total: `${Math.round(total / 1024 / 1024)} MB`,
    usage_percentage: `${Math.round(usage)}%`
  };
}

module.exports = router;

环境指标收集

javascript
// utils/metrics.js
const prometheus = require('prom-client');

// 创建指标收集器
const register = new prometheus.Registry();

// 环境特定指标
const environmentInfo = new prometheus.Gauge({
  name: 'app_environment_info',
  help: 'Application environment information',
  labelNames: ['environment', 'version', 'node_version'],
  registers: [register]
});

// 请求计数器
const httpRequestsTotal = new prometheus.Counter({
  name: 'http_requests_total',
  help: 'Total number of HTTP requests',
  labelNames: ['method', 'route', 'status_code', 'environment'],
  registers: [register]
});

// 响应时间直方图
const httpRequestDuration = new prometheus.Histogram({
  name: 'http_request_duration_seconds',
  help: 'Duration of HTTP requests in seconds',
  labelNames: ['method', 'route', 'environment'],
  buckets: [0.1, 0.5, 1, 2, 5],
  registers: [register]
});

// 数据库连接池指标
const dbConnectionsActive = new prometheus.Gauge({
  name: 'db_connections_active',
  help: 'Number of active database connections',
  labelNames: ['environment'],
  registers: [register]
});

// 初始化环境信息
environmentInfo.set(
  {
    environment: process.env.NODE_ENV,
    version: process.env.APP_VERSION,
    node_version: process.version
  },
  1
);

// 中间件:记录 HTTP 请求指标
function metricsMiddleware(req, res, next) {
  const start = Date.now();
  
  res.on('finish', () => {
    const duration = (Date.now() - start) / 1000;
    const environment = process.env.NODE_ENV;
    
    httpRequestsTotal.inc({
      method: req.method,
      route: req.route?.path || req.path,
      status_code: res.statusCode,
      environment
    });
    
    httpRequestDuration.observe(
      {
        method: req.method,
        route: req.route?.path || req.path,
        environment
      },
      duration
    );
  });
  
  next();
}

// 导出指标
function getMetrics() {
  return register.metrics();
}

module.exports = {
  register,
  metricsMiddleware,
  getMetrics,
  dbConnectionsActive
};

故障排除

常见问题

环境变量未加载

javascript
// 调试环境变量加载
console.log('NODE_ENV:', process.env.NODE_ENV);
console.log('All environment variables:', process.env);

// 检查 .env 文件是否存在
const fs = require('fs');
const path = require('path');

const envFile = path.join(process.cwd(), '.env');
if (fs.existsSync(envFile)) {
  console.log('.env 文件存在');
  console.log('内容:', fs.readFileSync(envFile, 'utf8'));
} else {
  console.log('.env 文件未找到');
}

配置文件冲突

javascript
// 调试配置加载顺序
const config = require('config');

console.log('Config sources:', config.util.getConfigSources());
console.log('Final config:', config.util.toObject());

数据库连接问题

javascript
// 数据库连接诊断
async function diagnoseDatabaseConnection() {
  const config = require('./config/database');
  
  console.log('Database config:', {
    host: config.host,
    port: config.port,
    database: config.database,
    username: config.username
    // 不要打印密码
  });
  
  try {
    const { Client } = require('pg');
    const client = new Client(config);
    
    await client.connect();
    console.log('数据库连接成功');
    
    const result = await client.query('SELECT version()');
    console.log('Database version:', result.rows[0].version);
    
    await client.end();
  } catch (error) {
    console.error('数据库连接失败:', error.message);
    console.error('Error details:', error);
  }
}

diagnoseDatabaseConnection();

环境同步工具

bash
#!/bin/bash
# sync-env.sh - 环境同步脚本

SOURCE_ENV=$1
TARGET_ENV=$2

if [ -z "$SOURCE_ENV" ] || [ -z "$TARGET_ENV" ]; then
  echo "用法: $0 <source-env> <target-env>"
  echo "示例: $0 staging production"
  exit 1
fi

echo "正在从 $SOURCE_ENV 同步环境到 $TARGET_ENV..."

# 备份目标环境
cp ".env.$TARGET_ENV" ".env.$TARGET_ENV.backup.$(date +%Y%m%d_%H%M%S)"

# 复制源环境到目标环境
cp ".env.$SOURCE_ENV" ".env.$TARGET_ENV"

# 更新环境特定的变量
sed -i "s/NODE_ENV=$SOURCE_ENV/NODE_ENV=$TARGET_ENV/g" ".env.$TARGET_ENV"

echo "环境同步完成!"
echo "请检查 .env.$TARGET_ENV 并更新环境特定的值。"

最佳实践总结

环境变量管理

  1. 使用 .env 文件:为每个环境创建单独的 .env 文件
  2. 验证配置:在应用启动时验证必需的环境变量
  3. 敏感信息保护:使用密钥管理服务存储敏感信息
  4. 文档化:维护环境变量的文档和示例

配置管理

  1. 分层配置:使用默认配置和环境特定覆盖
  2. 配置验证:实施配置模式验证
  3. 版本控制:将配置文件纳入版本控制(除敏感信息外)
  4. 配置即代码:将基础设施配置作为代码管理

环境隔离

  1. 物理隔离:使用不同的服务器或容器
  2. 网络隔离:实施网络分段和访问控制
  3. 数据隔离:为每个环境使用独立的数据库
  4. 资源隔离:设置适当的资源限制和配额

安全实践

  1. 最小权限原则:只授予必要的访问权限
  2. 定期轮换:定期更新密钥和凭证
  3. 审计日志:记录所有环境访问和更改
  4. 加密传输:使用 HTTPS/TLS 加密所有通信

相关文章

您的终极 AI 驱动 IDE 学习指南