Production Setup - RumenDamyanov/js-chess GitHub Wiki

Production Setup

Complete guide for deploying the JS Chess Framework Showcase to production environments.

Overview

This guide covers production deployment strategies for:

  • Static hosting (Netlify, Vercel, GitHub Pages)
  • Container orchestration (Docker, Kubernetes)
  • Cloud platforms (AWS, Google Cloud, Azure)
  • CDN configuration and optimization
  • Monitoring and logging setup

Pre-Deployment Checklist

Code Quality

  • All tests passing (unit, integration, E2E)
  • Code coverage > 80%
  • ESLint and Prettier checks passing
  • TypeScript compilation without errors
  • Bundle size optimization completed
  • Security audit passed

Performance Optimization

  • Images optimized and compressed
  • Bundle splitting configured
  • Tree shaking enabled
  • Lazy loading implemented
  • Service worker configured
  • Caching strategies implemented

Security

  • Environment variables secured
  • API endpoints protected
  • HTTPS enforced
  • Security headers configured
  • Content Security Policy implemented
  • XSS and CSRF protection enabled

Build Configuration

Environment Variables

# .env.production
NODE_ENV=production
CHESS_API_URL=https://api.yourchess.com
ANALYTICS_ID=GA_MEASUREMENT_ID
SENTRY_DSN=your_sentry_dsn
CDN_URL=https://cdn.yourchess.com

Production Build Scripts

{
  "scripts": {
    "build:prod": "npm run build:all && npm run optimize",
    "build:all": "npm run build:angular && npm run build:react && npm run build:vue",
    "build:angular": "cd angular-chess && npm run build:prod",
    "build:react": "cd react-chess && npm run build",
    "build:vue": "cd vue-chess && npm run build",
    "optimize": "npm run compress && npm run analyze",
    "compress": "gzip -r dist/ && brotli -r dist/",
    "analyze": "npm run analyze:angular && npm run analyze:react && npm run analyze:vue"
  }
}

Webpack Production Configuration

// webpack.prod.js
const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  mode: 'production',
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: true,
            drop_debugger: true,
          },
        },
      }),
    ],
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
        },
        chess: {
          test: /[\\/]src[\\/]chess[\\/]/,
          name: 'chess-logic',
          chunks: 'all',
        },
      },
    },
  },
  plugins: [
    new CompressionPlugin({
      algorithm: 'gzip',
      test: /\.(js|css|html|svg)$/,
      threshold: 8192,
      minRatio: 0.8,
    }),
    new CompressionPlugin({
      algorithm: 'brotliCompress',
      test: /\.(js|css|html|svg)$/,
      threshold: 8192,
      minRatio: 0.8,
    }),
    process.env.ANALYZE && new BundleAnalyzerPlugin(),
  ].filter(Boolean),
};

Static Hosting Deployment

Netlify Deployment

netlify.toml Configuration

[build]
  publish = "dist"
  command = "npm run build:prod"

[build.environment]
  NODE_VERSION = "18"
  NPM_VERSION = "9"

[redirects](/RumenDamyanov/js-chess/wiki/redirects)
  from = "/angular/*"
  to = "/angular/index.html"
  status = 200

[redirects](/RumenDamyanov/js-chess/wiki/redirects)
  from = "/react/*"
  to = "/react/index.html"
  status = 200

[redirects](/RumenDamyanov/js-chess/wiki/redirects)
  from = "/vue/*"
  to = "/vue/index.html"
  status = 200

[headers](/RumenDamyanov/js-chess/wiki/headers)
  for = "/*.js"
  [headers.values]
    Cache-Control = "public, max-age=31536000, immutable"

[headers](/RumenDamyanov/js-chess/wiki/headers)
  for = "/*.css"
  [headers.values]
    Cache-Control = "public, max-age=31536000, immutable"

[headers](/RumenDamyanov/js-chess/wiki/headers)
  for = "/*"
  [headers.values]
    X-Frame-Options = "DENY"
    X-XSS-Protection = "1; mode=block"
    X-Content-Type-Options = "nosniff"
    Referrer-Policy = "strict-origin-when-cross-origin"
    Content-Security-Policy = "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"

