🚀 Production 배포 가이드

📋 목차


시스템 요구사항

하드웨어 요구사항

최소 사양

  • CPU: 2 코어
  • RAM: 4GB
  • 디스크: 20GB SSD
  • 네트워크: 100 Mbps

권장 사양 (Production)

  • CPU: 4+ 코어
  • RAM: 16GB+
  • 디스크: 100GB+ SSD (NVMe 권장)
  • 네트워크: 1 Gbps+

대규모 배포

  • CPU: 8+ 코어
  • RAM: 32GB+
  • 디스크: 500GB+ NVMe SSD
  • 네트워크: 10 Gbps+

소프트웨어 요구사항

  • 운영체제:
    • Ubuntu 20.04+ LTS
    • Debian 11+
    • RHEL 8+
    • Amazon Linux 2
  • Node.js: 18.x LTS 이상
  • npm: 9.x 이상
  • Claude Code: 최신 버전
  • Git: 2.x 이상

네트워크 요구사항

  • 인바운드 포트:
    • 443 (HTTPS) - API 액세스
    • 22 (SSH) - 관리 액세스
  • 아웃바운드 포트:
    • 443 (HTTPS) - Anthropic API, npm registry
    • 80 (HTTP) - Package 업데이트
  • 방화벽: Docker/Kubernetes network policy 필요

환경 변수

필수 변수

# .env.production
NODE_ENV=production
PORT=3000

# Anthropic API
ANTHROPIC_API_KEY=sk-ant-xxxxx
CLAUDE_MODEL=claude-3-5-sonnet-20241022

# Database
DATABASE_URL=postgresql://user:pass@localhost:5432/claude_flow
REDIS_URL=redis://localhost:6379

# Security
JWT_SECRET=your-secure-random-string
ENCRYPTION_KEY=your-32-byte-encryption-key
SESSION_SECRET=your-session-secret

# Logging
LOG_LEVEL=info
LOG_FORMAT=json
SENTRY_DSN=https://xxxxx@sentry.io/xxxxx

선택적 변수

# Feature Flags
ENABLE_TELEMETRY=true
ENABLE_NEURAL_TRAINING=true
ENABLE_DISTRIBUTED_MEMORY=true

# Performance
MAX_WORKERS=4
WORKER_TIMEOUT=300000
MEMORY_LIMIT=2048

# Integrations
GITHUB_TOKEN=ghp_xxxxx
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/xxxxx

# Monitoring
PROMETHEUS_PORT=9090
GRAFANA_PORT=3001
METRICS_INTERVAL=60000

# Storage
S3_BUCKET=claude-flow-storage
S3_REGION=us-east-1
AWS_ACCESS_KEY_ID=xxxxx
AWS_SECRET_ACCESS_KEY=xxxxx

환경별 구성

# Development
NODE_ENV=development
LOG_LEVEL=debug
ENABLE_HOT_RELOAD=true

# Staging
NODE_ENV=staging
LOG_LEVEL=info
RATE_LIMIT=100

# Production
NODE_ENV=production
LOG_LEVEL=warn
RATE_LIMIT=1000
ENABLE_CACHING=true

Docker 배포

Dockerfile

# Multi-stage build for optimization
FROM node:18-alpine AS builder

WORKDIR /app

# Install dependencies
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force

# Copy source code
COPY . .

# Build application
RUN npm run build

# Production image
FROM node:18-alpine

WORKDIR /app

# Install Claude Code
RUN npm install -g @anthropic-ai/claude-code

# Copy from builder
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./

# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001 && \
    chown -R nodejs:nodejs /app

USER nodejs

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s \
  CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"

EXPOSE 3000

CMD ["node", "dist/index.js"]

docker-compose.yml

version: '3.8'

