Skip to content

Latest commit

 

History

History
928 lines (786 loc) · 20.2 KB

File metadata and controls

928 lines (786 loc) · 20.2 KB

Deployment Guide

Complete deployment strategies and production considerations for the Qwe Framework.

Table of Contents

Overview

This guide covers deploying Qwe Framework applications from development to production, including containerization, cloud deployment, monitoring, and security best practices.

Deployment Options

  • 🐳 Containerized: Docker/Kubernetes deployment
  • ☁️ Cloud Native: AWS, GCP, Azure platforms
  • 🖥️ Traditional: VPS/Dedicated servers
  • ⚡ Serverless: Edge computing and functions
  • 🔄 CI/CD: Automated deployment pipelines

Environment Setup

Production Environment Variables

# Core Configuration
NODE_ENV=production
PORT=8080
HOST=0.0.0.0

# Security
JWT_SECRET=your-super-secure-jwt-secret-key
SESSION_SECRET=your-session-secret-key

# Database
DATABASE_URL=postgresql://user:password@host:5432/database
DB_POOL_SIZE=20
DB_SSL=true

# Monitoring
LOG_LEVEL=warn
METRICS_ENABLED=true
HEALTH_CHECK_PORT=8081

# Performance
COMPRESSION_ENABLED=true
CACHE_TTL=3600
RATE_LIMIT_WINDOW=900000
RATE_LIMIT_MAX=1000

Configuration Management

// config/production.ts
const productionConfig = {
  server: {
    port: parseInt(process.env.PORT || '8080'),
    host: process.env.HOST || '0.0.0.0',
    cluster: true,
    workers: parseInt(process.env.WORKERS || '0') // 0 = CPU count
  },
  
  database: {
    url: process.env.DATABASE_URL!,
    pool: {
      min: 5,
      max: parseInt(process.env.DB_POOL_SIZE || '20'),
      idle: 30000,
      acquire: 60000
    },
    ssl: process.env.DB_SSL === 'true'
  },
  
  security: {
    jwt: {
      secret: process.env.JWT_SECRET!,
      expiresIn: '15m'
    },
    cors: {
      origin: process.env.ALLOWED_ORIGINS?.split(',') || false,
      credentials: true
    },
    helmet: {
      contentSecurityPolicy: true,
      crossOriginEmbedderPolicy: true
    }
  },
  
  logging: {
    level: process.env.LOG_LEVEL || 'info',
    format: 'json',
    destinations: ['console', 'file']
  }
};

Container Deployment

Dockerfile

# Multi-stage build for optimal image size
FROM node:18-alpine AS builder

WORKDIR /app

# Copy package files
COPY package*.json ./
COPY tsconfig.json ./

# Install dependencies
RUN npm ci --only=production && npm cache clean --force

# Copy source code
COPY src/ ./src/

# Build application
RUN npm run build

# Production stage
FROM node:18-alpine AS production

# Create app user
RUN addgroup -g 1001 -S appgroup && \
    adduser -S appuser -u 1001 -G appgroup

WORKDIR /app

# Copy built application
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appgroup /app/package.json ./