Deployment Script

#!/bin/bash
# deploy-netlify.sh

set -e

echo "Building for production..."
npm run build:prod

echo "Running final tests..."
npm run test:prod

echo "Deploying to Netlify..."
netlify deploy --prod --dir=dist

echo "Deployment complete!"
echo "URL: https://js-chess-showcase.netlify.app"

Vercel Deployment

vercel.json Configuration

{
  "version": 2,
  "builds": [
    {
      "src": "package.json",
      "use": "@vercel/static-build",
      "config": {
        "distDir": "dist"
      }
    }
  ],
  "routes": [
    {
      "src": "/angular/(.*)",
      "dest": "/angular/index.html"
    },
    {
      "src": "/react/(.*)",
      "dest": "/react/index.html"
    },
    {
      "src": "/vue/(.*)",
      "dest": "/vue/index.html"
    }
  ],
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "X-Frame-Options",
          "value": "DENY"
        },
        {
          "key": "X-Content-Type-Options",
          "value": "nosniff"
        },
        {
          "key": "X-XSS-Protection",
          "value": "1; mode=block"
        }
      ]
    }
  ]
}

GitHub Pages Deployment

GitHub Actions Workflow

# .github/workflows/deploy.yml
name: Deploy to GitHub Pages

on:
  push:
    branches: [ main ]
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: "pages"
  cancel-in-progress: true

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        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: Build for production
        run: npm run build:prod
        env:
          NODE_ENV: production
          CHESS_API_URL: ${{ secrets.CHESS_API_URL }}

      - name: Setup Pages
        uses: actions/configure-pages@v2

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v1
        with:
          path: 'dist'

  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v1

Container Deployment

Multi-Stage Dockerfile

# Dockerfile.prod
# Build stage
FROM node:18-alpine AS builder

WORKDIR /app

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

# Install dependencies
RUN npm ci --only=production

# Copy source code
COPY . .

# Build all frameworks
RUN npm run build:prod

# Production stage
FROM nginx:alpine AS production

# Copy custom nginx configuration
COPY nginx.conf /etc/nginx/nginx.conf

# Copy built applications
COPY --from=builder /app/dist /usr/share/nginx/html

# Copy SSL certificates (if using)
COPY ssl/ /etc/nginx/ssl/

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost/ || exit 1

EXPOSE 80 443

CMD ["nginx", "-g", "daemon off;"]

Nginx Configuration

# nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
    use epoll;
    multi_accept on;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Logging
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
    access_log /var/log/nginx/access.log main;

    # Performance
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/json
        application/javascript
        application/xml+rss
        application/atom+xml
        image/svg+xml;

    # Brotli compression (if available)
    brotli on;
    brotli_comp_level 6;
    brotli_types
        text/plain
        text/css
        application/json
        application/javascript
        text/xml
        application/xml
        application/xml+rss
        text/javascript;

    # Security headers
    add_header X-Frame-Options "DENY" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' 'unsafe-eval'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https:; frame-ancestors 'none';" always;

    # Rate limiting
    limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=api:10m rate=5r/s;

    server {
        listen 80;
        server_name _;
        root /usr/share/nginx/html;
        index index.html;

        # Rate limiting
        limit_req zone=general burst=20 nodelay;

        # Main application routes
        location / {
            try_files $uri $uri/ /index.html;

            # Cache static assets
            location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
                expires 1y;
                add_header Cache-Control "public, immutable";
                access_log off;
            }
        }

        # Framework-specific routes
        location /angular/ {
            alias /usr/share/nginx/html/angular/;
            try_files $uri $uri/ /angular/index.html;
        }

        location /react/ {
            alias /usr/share/nginx/html/react/;
            try_files $uri $uri/ /react/index.html;
        }

        location /vue/ {
            alias /usr/share/nginx/html/vue/;
            try_files $uri $uri/ /vue/index.html;
        }

        # API proxy (if needed)
        location /api/ {
            limit_req zone=api burst=10 nodelay;
            proxy_pass $CHESS_API_URL;
            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;
        }

        # Health check
        location /health {
            access_log off;
            return 200 "healthy\n";
            add_header Content-Type text/plain;
        }

        # Error pages
        error_page 404 /404.html;
        error_page 500 502 503 504 /50x.html;

        location = /50x.html {
            root /usr/share/nginx/html;
        }
    }

    # HTTPS configuration (if using SSL)
    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 configuration
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
        ssl_prefer_server_ciphers off;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 10m;

        # HSTS
        add_header Strict-Transport-Security "max-age=63072000" always;

        # Same location blocks as HTTP server
        # ... (inherit from above)
    }
}