services:
  claude-flow:
    build: .
    container_name: claude-flow
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://postgres:password@db:5432/claude_flow
      - REDIS_URL=redis://redis:6379
    env_file:
      - .env.production
    depends_on:
      - db
      - redis
    volumes:
      - ./logs:/app/logs
      - ./data:/app/data
    networks:
      - claude-flow-network
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

  db:
    image: postgres:15-alpine
    container_name: claude-flow-db
    restart: unless-stopped
    environment:
      - POSTGRES_DB=claude_flow
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
    volumes:
      - postgres-data:/var/lib/postgresql/data
    networks:
      - claude-flow-network
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    container_name: claude-flow-redis
    restart: unless-stopped
    command: redis-server --appendonly yes
    volumes:
      - redis-data:/data
    networks:
      - claude-flow-network
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  prometheus:
    image: prom/prometheus:latest
    container_name: claude-flow-prometheus
    restart: unless-stopped
    ports:
      - "9090:9090"
    volumes:
      - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus-data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
    networks:
      - claude-flow-network

  grafana:
    image: grafana/grafana:latest
    container_name: claude-flow-grafana
    restart: unless-stopped
    ports:
      - "3001:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
      - GF_INSTALL_PLUGINS=redis-datasource
    volumes:
      - grafana-data:/var/lib/grafana
      - ./monitoring/grafana/dashboards:/etc/grafana/provisioning/dashboards
    depends_on:
      - prometheus
    networks:
      - claude-flow-network

networks:
  claude-flow-network:
    driver: bridge

volumes:
  postgres-data:
  redis-data:
  prometheus-data:
  grafana-data:

배포 명령어

# Build and start services
docker-compose up -d

# View logs
docker-compose logs -f claude-flow

# Scale service
docker-compose up -d --scale claude-flow=3

# Stop services
docker-compose down

# Clean up volumes
docker-compose down -v

Kubernetes 배포

Namespace 생성

# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: claude-flow
  labels:
    name: claude-flow
    environment: production

ConfigMap

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: claude-flow-config
  namespace: claude-flow
data:
  NODE_ENV: "production"
  LOG_LEVEL: "info"
  LOG_FORMAT: "json"
  ENABLE_TELEMETRY: "true"
  MAX_WORKERS: "4"
  PROMETHEUS_PORT: "9090"

Secret

# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: claude-flow-secrets
  namespace: claude-flow
type: Opaque
stringData:
  ANTHROPIC_API_KEY: "sk-ant-xxxxx"
  DATABASE_URL: "postgresql://user:pass@db:5432/claude_flow"
  REDIS_URL: "redis://redis:6379"
  JWT_SECRET: "your-secure-random-string"
  ENCRYPTION_KEY: "your-32-byte-encryption-key"

Deployment

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: claude-flow
  namespace: claude-flow
  labels:
    app: claude-flow
    version: v2.7.0
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: claude-flow
  template:
    metadata:
      labels:
        app: claude-flow
        version: v2.7.0
    spec:
      containers:
      - name: claude-flow
        image: your-registry/claude-flow:2.7.0
        imagePullPolicy: Always
        ports:
        - containerPort: 3000
          name: http
        - containerPort: 9090
          name: metrics
        env:
        - name: PORT
          value: "3000"
        envFrom:
        - configMapRef:
            name: claude-flow-config
        - secretRef:
            name: claude-flow-secrets
        resources:
          requests:
            memory: "2Gi"
            cpu: "1000m"
          limits:
            memory: "4Gi"
            cpu: "2000m"
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3
        readinessProbe:
          httpGet:
            path: /ready
            port: 3000
          initialDelaySeconds: 10
          periodSeconds: 5
          timeoutSeconds: 3
          failureThreshold: 3
        volumeMounts:
        - name: data
          mountPath: /app/data
        - name: logs
          mountPath: /app/logs
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: claude-flow-data
      - name: logs
        emptyDir: {}
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - claude-flow
              topologyKey: kubernetes.io/hostname

Service

# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: claude-flow
  namespace: claude-flow
  labels:
    app: claude-flow
spec:
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 3000
    protocol: TCP
    name: http
  - port: 9090
    targetPort: 9090
    protocol: TCP
    name: metrics
  selector:
    app: claude-flow

Ingress

# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: claude-flow
  namespace: claude-flow
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/rate-limit: "100"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  tls:
  - hosts:
    - api.claude-flow.com
    secretName: claude-flow-tls
  rules:
  - host: api.claude-flow.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: claude-flow
            port:
              number: 80

HorizontalPodAutoscaler

# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: claude-flow
  namespace: claude-flow
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: claude-flow
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 50
        periodSeconds: 60
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
      - type: Percent
        value: 100
        periodSeconds: 30
      - type: Pods
        value: 2
        periodSeconds: 60

PersistentVolumeClaim

# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: claude-flow-data
  namespace: claude-flow
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: aws-efs
  resources:
    requests:
      storage: 100Gi

