環境管理
概要
Trae IDEでは、開発、ステージング、本番環境を効率的に管理できます。このガイドでは、環境設定、構成管理、デプロイメント戦略について説明します。
環境の種類
開発環境
yaml
development:
name: "開発環境"
purpose: "機能開発とテスト"
characteristics:
- ホットリロード有効
- デバッグモード有効
- 詳細ログ出力
- 開発用データベースステージング環境
yaml
staging:
name: "ステージング環境"
purpose: "本番前テスト"
characteristics:
- 本番環境と同等の構成
- 本番データのサブセット
- パフォーマンステスト
- 統合テスト本番環境
yaml
production:
name: "本番環境"
purpose: "実際のサービス提供"
characteristics:
- 高可用性構成
- 監視とアラート
- バックアップとリカバリ
- セキュリティ強化環境設定
環境変数管理
javascript
// .env.development
const developmentConfig = {
NODE_ENV: 'development',
API_URL: 'http://localhost:3000',
DATABASE_URL: 'postgresql://localhost:5432/myapp_dev',
LOG_LEVEL: 'debug',
REDIS_URL: 'redis://localhost:6379',
JWT_SECRET: 'dev-secret-key'
};
// .env.staging
const stagingConfig = {
NODE_ENV: 'staging',
API_URL: 'https://staging-api.example.com',
DATABASE_URL: 'postgresql://staging-db:5432/myapp_staging',
LOG_LEVEL: 'info',
REDIS_URL: 'redis://staging-redis:6379',
JWT_SECRET: process.env.JWT_SECRET_STAGING
};
// .env.production
const productionConfig = {
NODE_ENV: 'production',
API_URL: 'https://api.example.com',
DATABASE_URL: process.env.DATABASE_URL,
LOG_LEVEL: 'warn',
REDIS_URL: process.env.REDIS_URL,
JWT_SECRET: process.env.JWT_SECRET
};設定ファイル管理
typescript
// config/environment.ts
interface EnvironmentConfig {
database: DatabaseConfig;
redis: RedisConfig;
auth: AuthConfig;
logging: LoggingConfig;
}
class ConfigManager {
private config: EnvironmentConfig;
constructor() {
this.loadConfig();
}
private loadConfig(): void {
const env = process.env.NODE_ENV || 'development';
switch (env) {
case 'development':
this.config = this.loadDevelopmentConfig();
break;
case 'staging':
this.config = this.loadStagingConfig();
break;
case 'production':
this.config = this.loadProductionConfig();
break;
default:
throw new Error(`未知の環境: ${env}`);
}
}
getConfig(): EnvironmentConfig {
return this.config;
}
}Docker環境管理
開発用Docker構成
dockerfile
# Dockerfile.development
FROM node:18-alpine
WORKDIR /app
# 開発用依存関係をインストール
COPY package*.json ./
RUN npm install
# ソースコードをコピー
COPY . .
# 開発サーバーを起動
CMD ["npm", "run", "dev"]本番用Docker構成
dockerfile
# Dockerfile.production
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM node:18-alpine AS runtime
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
EXPOSE 3000
CMD ["npm", "start"]Docker Compose設定
yaml
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile.${NODE_ENV:-development}
ports:
- "${PORT:-3000}:3000"
environment:
- NODE_ENV=${NODE_ENV:-development}
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
depends_on:
- database
- redis
volumes:
- .:/app
- /app/node_modules
database:
image: postgres:15-alpine
environment:
- POSTGRES_DB=${DB_NAME:-myapp}
- POSTGRES_USER=${DB_USER:-postgres}
- POSTGRES_PASSWORD=${DB_PASSWORD:-password}
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:Kubernetes環境管理
名前空間管理
yaml
# namespaces.yaml
apiVersion: v1
kind: Namespace
metadata:
name: myapp-development
labels:
environment: development
---
apiVersion: v1
kind: Namespace
metadata:
name: myapp-staging
labels:
environment: staging
---
apiVersion: v1
kind: Namespace
metadata:
name: myapp-production
labels:
environment: productionConfigMapとSecret管理
yaml
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: myapp-production
data:
NODE_ENV: "production"
LOG_LEVEL: "warn"
API_URL: "https://api.example.com"
---
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: myapp-production
type: Opaque
data:
DATABASE_URL: <base64-encoded-value>
JWT_SECRET: <base64-encoded-value>
REDIS_URL: <base64-encoded-value>デプロイメント設定
yaml
# deployment.yaml
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: app
image: myapp:latest
ports:
- containerPort: 3000
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secrets
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5CI/CD環境管理
GitHub Actions設定
yaml
# .github/workflows/deploy.yml
name: Deploy to Environments
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm ci
- run: npm test
deploy-staging:
needs: test
if: github.ref == 'refs/heads/develop'
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v3
- name: Deploy to Staging
run: |
echo "ステージング環境にデプロイ中..."
# デプロイスクリプトを実行
deploy-production:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v3
- name: Deploy to Production
run: |
echo "本番環境にデプロイ中..."
# デプロイスクリプトを実行環境固有のビルド設定
javascript
// webpack.config.js
const path = require('path');
module.exports = (env, argv) => {
const isProduction = argv.mode === 'production';
const isDevelopment = argv.mode === 'development';
return {
mode: argv.mode,
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: isProduction ? '[name].[contenthash].js' : '[name].js'
},
devtool: isDevelopment ? 'eval-source-map' : 'source-map',
optimization: {
minimize: isProduction,
splitChunks: isProduction ? {
chunks: 'all'
} : false
},
plugins: [
// 環境固有のプラグイン設定
]
};
};環境監視
ヘルスチェック実装
typescript
// health-check.ts
interface HealthStatus {
status: 'healthy' | 'unhealthy';
timestamp: string;
services: ServiceHealth[];
}
interface ServiceHealth {
name: string;
status: 'up' | 'down';
responseTime?: number;
error?: string;
}
class HealthChecker {
async checkHealth(): Promise<HealthStatus> {
const services = await Promise.all([
this.checkDatabase(),
this.checkRedis(),
this.checkExternalAPI()
]);
const allHealthy = services.every(service => service.status === 'up');
return {
status: allHealthy ? 'healthy' : 'unhealthy',
timestamp: new Date().toISOString(),
services
};
}
private async checkDatabase(): Promise<ServiceHealth> {
try {
const start = Date.now();
// データベース接続テスト
await database.query('SELECT 1');
const responseTime = Date.now() - start;
return {
name: 'database',
status: 'up',
responseTime
};
} catch (error) {
return {
name: 'database',
status: 'down',
error: error.message
};
}
}
}メトリクス収集
javascript
// metrics.js
const prometheus = require('prom-client');
// カスタムメトリクス定義
const httpRequestDuration = new prometheus.Histogram({
name: 'http_request_duration_seconds',
help: 'HTTPリクエストの処理時間',
labelNames: ['method', 'route', 'status_code', 'environment']
});
const activeConnections = new prometheus.Gauge({
name: 'active_connections',
help: 'アクティブな接続数',
labelNames: ['environment']
});
// メトリクス収集ミドルウェア
const metricsMiddleware = (req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = (Date.now() - start) / 1000;
httpRequestDuration
.labels(req.method, req.route?.path || req.path, res.statusCode, process.env.NODE_ENV)
.observe(duration);
});
next();
};
module.exports = {
httpRequestDuration,
activeConnections,
metricsMiddleware
};環境間データ同期
データベース移行管理
javascript
// migrations/manager.js
class MigrationManager {
constructor(environment) {
this.environment = environment;
this.config = this.getEnvironmentConfig(environment);
}
async runMigrations() {
const pendingMigrations = await this.getPendingMigrations();
for (const migration of pendingMigrations) {
console.log(`実行中: ${migration.name}`);
await this.executeMigration(migration);
await this.recordMigration(migration);
}
}
async seedData() {
if (this.environment === 'development') {
await this.loadDevelopmentSeeds();
} else if (this.environment === 'staging') {
await this.loadStagingSeeds();
}
// 本番環境では手動でのデータ投入のみ
}
}設定同期スクリプト
bash
#!/bin/bash
# sync-config.sh
ENVIRONMENT=$1
SOURCE_ENV=$2
if [ -z "$ENVIRONMENT" ] || [ -z "$SOURCE_ENV" ]; then
echo "使用方法: $0 <target-environment> <source-environment>"
exit 1
fi
echo "設定を同期中: $SOURCE_ENV -> $ENVIRONMENT"
# Kubernetes ConfigMapを同期
kubectl get configmap app-config -n myapp-$SOURCE_ENV -o yaml | \
sed "s/namespace: myapp-$SOURCE_ENV/namespace: myapp-$ENVIRONMENT/" | \
kubectl apply -f -
# 環境固有の設定を更新
kubectl patch configmap app-config -n myapp-$ENVIRONMENT --patch "
data:
NODE_ENV: '$ENVIRONMENT'
"
echo "設定同期完了"トラブルシューティング
よくある問題
Q: 環境変数が正しく読み込まれない A: .envファイルの場所と命名規則を確認してください。
Q: Docker環境でホットリロードが動作しない A: ボリュームマウントの設定を確認し、node_modulesの除外設定を確認してください。
Q: Kubernetes環境でConfigMapが更新されない A: Podの再起動が必要な場合があります。kubectl rollout restart deploymentを実行してください。
デバッグツール
javascript
// debug-tools.js
const debugTools = {
// 環境情報を表示
showEnvironmentInfo() {
console.log('=== 環境情報 ===');
console.log('NODE_ENV:', process.env.NODE_ENV);
console.log('Platform:', process.platform);
console.log('Node Version:', process.version);
console.log('Memory Usage:', process.memoryUsage());
},
// 設定値を検証
validateConfig(config) {
const required = ['DATABASE_URL', 'JWT_SECRET', 'API_URL'];
const missing = required.filter(key => !config[key]);
if (missing.length > 0) {
console.error('不足している設定:', missing);
return false;
}
return true;
},
// 接続テスト
async testConnections() {
const results = {};
try {
// データベース接続テスト
results.database = await this.testDatabase();
results.redis = await this.testRedis();
results.externalAPI = await this.testExternalAPI();
} catch (error) {
console.error('接続テストエラー:', error);
}
return results;
}
};
module.exports = debugTools;ベストプラクティス
環境管理のガイドライン
- 設定の外部化: 環境固有の設定はコードから分離
- シークレット管理: 機密情報は専用のシークレット管理ツールを使用
- 環境の一貫性: 本番環境に近い構成でテスト
- 自動化: デプロイメントとテストの自動化
- 監視: 各環境の健全性を継続的に監視
セキュリティ考慮事項
yaml
security_guidelines:
secrets:
- 環境変数でシークレットを管理
- バージョン管理にシークレットを含めない
- 定期的なシークレットローテーション
access_control:
- 環境ごとのアクセス制御
- 最小権限の原則
- 監査ログの記録
network:
- 環境間のネットワーク分離
- VPNまたはプライベートネットワーク使用
- ファイアウォール設定