Docker Compose for Production

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

services:
  chess-frontend:
    build:
      context: .
      dockerfile: Dockerfile.prod
    container_name: chess-frontend
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    environment:
      - CHESS_API_URL=${CHESS_API_URL}
    volumes:
      - ./ssl:/etc/nginx/ssl:ro
      - ./logs/nginx:/var/log/nginx
    networks:
      - chess-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

  chess-api:
    image: rumendamyanov/go-chess:latest
    container_name: chess-api
    restart: unless-stopped
    expose:
      - "8080"
    environment:
      - GIN_MODE=release
      - DATABASE_URL=${DATABASE_URL}
    networks:
      - chess-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  # Monitoring services
  prometheus:
    image: prom/prometheus:latest
    container_name: chess-prometheus
    restart: unless-stopped
    ports:
      - "9090:9090"
    volumes:
      - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    networks:
      - chess-network

  grafana:
    image: grafana/grafana:latest
    container_name: chess-grafana
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
    volumes:
      - grafana_data:/var/lib/grafana
      - ./monitoring/grafana:/etc/grafana/provisioning
    networks:
      - chess-network

networks:
  chess-network:
    driver: bridge

volumes:
  prometheus_data:
  grafana_data:

Cloud Platform Deployment

AWS S3 + CloudFront

#!/bin/bash
# deploy-aws.sh

# Build the application
npm run build:prod

# Sync to S3
aws s3 sync dist/ s3://your-chess-bucket --delete --cache-control "max-age=31536000"

# Invalidate CloudFront cache
aws cloudfront create-invalidation --distribution-id YOUR_DISTRIBUTION_ID --paths "/*"

echo "Deployed to AWS successfully!"

CloudFormation Template

# infrastructure.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Chess Showcase Infrastructure'

Parameters:
  DomainName:
    Type: String
    Default: chess.yourdomain.com

Resources:
  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub '${DomainName}-static'
      WebsiteConfiguration:
        IndexDocument: index.html
        ErrorDocument: error.html
      PublicAccessBlockConfiguration:
        BlockPublicAcls: false
        BlockPublicPolicy: false
        IgnorePublicAcls: false
        RestrictPublicBuckets: false

  CloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Aliases:
          - !Ref DomainName
        DefaultCacheBehavior:
          TargetOriginId: S3Origin
          ViewerProtocolPolicy: redirect-to-https
          Compress: true
          CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # Managed-CachingOptimized
        Origins:
          - Id: S3Origin
            DomainName: !GetAtt S3Bucket.RegionalDomainName
            S3OriginConfig:
              OriginAccessIdentity: !Sub 'origin-access-identity/cloudfront/${OriginAccessIdentity}'
        Enabled: true
        DefaultRootObject: index.html
        PriceClass: PriceClass_100

  OriginAccessIdentity:
    Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
    Properties:
      CloudFrontOriginAccessIdentityConfig:
        Comment: !Sub 'OAI for ${DomainName}'

Outputs:
  CloudFrontURL:
    Description: 'CloudFront Distribution URL'
    Value: !GetAtt CloudFrontDistribution.DomainName

Google Cloud Platform

#!/bin/bash
# deploy-gcp.sh

# Build the application
npm run build:prod

# Deploy to Cloud Storage
gsutil -m rsync -r -d dist/ gs://your-chess-bucket

# Set up load balancer (if needed)
gcloud compute backend-buckets create chess-backend --gcs-bucket-name=your-chess-bucket

echo "Deployed to GCP successfully!"

Azure Static Web Apps