배포 스크립트

#!/bin/bash
# deploy.sh

set -e

NAMESPACE="claude-flow"
VERSION="2.7.0"

echo "🚀 Deploying Claude-Flow v${VERSION} to Kubernetes..."

# Create namespace
kubectl apply -f k8s/namespace.yaml

# Apply configurations
kubectl apply -f k8s/configmap.yaml
kubectl apply -f k8s/secret.yaml
kubectl apply -f k8s/pvc.yaml

# Deploy application
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/ingress.yaml
kubectl apply -f k8s/hpa.yaml

# Wait for rollout
echo "⏳ Waiting for deployment to complete..."
kubectl rollout status deployment/claude-flow -n ${NAMESPACE} --timeout=5m

# Verify deployment
echo "✅ Verifying deployment..."
kubectl get pods -n ${NAMESPACE} -l app=claude-flow

echo "🎉 Deployment complete!"
echo "📊 Access Grafana: http://grafana.claude-flow.com"
echo "📈 Access Prometheus: http://prometheus.claude-flow.com"

CI/CD Pipeline

GitHub Actions

# .github/workflows/deploy-production.yml
name: 🚀 Deploy to Production

on:
  push:
    branches:
      - main
    tags:
      - 'v*'
  workflow_dispatch:

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: $
  KUBE_CONFIG: $

jobs:
  build:
    name: 🏗️ Build Docker Image
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - name: 📥 Checkout code
        uses: actions/checkout@v4

      - name: 🔐 Log in to Container Registry
        uses: docker/login-action@v3
        with:
          registry: $
          username: $
          password: $

      - name: 🏷️ Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: $/$
          tags: |
            type=ref,event=branch
            type=ref,event=pr
            type=semver,pattern=
            type=semver,pattern=.
            type=sha

      - name: 🏗️ Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: $
          labels: $
          cache-from: type=gha
          cache-to: type=gha,mode=max

  test:
    name: 🧪 Run Tests
    runs-on: ubuntu-latest
    needs: build

    steps:
      - name: 📥 Checkout code
        uses: actions/checkout@v4

      - name: 🟢 Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'

      - name: 📦 Install dependencies
        run: npm ci

      - name: 🧪 Run unit tests
        run: npm run test:unit

      - name: 🔗 Run integration tests
        run: npm run test:integration
        env:
          DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test
          REDIS_URL: redis://localhost:6379

      - name: 📊 Upload coverage
        uses: codecov/codecov-action@v3
        with:
          files: ./coverage/lcov.info
          flags: unittests
          name: codecov-umbrella

  security:
    name: 🔒 Security Scan
    runs-on: ubuntu-latest
    needs: build

    steps:
      - name: 📥 Checkout code
        uses: actions/checkout@v4

      - name: 🔍 Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: $/$:$
          format: 'sarif'
          output: 'trivy-results.sarif'

      - name: 📤 Upload Trivy results to GitHub Security
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: 'trivy-results.sarif'

  deploy:
    name: 🚀 Deploy to Kubernetes
    runs-on: ubuntu-latest
    needs: [build, test, security]
    environment:
      name: production
      url: https://api.claude-flow.com

    steps:
      - name: 📥 Checkout code
        uses: actions/checkout@v4

      - name: ⚙️ Configure kubectl
        run: |
          echo "$" | base64 -d > kubeconfig
          export KUBECONFIG=kubeconfig

      - name: 🔄 Update deployment
        run: |
          kubectl set image deployment/claude-flow \
            claude-flow=$/$:$ \
            -n claude-flow

      - name: ⏳ Wait for rollout
        run: |
          kubectl rollout status deployment/claude-flow \
            -n claude-flow \
            --timeout=5m

      - name: ✅ Verify deployment
        run: |
          kubectl get pods -n claude-flow -l app=claude-flow

  notify:
    name: 📢 Notify Team
    runs-on: ubuntu-latest
    needs: [deploy]
    if: always()

    steps:
      - name: 📬 Send Slack notification
        uses: 8398a7/action-slack@v3
        with:
          status: $
          text: |
            🚀 Deployment to Production: $
            📦 Version: $
            👤 Author: $
            🔗 Workflow: $/$/actions/runs/$
          webhook_url: $
        if: always()

모니터링 및 로깅

Prometheus 구성