# Security improvements
RUN apk --no-cache add dumb-init && \
    rm -rf /var/cache/apk/*

# Switch to non-root user
USER appuser

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD node dist/healthcheck.js

# Expose port
EXPOSE 8080

# Start application
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/index.js"]

Docker Compose

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

services:
  app:
    build:
      context: .
      target: production
    ports:
      - "8080:8080"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://postgres:password@db:5432/qweapp
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis
    networks:
      - app-network
    restart: unless-stopped
    
  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=qweapp
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - app-network
    restart: unless-stopped
    
  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data
    networks:
      - app-network
    restart: unless-stopped
    
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./ssl:/etc/nginx/ssl
    depends_on:
      - app
    networks:
      - app-network
    restart: unless-stopped

volumes:
  postgres_data:
  redis_data:

networks:
  app-network:
    driver: bridge

Kubernetes Deployment

# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: qwe-app
  labels:
    app: qwe-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: qwe-app
  template:
    metadata:
      labels:
        app: qwe-app
    spec:
      containers:
      - name: qwe-app
        image: your-registry/qwe-app:latest
        ports:
        - containerPort: 8080
        env:
        - name: NODE_ENV
          value: "production"
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: database-url
        - name: JWT_SECRET
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: jwt-secret
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5

---
apiVersion: v1
kind: Service
metadata:
  name: qwe-app-service
spec:
  selector:
    app: qwe-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
  type: LoadBalancer

Cloud Platforms

AWS Deployment

ECS with Fargate

{
  "family": "qwe-app",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "512",
  "memory": "1024",
  "executionRoleArn": "arn:aws:iam::account:role/ecsTaskExecutionRole",
  "taskRoleArn": "arn:aws:iam::account:role/ecsTaskRole",
  "containerDefinitions": [
    {
      "name": "qwe-app",
      "image": "your-account.dkr.ecr.region.amazonaws.com/qwe-app:latest",
      "portMappings": [
        {
          "containerPort": 8080,
          "protocol": "tcp"
        }
      ],
      "essential": true,
      "environment": [
        {
          "name": "NODE_ENV",
          "value": "production"
        }
      ],
      "secrets": [
        {
          "name": "DATABASE_URL",
          "valueFrom": "arn:aws:secretsmanager:region:account:secret:prod/database-url"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/qwe-app",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ]
}

Lambda Deployment

// lambda/handler.ts
import { createLambdaHandler } from 'qwe-framework';
import { app } from '../src/app';

export const handler = createLambdaHandler(app, {
  binary: ['image/*', 'application/pdf'],
  stripBasePath: '/prod'
});

Google Cloud Platform

Cloud Run

# cloudrun.yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: qwe-app
  annotations:
    run.googleapis.com/ingress: all
spec:
  template:
    metadata:
      annotations:
        run.googleapis.com/cpu-throttling: "false"
        run.googleapis.com/memory: "512Mi"
        run.googleapis.com/cpu: "1000m"
    spec:
      containers:
      - image: gcr.io/project-id/qwe-app:latest
        ports:
        - containerPort: 8080
        env:
        - name: NODE_ENV
          value: "production"
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: database-url
        resources:
          limits:
            memory: "512Mi"
            cpu: "1000m"

Azure Deployment

Container Instances

{
  "location": "East US",
  "properties": {
    "containers": [
      {
        "name": "qwe-app",
        "properties": {
          "image": "your-registry.azurecr.io/qwe-app:latest",
          "ports": [
            {
              "port": 8080,
              "protocol": "TCP"
            }
          ],
          "environmentVariables": [
            {
              "name": "NODE_ENV",
              "value": "production"
            }
          ],
          "resources": {
            "requests": {
              "cpu": 1,
              "memoryInGB": 1
            }
          }
        }
      }
    ],
    "osType": "Linux",
    "ipAddress": {
      "type": "Public",
      "ports": [
        {
          "port": 8080,
          "protocol": "TCP"
        }
      ]
    }
  }
}

Load Balancing

Nginx Configuration

# nginx.conf
upstream qwe_app {
    least_conn;
    server app1:8080 max_fails=3 fail_timeout=30s;
    server app2:8080 max_fails=3 fail_timeout=30s;
    server app3:8080 max_fails=3 fail_timeout=30s;
}

server {
    listen 80;
    server_name your-domain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name your-domain.com;
    
    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
    
    # WebSocket support
    location /ws {
        proxy_pass http://qwe_app;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
    
    # API routes
    location /api {
        proxy_pass http://qwe_app;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # Rate limiting
        limit_req zone=api burst=10 nodelay;
    }
    
    # Static files
    location /static {
        alias /var/www/static;
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

# Rate limiting
http {
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
}

Monitoring & Logging

Application Monitoring

// monitoring/setup.ts
import { createMonitoring } from 'qwe-framework';

const monitoring = createMonitoring({
  prometheus: {
    enabled: true,
    endpoint: '/metrics',
    defaultMetrics: true
  },
  
  healthChecks: {
    endpoint: '/health',
    checks: {
      database: async () => {
        const result = await db.query('SELECT 1');
        return { status: 'healthy', response_time: Date.now() };
      },
      
      redis: async () => {
        const start = Date.now();
        await redis.ping();
        return { status: 'healthy', response_time: Date.now() - start };
      },
      
      external_api: async () => {
        try {
          const response = await fetch('https://api.external.com/health');
          return { status: response.ok ? 'healthy' : 'unhealthy' };
        } catch (error) {
          return { status: 'unhealthy', error: error.message };
        }
      }
    }
  },
  
  logging: {
    level: 'info',
    format: 'json',
    includeStack: process.env.NODE_ENV === 'development'
  }
});

app.use(monitoring.middleware());

Structured Logging

// logging/logger.ts
import { createLogger } from 'qwe-framework';

const logger = createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: 'json',
  
  transports: [
    {
      type: 'console',
      colorize: process.env.NODE_ENV === 'development'
    },
    {
      type: 'file',
      filename: 'logs/app.log',
      maxSize: '10MB',
      maxFiles: 5
    },
    {
      type: 'http',
      host: 'logs.example.com',
      port: 443,
      ssl: true
    }
  ],
  
  metadata: {
    service: 'qwe-app',
    version: process.env.APP_VERSION || '1.0.0',
    environment: process.env.NODE_ENV
  }
});

// Usage in middleware
app.use((qwe, next) => {
  const start = Date.now();
  
  qwe.logger = logger.child({
    requestId: qwe.generateId(),
    ip: qwe.ip,
    userAgent: qwe.headers['user-agent']
  });
  
  qwe.logger.info('Request started', {
    method: qwe.method,
    url: qwe.url,
    headers: qwe.headers
  });
  
  const originalJson = qwe.json;
  qwe.json = (data) => {
    qwe.logger.info('Request completed', {
      method: qwe.method,
      url: qwe.url,
      statusCode: qwe.statusCode,
      duration: Date.now() - start
    });
    return originalJson.call(qwe, data);
  };
  
  return next();
});

Security Considerations

Production Security Setup

// security/production.ts
import { createSecurityMiddleware } from 'qwe-framework';

const security = createSecurityMiddleware({
  // Helmet configuration
  helmet: {
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        styleSrc: ["'self'", "'unsafe-inline'"],
        scriptSrc: ["'self'"],
        imgSrc: ["'self'", "data:", "https:"],
        connectSrc: ["'self'", "wss:", "https:"],
        fontSrc: ["'self'"],
        objectSrc: ["'none'"],
        mediaSrc: ["'self'"],
        frameSrc: ["'none'"]
      }
    },
    crossOriginEmbedderPolicy: true,
    crossOriginOpenerPolicy: true,
    crossOriginResourcePolicy: true,
    dnsPrefetchControl: true,
    frameguard: { action: 'deny' },
    hidePoweredBy: true,
    hsts: {
      maxAge: 31536000,
      includeSubDomains: true,
      preload: true
    },
    ieNoOpen: true,
    noSniff: true,
    originAgentCluster: true,
    permittedCrossDomainPolicies: false,
    referrerPolicy: 'no-referrer',
    xssFilter: true
  },
  
  // CORS configuration
  cors: {
    origin: process.env.ALLOWED_ORIGINS?.split(',') || false,
    methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
    allowedHeaders: ['Content-Type', 'Authorization'],
    credentials: true,
    maxAge: 86400
  },
  
  // Rate limiting
  rateLimit: {
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 1000, // limit each IP to 1000 requests per windowMs
    standardHeaders: true,
    legacyHeaders: false,
    skipSuccessfulRequests: false,
    skipFailedRequests: false,
    
    // Custom key generator
    keyGenerator: (qwe) => {
      return qwe.headers['x-forwarded-for'] || qwe.ip;
    },
    
    // Custom handler
    handler: (qwe) => {
      return qwe.tooManyRequests('Too many requests from this IP');
    }
  }
});

app.use(security);

SSL/TLS Configuration

# Generate SSL certificate with Let's Encrypt
certbot certonly --webroot \
  --webroot-path=/var/www/certbot \
  --email admin@your-domain.com \
  --agree-tos \
  --no-eff-email \
  -d your-domain.com \
  -d www.your-domain.com

# Auto-renewal cron job
0 12 * * * /usr/bin/certbot renew --quiet --deploy-hook "systemctl reload nginx"

Performance Optimization

Clustering

// cluster.ts
import cluster from 'cluster';
import os from 'os';
import { createApp } from './app';

if (cluster.isPrimary) {
  const numWorkers = process.env.WORKERS || os.cpus().length;
  
  console.log(`Master ${process.pid} is running`);
  console.log(`Starting ${numWorkers} workers`);
  
  // Fork workers
  for (let i = 0; i < numWorkers; i++) {
    cluster.fork();
  }
  
  // Replace dead workers
  cluster.on('exit', (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died`);
    cluster.fork();
  });
  
  // Graceful shutdown
  process.on('SIGTERM', () => {
    console.log('Shutting down gracefully');
    for (const id in cluster.workers) {
      cluster.workers[id]?.kill();
    }
  });
} else {
  // Worker process
  const app = createApp();
  
  const server = app.listen(process.env.PORT || 8080, () => {
    console.log(`Worker ${process.pid} started`);
  });
  
  // Graceful shutdown for workers
  process.on('SIGTERM', () => {
    console.log(`Worker ${process.pid} shutting down`);
    server.close(() => {
      process.exit(0);
    });
  });
}

Caching Strategy

// caching/setup.ts
import { createCacheManager } from 'qwe-framework';

const cache = createCacheManager({
  // Redis for distributed caching
  redis: {
    host: process.env.REDIS_HOST || 'localhost',
    port: parseInt(process.env.REDIS_PORT || '6379'),
    password: process.env.REDIS_PASSWORD,
    db: 0,
    keyPrefix: 'qwe:',
    ttl: 3600 // 1 hour default
  },
  
  // Memory cache for frequently accessed data
  memory: {
    max: 1000,
    ttl: 300 // 5 minutes
  }
});

// Cache middleware
app.use('/api', cache.middleware({
  ttl: 300,
  vary: ['Authorization'],
  skip: (qwe) => qwe.method !== 'GET',
  key: (qwe) => `${qwe.method}:${qwe.url}:${qwe.user?.userId || 'anonymous'}`
}));

Database Optimization

// database/optimization.ts
const dbConfig = {
  // Connection pooling
  pool: {
    min: 5,
    max: 20,
    idle: 30000,
    acquire: 60000,
    evict: 1000
  },
  
  // Query optimization
  benchmark: true,
  logging: (sql, timing) => {
    if (timing > 1000) { // Log slow queries
      console.warn(`Slow query (${timing}ms): ${sql}`);
    }
  },
  
  // Read replicas
  replication: {
    read: [
      { host: 'read-replica-1.example.com' },
      { host: 'read-replica-2.example.com' }
    ],
    write: { host: 'master.example.com' }
  }
};

CI/CD Pipeline

GitHub Actions

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

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run tests
      run: npm test
    
    - name: Run security audit
      run: npm audit
  
  build:
    needs: test
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    
    - name: Build Docker image
      run: |
        docker build -t ${{ secrets.REGISTRY_URL }}/qwe-app:${{ github.sha }} .
        docker build -t ${{ secrets.REGISTRY_URL }}/qwe-app:latest .
    
    - name: Push to registry
      run: |
        echo ${{ secrets.REGISTRY_PASSWORD }} | docker login ${{ secrets.REGISTRY_URL }} -u ${{ secrets.REGISTRY_USERNAME }} --password-stdin
        docker push ${{ secrets.REGISTRY_URL }}/qwe-app:${{ github.sha }}
        docker push ${{ secrets.REGISTRY_URL }}/qwe-app:latest
  
  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
    - name: Deploy to production
      run: |
        # Update Kubernetes deployment
        kubectl set image deployment/qwe-app qwe-app=${{ secrets.REGISTRY_URL }}/qwe-app:${{ github.sha }}
        kubectl rollout status deployment/qwe-app

Conclusion

This deployment guide provides comprehensive strategies for taking Qwe Framework applications from development to production. The combination of containerization, cloud platforms, monitoring, and security measures ensures robust, scalable, and secure deployments.

Key Takeaways

  • 🐳 Containerization: Use Docker for consistent deployments
  • ☁️ Cloud Ready: Deploy on any major cloud platform
  • 📊 Monitoring: Implement comprehensive logging and metrics
  • 🔒 Security: Follow production security best practices
  • ⚡ Performance: Optimize for production workloads
  • 🔄 Automation: Use CI/CD for reliable deployments

Next Steps


Need help? Check the API Reference for complete deployment and configuration documentation.