# .github/workflows/azure-static-web-apps.yml
name: Azure Static Web Apps CI/CD

on:
  push:
    branches:
      - main
  pull_request:
    types: [opened, synchronize, reopened, closed]
    branches:
      - main

jobs:
  build_and_deploy_job:
    if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
    runs-on: ubuntu-latest
    name: Build and Deploy Job
    steps:
      - uses: actions/checkout@v3
        with:
          submodules: true

      - name: Build And Deploy
        id: builddeploy
        uses: Azure/static-web-apps-deploy@v1
        with:
          azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
          repo_token: ${{ secrets.GITHUB_TOKEN }}
          action: "upload"
          app_location: "/"
          api_location: ""
          output_location: "dist"
          app_build_command: "npm run build:prod"

  close_pull_request_job:
    if: github.event_name == 'pull_request' && github.event.action == 'closed'
    runs-on: ubuntu-latest
    name: Close Pull Request Job
    steps:
      - name: Close Pull Request
        id: closepullrequest
        uses: Azure/static-web-apps-deploy@v1
        with:
          azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
          action: "close"

Kubernetes Deployment

Kubernetes Manifests

# k8s/namespace.yml
apiVersion: v1
kind: Namespace
metadata:
  name: chess-showcase

---
# k8s/deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: chess-frontend
  namespace: chess-showcase
spec:
  replicas: 3
  selector:
    matchLabels:
      app: chess-frontend
  template:
    metadata:
      labels:
        app: chess-frontend
    spec:
      containers:
      - name: chess-frontend
        image: your-registry/chess-frontend:latest
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: "64Mi"
            cpu: "50m"
          limits:
            memory: "128Mi"
            cpu: "100m"
        livenessProbe:
          httpGet:
            path: /health
            port: 80
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /health
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5

---
# k8s/service.yml
apiVersion: v1
kind: Service
metadata:
  name: chess-frontend-service
  namespace: chess-showcase
spec:
  selector:
    app: chess-frontend
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  type: ClusterIP

---
# k8s/ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: chess-frontend-ingress
  namespace: chess-showcase
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  tls:
  - hosts:
    - chess.yourdomain.com
    secretName: chess-tls
  rules:
  - host: chess.yourdomain.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: chess-frontend-service
            port:
              number: 80

Helm Chart

# helm/chess-showcase/Chart.yaml
apiVersion: v2
name: chess-showcase
description: A Helm chart for Chess Showcase
type: application
version: 0.1.0
appVersion: "1.0.0"

# helm/chess-showcase/values.yaml
replicaCount: 3

image:
  repository: your-registry/chess-frontend
  pullPolicy: IfNotPresent
  tag: "latest"

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: true
  className: "nginx"
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
  hosts:
    - host: chess.yourdomain.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: chess-tls
      hosts:
        - chess.yourdomain.com

resources:
  limits:
    cpu: 100m
    memory: 128Mi
  requests:
    cpu: 50m
    memory: 64Mi

autoscaling:
  enabled: true
  minReplicas: 2
  maxReplicas: 10
  targetCPUUtilizationPercentage: 80

Monitoring and Logging

Application Monitoring

// monitoring/metrics.js
import { createPrometheusMetrics } from './prometheus';

class ApplicationMetrics {
  constructor() {
    this.metrics = createPrometheusMetrics();
  }

  recordGameStart() {
    this.metrics.gamesStarted.inc();
  }

  recordGameComplete(duration) {
    this.metrics.gamesCompleted.inc();
    this.metrics.gameDuration.observe(duration);
  }

  recordAIMove(difficulty, duration) {
    this.metrics.aiMoves.inc({ difficulty });
    this.metrics.aiThinkTime.observe({ difficulty }, duration);
  }

  recordError(type) {
    this.metrics.errors.inc({ type });
  }
}

export const metrics = new ApplicationMetrics();

Error Tracking with Sentry

// monitoring/sentry.js
import * as Sentry from '@sentry/browser';
import { Integrations } from '@sentry/tracing';

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  environment: process.env.NODE_ENV,
  integrations: [
    new Integrations.BrowserTracing(),
  ],
  tracesSampleRate: 0.1,
  beforeSend(event) {
    // Filter out non-critical errors in production
    if (event.level === 'warning' && process.env.NODE_ENV === 'production') {
      return null;
    }
    return event;
  }
});