# monitoring/prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s
  external_labels:
    cluster: 'claude-flow-prod'
    environment: 'production'

alerting:
  alertmanagers:
    - static_configs:
        - targets:
            - alertmanager:9093

scrape_configs:
  - job_name: 'claude-flow'
    kubernetes_sd_configs:
      - role: pod
        namespaces:
          names:
            - claude-flow
    relabel_configs:
      - source_labels: [__meta_kubernetes_pod_label_app]
        action: keep
        regex: claude-flow
      - source_labels: [__meta_kubernetes_pod_name]
        action: replace
        target_label: pod
      - source_labels: [__meta_kubernetes_namespace]
        action: replace
        target_label: namespace

  - job_name: 'node-exporter'
    static_configs:
      - targets: ['node-exporter:9100']

  - job_name: 'postgres'
    static_configs:
      - targets: ['postgres-exporter:9187']

  - job_name: 'redis'
    static_configs:
      - targets: ['redis-exporter:9121']

Grafana Dashboard

{
  "dashboard": {
    "title": "Claude-Flow Production Metrics",
    "panels": [
      {
        "title": "Request Rate",
        "targets": [
          {
            "expr": "rate(http_requests_total[5m])"
          }
        ]
      },
      {
        "title": "Response Time (p95)",
        "targets": [
          {
            "expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))"
          }
        ]
      },
      {
        "title": "Error Rate",
        "targets": [
          {
            "expr": "rate(http_requests_total{status=~\"5..\"}[5m])"
          }
        ]
      },
      {
        "title": "Active Swarms",
        "targets": [
          {
            "expr": "claude_flow_active_swarms"
          }
        ]
      }
    ]
  }
}

Logging 구성

// config/logger.js
import winston from 'winston';
import { ElasticsearchTransport } from 'winston-elasticsearch';

const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  defaultMeta: {
    service: 'claude-flow',
    environment: process.env.NODE_ENV,
    version: process.env.APP_VERSION
  },
  transports: [
    // Console output
    new winston.transports.Console({
      format: winston.format.combine(
        winston.format.colorize(),
        winston.format.simple()
      )
    }),

    // File output
    new winston.transports.File({
      filename: 'logs/error.log',
      level: 'error',
      maxsize: 10485760, // 10MB
      maxFiles: 5
    }),
    new winston.transports.File({
      filename: 'logs/combined.log',
      maxsize: 10485760,
      maxFiles: 10
    }),

    // Elasticsearch (production)
    ...(process.env.NODE_ENV === 'production' ? [
      new ElasticsearchTransport({
        level: 'info',
        clientOpts: {
          node: process.env.ELASTICSEARCH_URL,
          auth: {
            username: process.env.ELASTICSEARCH_USER,
            password: process.env.ELASTICSEARCH_PASSWORD
          }
        },
        index: 'claude-flow-logs'
      })
    ] : [])
  ]
});

export default logger;

보안 구성

Secrets 관리

# Using Kubernetes Secrets
kubectl create secret generic claude-flow-secrets \
  --from-literal=anthropic-api-key=$ANTHROPIC_API_KEY \
  --from-literal=database-url=$DATABASE_URL \
  --from-literal=jwt-secret=$JWT_SECRET \
  -n claude-flow

# Using AWS Secrets Manager
aws secretsmanager create-secret \
  --name claude-flow/production/api-keys \
  --secret-string '{
    "anthropic_api_key": "sk-ant-xxxxx",
    "jwt_secret": "xxxxx"
  }'

# Using HashiCorp Vault
vault kv put secret/claude-flow/production \
  anthropic_api_key=sk-ant-xxxxx \
  database_url=postgresql://... \
  jwt_secret=xxxxx

Network Policy

# network-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: claude-flow-network-policy
  namespace: claude-flow
spec:
  podSelector:
    matchLabels:
      app: claude-flow
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              name: ingress-nginx
      ports:
        - protocol: TCP
          port: 3000
    - from:
        - namespaceSelector:
            matchLabels:
              name: monitoring
      ports:
        - protocol: TCP
          port: 9090
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              name: claude-flow
      ports:
        - protocol: TCP
          port: 5432
        - protocol: TCP
          port: 6379
    - to:
        - podSelector: {}
      ports:
        - protocol: TCP
          port: 53
        - protocol: UDP
          port: 53