export { Sentry };

Health Check Endpoint

// health.js
export function createHealthCheck() {
  return {
    status: 'healthy',
    timestamp: new Date().toISOString(),
    version: process.env.APP_VERSION || '1.0.0',
    uptime: process.uptime(),
    memory: process.memoryUsage(),
    checks: {
      api: await checkAPIHealth(),
      database: await checkDatabaseHealth(),
      cache: await checkCacheHealth()
    }
  };
}

Performance Optimization

CDN Configuration

// CDN cache headers configuration
const cacheHeaders = {
  // Static assets - long cache
  '**/*.{js,css,png,jpg,jpeg,gif,ico,svg,woff,woff2}': {
    'Cache-Control': 'public, max-age=31536000, immutable',
    'ETag': true
  },

  // HTML files - short cache
  '**/*.html': {
    'Cache-Control': 'public, max-age=300, must-revalidate',
    'ETag': true
  },

  // API responses - no cache
  '/api/**': {
    'Cache-Control': 'no-store, no-cache, must-revalidate',
    'Pragma': 'no-cache'
  }
};

Service Worker

// sw.js - Service Worker for caching
const CACHE_NAME = 'chess-showcase-v1';
const urlsToCache = [
  '/',
  '/static/js/bundle.js',
  '/static/css/main.css',
  '/static/media/pieces.svg'
];

self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then((cache) => cache.addAll(urlsToCache))
  );
});

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then((response) => {
        // Return cached version or fetch from network
        return response || fetch(event.request);
      })
  );
});

Security Considerations

Content Security Policy

<!-- CSP Meta Tag -->
<meta http-equiv="Content-Security-Policy"
      content="default-src 'self';
               script-src 'self' 'unsafe-inline' https://analytics.google.com;
               style-src 'self' 'unsafe-inline';
               img-src 'self' data: https:;
               font-src 'self' data:;
               connect-src 'self' https://api.chess.com;
               frame-ancestors 'none';">

Environment Security

# Secure environment variables
CHESS_API_KEY=your_secure_api_key
DATABASE_PASSWORD=your_secure_db_password
JWT_SECRET=your_jwt_secret
SENTRY_DSN=your_sentry_dsn

# Never commit these to version control!
# Use secret management services in production

Rollback Strategy

Blue-Green Deployment

#!/bin/bash
# blue-green-deploy.sh

CURRENT_ENV=$(kubectl get service chess-frontend -o jsonpath='{.spec.selector.version}')
NEW_ENV=$([ "$CURRENT_ENV" = "blue" ] && echo "green" || echo "blue")

echo "Current environment: $CURRENT_ENV"
echo "Deploying to: $NEW_ENV"

# Deploy new version
kubectl set image deployment/chess-frontend-$NEW_ENV chess-frontend=your-registry/chess-frontend:$NEW_VERSION

# Wait for rollout
kubectl rollout status deployment/chess-frontend-$NEW_ENV

# Run health checks
if curl -f http://chess-frontend-$NEW_ENV/health; then
    echo "Health check passed, switching traffic..."
    kubectl patch service chess-frontend -p '{"spec":{"selector":{"version":"'$NEW_ENV'"}}}'
    echo "Traffic switched to $NEW_ENV"
else
    echo "Health check failed, keeping current environment"
    exit 1
fi

Backup and Recovery

Database Backup

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

TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="chess_db_backup_$TIMESTAMP.sql"

# Create backup
pg_dump $DATABASE_URL > $BACKUP_FILE

# Upload to cloud storage
aws s3 cp $BACKUP_FILE s3://your-backup-bucket/database/

# Keep only last 30 days of backups
aws s3 ls s3://your-backup-bucket/database/ | head -n -30 | awk '{print $4}' | xargs -I {} aws s3 rm s3://your-backup-bucket/database/{}

echo "Database backup completed: $BACKUP_FILE"

Next Steps