WAF 구성

# waf-rules.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: claude-flow
  annotations:
    nginx.ingress.kubernetes.io/enable-modsecurity: "true"
    nginx.ingress.kubernetes.io/enable-owasp-core-rules: "true"
    nginx.ingress.kubernetes.io/modsecurity-snippet: |
      SecRuleEngine On
      SecRequestBodyLimit 13107200
      SecRule ARGS "@contains <script>" "id:1,deny,status:403,msg:'XSS Attack'"
      SecRule ARGS "@contains .." "id:2,deny,status:403,msg:'Path Traversal'"

백업 및 복구

Database 백업

#!/bin/bash
# scripts/backup-database.sh

set -e

BACKUP_DIR="/backups/postgresql"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="claude_flow_${TIMESTAMP}.sql.gz"

# Create backup
pg_dump -h localhost -U postgres claude_flow | gzip > "${BACKUP_DIR}/${BACKUP_FILE}"

# Upload to S3
aws s3 cp "${BACKUP_DIR}/${BACKUP_FILE}" \
  s3://claude-flow-backups/postgresql/${BACKUP_FILE}

# Clean up old backups (keep last 30 days)
find "${BACKUP_DIR}" -name "*.sql.gz" -mtime +30 -delete

echo "✅ Backup completed: ${BACKUP_FILE}"

Automated 백업 (Kubernetes CronJob)

# cronjob-backup.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: claude-flow-backup
  namespace: claude-flow
spec:
  schedule: "0 2 * * *"  # Daily at 2 AM
  successfulJobsHistoryLimit: 7
  failedJobsHistoryLimit: 3
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: postgres:15-alpine
            env:
            - name: PGPASSWORD
              valueFrom:
                secretKeyRef:
                  name: claude-flow-secrets
                  key: database-password
            command:
            - /bin/sh
            - -c
            - |
              TIMESTAMP=$(date +%Y%m%d_%H%M%S)
              BACKUP_FILE="claude_flow_${TIMESTAMP}.sql.gz"

              pg_dump -h postgres -U postgres claude_flow | gzip > /tmp/${BACKUP_FILE}

              aws s3 cp /tmp/${BACKUP_FILE} s3://claude-flow-backups/postgresql/${BACKUP_FILE}

              echo "Backup completed: ${BACKUP_FILE}"
            volumeMounts:
            - name: backup-storage
              mountPath: /tmp
          volumes:
          - name: backup-storage
            emptyDir: {}
          restartPolicy: OnFailure

복구 절차

#!/bin/bash
# scripts/restore-database.sh

set -e

BACKUP_FILE=$1

if [ -z "$BACKUP_FILE" ]; then
  echo "Usage: $0 <backup-file>"
  exit 1
fi

# Download from S3
aws s3 cp "s3://claude-flow-backups/postgresql/${BACKUP_FILE}" /tmp/

# Restore database
gunzip < "/tmp/${BACKUP_FILE}" | psql -h localhost -U postgres claude_flow

echo "✅ Database restored from: ${BACKUP_FILE}"

확장성

Horizontal Scaling

# Scale deployment
kubectl scale deployment claude-flow --replicas=10 -n claude-flow

# Autoscaling based on metrics
kubectl autoscale deployment claude-flow \
  --cpu-percent=70 \
  --min=3 \
  --max=10 \
  -n claude-flow

Database Connection Pooling

// config/database.js
import { Pool } from 'pg';

const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  max: 20,                    // Maximum pool size
  idleTimeoutMillis: 30000,   // Close idle connections after 30s
  connectionTimeoutMillis: 2000,
  ssl: {
    rejectUnauthorized: false
  }
});

// Health check
pool.on('error', (err) => {
  console.error('Unexpected database error', err);
  process.exit(-1);
});

export default pool;

Redis Caching

// config/redis.js
import Redis from 'ioredis';

const redis = new Redis({
  host: process.env.REDIS_HOST,
  port: process.env.REDIS_PORT,
  password: process.env.REDIS_PASSWORD,
  retryStrategy(times) {
    const delay = Math.min(times * 50, 2000);
    return delay;
  },
  maxRetriesPerRequest: 3,
  enableReadyCheck: true,
  autoResendUnfulfilledCommands: true
});

// Cache helper
export async function cacheGet(key) {
  const cached = await redis.get(key);
  return cached ? JSON.parse(cached) : null;
}

export async function cacheSet(key, value, ttl = 3600) {
  await redis.setex(key, ttl, JSON.stringify(value));
}

export default redis;

문제 해결

일반적인 문제

1. Pod가 시작되지 않음

# Check pod status
kubectl get pods -n claude-flow

# View pod logs
kubectl logs <pod-name> -n claude-flow

# Describe pod for events
kubectl describe pod <pod-name> -n claude-flow

# Check resource limits
kubectl top pods -n claude-flow

2. Database 연결 실패

# Test database connectivity
kubectl run -it --rm debug \
  --image=postgres:15-alpine \
  --restart=Never \
  -- psql -h postgres -U postgres -d claude_flow

# Check database logs
kubectl logs -n claude-flow -l app=postgres

# Verify secrets
kubectl get secret claude-flow-secrets -n claude-flow -o yaml

3. 높은 메모리 사용량

# Check memory usage
kubectl top pods -n claude-flow

# Analyze heap dump
node --expose-gc --inspect app.js

# Increase memory limit
kubectl set resources deployment claude-flow \
  --limits=memory=8Gi \
  -n claude-flow

4. Ingress 문제

# Check ingress status
kubectl get ingress -n claude-flow

# View nginx logs
kubectl logs -n ingress-nginx -l app=ingress-nginx

# Test endpoint
curl -v https://api.claude-flow.com/health

Performance 최적화

// Enable clustering
import cluster from 'cluster';
import os from 'os';

if (cluster.isPrimary) {
  const numCPUs = os.cpus().length;

  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker) => {
    console.log(`Worker ${worker.process.pid} died`);
    cluster.fork();
  });
} else {
  // Start application
  import('./app.js');
}

모니터링 Alert

# alerting-rules.yaml
groups:
  - name: claude-flow-alerts
    interval: 30s
    rules:
      - alert: HighErrorRate
        expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.05
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "High error rate detected"
          description: "Error rate is  requests/sec"

      - alert: HighMemoryUsage
        expr: container_memory_usage_bytes{pod=~"claude-flow-.*"} > 3.5e9
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "High memory usage"
          description: "Memory usage is B"

      - alert: PodCrashLooping
        expr: rate(kube_pod_container_status_restarts_total[15m]) > 0
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Pod is crash looping"
          description: "Pod  is restarting"

Cloud 플랫폼별 배포

AWS EKS

# Create EKS cluster
eksctl create cluster \
  --name claude-flow-prod \
  --version 1.28 \
  --region us-east-1 \
  --nodegroup-name standard-workers \
  --node-type t3.large \
  --nodes 3 \
  --nodes-min 3 \
  --nodes-max 10 \
  --managed

# Configure kubectl
aws eks update-kubeconfig --name claude-flow-prod --region us-east-1

# Deploy application
kubectl apply -k k8s/overlays/production

GCP GKE

# Create GKE cluster
gcloud container clusters create claude-flow-prod \
  --zone us-central1-a \
  --num-nodes 3 \
  --machine-type n1-standard-4 \
  --enable-autoscaling \
  --min-nodes 3 \
  --max-nodes 10

# Get credentials
gcloud container clusters get-credentials claude-flow-prod \
  --zone us-central1-a

# Deploy application
kubectl apply -k k8s/overlays/production

Azure AKS

# Create AKS cluster
az aks create \
  --resource-group claude-flow-rg \
  --name claude-flow-prod \
  --node-count 3 \
  --node-vm-size Standard_D4s_v3 \
  --enable-cluster-autoscaler \
  --min-count 3 \
  --max-count 10 \
  --generate-ssh-keys

# Get credentials
az aks get-credentials \
  --resource-group claude-flow-rg \
  --name claude-flow-prod

# Deploy application
kubectl apply -k k8s/overlays/production

Heroku

# Create Heroku app
heroku create claude-flow-prod

# Set environment variables
heroku config:set \
  NODE_ENV=production \
  ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
  DATABASE_URL=$DATABASE_URL

# Add buildpacks
heroku buildpacks:add heroku/nodejs

# Deploy
git push heroku main

# Scale dynos
heroku ps:scale web=3:standard-2x

# View logs
heroku logs --tail

추가 리소스


문서 버전: 2.7.0 마지막 업데이트: 2025-01-15