diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e0256f9 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,69 @@ +# Backend-only Docker image - exclude frontend entirely +frontend/ + +# Python +backend/venv/ +backend/__pycache__/ +backend/**/__pycache__/ +backend/**/*.pyc +backend/**/*.pyo +backend/**/*.pyd +backend/.pytest_cache/ +backend/htmlcov/ +backend/.coverage +backend/.env +backend/.env.local +backend/env.production.template + +# Git +.git/ +.gitignore +.gitattributes + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ +.DS_Store + +# Documentation (not needed in container) +docs/ +*.md + +# Testing +backend/tests/ + +# Build artifacts +*.log + +# Misc +.cursor/ +terminals/ +*.local.conf +*.example +*.template +Dockerfile +docker-compose.yml +.dockerignore + +# CI/CD +.github/ + +# Nginx configs +nginx/ +nginx.conf + +# Project specific +Multi\ User\ Pregame\ Lobby\ API.postman_collection.json +image.png +lobby.png +LICENSE +GAME.MD +SERVING.md +node_modules/ +package.json +package-lock.json +eslint.config.js + diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..81da00c --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,116 @@ +name: Deploy Backend to AWS ECS + +on: + push: + branches: + - main + paths: + - 'backend/**' + - 'Dockerfile' + - '.github/workflows/deploy.yml' + workflow_dispatch: + +env: + AWS_REGION: us-east-1 + ECR_REPOSITORY: lockout-game-backend + ECS_CLUSTER: lockout-game-cluster + ECS_SERVICE: lockout-game-service + ECS_TASK_DEFINITION: lockout-game-task + CONTAINER_NAME: lockout-game-backend + +jobs: + test: + name: Run Backend Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + cache: 'pip' + cache-dependency-path: backend/requirements.txt + + - name: Install backend dependencies + working-directory: backend + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install pytest pytest-cov + + - name: Run backend tests + working-directory: backend + run: pytest + + build-and-deploy: + name: Build and Deploy to ECS + needs: test + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2 + + - name: Build, tag, and push image to Amazon ECR + id: build-image + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + IMAGE_TAG: ${{ github.sha }} + run: | + # Build Docker image + docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . + docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:latest + + # Push to ECR + docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG + docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest + + # Output image URI for use in next steps + echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT + + - name: Download current task definition + run: | + aws ecs describe-task-definition \ + --task-definition ${{ env.ECS_TASK_DEFINITION }} \ + --query taskDefinition > task-definition.json + + - name: Fill in the new image ID in the Amazon ECS task definition + id: task-def + uses: aws-actions/amazon-ecs-render-task-definition@v1 + with: + task-definition: task-definition.json + container-name: ${{ env.CONTAINER_NAME }} + image: ${{ steps.build-image.outputs.image }} + + - name: Deploy Amazon ECS task definition + uses: aws-actions/amazon-ecs-deploy-task-definition@v1 + with: + task-definition: ${{ steps.task-def.outputs.task-definition }} + service: ${{ env.ECS_SERVICE }} + cluster: ${{ env.ECS_CLUSTER }} + wait-for-service-stability: true + + - name: Deployment summary + run: | + echo "### Backend Deployment Successful! πŸš€" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Image**: ${{ steps.build-image.outputs.image }}" >> $GITHUB_STEP_SUMMARY + echo "- **Cluster**: ${{ env.ECS_CLUSTER }}" >> $GITHUB_STEP_SUMMARY + echo "- **Service**: ${{ env.ECS_SERVICE }}" >> $GITHUB_STEP_SUMMARY + echo "- **Region**: ${{ env.AWS_REGION }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Check the [ECS Console](https://console.aws.amazon.com/ecs/v2/clusters/${{ env.ECS_CLUSTER }}/services/${{ env.ECS_SERVICE }}/health?region=${{ env.AWS_REGION }}) for service status." >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8a06792..2ce9168 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,4 +30,4 @@ jobs: run: npm ci - name: Run tests - run: npm run test + run: npm run test -- --run diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..42d5b42 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,35 @@ +# Backend-only Dockerfile for Lockout Game Flask API +FROM python:3.12-slim + +WORKDIR /app + +# Install system dependencies: curl for health checks +RUN apt-get update && apt-get install -y --no-install-recommends \ + curl \ + gcc \ + && rm -rf /var/lib/apt/lists/* + +# Copy backend requirements and install Python dependencies +COPY backend/requirements.txt ./backend/ +RUN pip install --no-cache-dir -r backend/requirements.txt + +# Copy backend application code +COPY backend/ ./backend/ + +# Expose port 5000 (Flask/Gunicorn) +EXPOSE 5000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ + CMD curl -f http://localhost:5000/health || exit 1 + +# Run Gunicorn with eventlet worker for WebSocket support +CMD ["gunicorn", "--bind", "0.0.0.0:5000", \ + "--workers", "2", \ + "--worker-class", "eventlet", \ + "--timeout", "120", \ + "--access-logfile", "-", \ + "--error-logfile", "-", \ + "--log-level", "info", \ + "backend.app:app"] + diff --git a/README.md b/README.md index 51cd9c8..5acd260 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,94 @@ Optionally, the **Game Host** can use **Force Start** (bypasses balance check bu --- +## πŸš€ Deployment + +The Lockout Game is designed for deployment on AWS using a split architecture: + +- **Backend (Flask + Socket.IO)**: AWS ECS Fargate with Application Load Balancer +- **Frontend (React)**: AWS Amplify with CloudFront CDN + +### Deployment Architecture + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Users β”‚ +β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ + β”‚ HTTPS + β”‚ +β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ AWS Amplify β”‚ +β”‚ Frontend β”‚ +β”‚ CloudFront β”‚ +β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ API/WebSocket + β”‚ HTTPS + β”‚ +β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ ALB (HTTPS) β”‚ +β”‚ Backend API β”‚ +β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ +β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ ECS Fargate β”‚ +β”‚ Flask Tasks β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +### Quick Start Deployment + +1. **Deploy Backend to ECS Fargate** + + - See [AWS Backend Deployment Guide](docs/aws-backend-deployment.md) + - Estimated setup time: 30-60 minutes + +2. **Deploy Frontend to Amplify** + + - See [AWS Frontend Deployment Guide](docs/aws-frontend-deployment.md) + - Estimated setup time: 15-30 minutes + +3. **Troubleshooting** + - See [Deployment Troubleshooting Guide](docs/deployment-troubleshooting.md) + +### Local Testing with Docker + +Test the backend in a production-like environment: + +```bash +# Build and run backend container +docker-compose up --build + +# Backend available at http://localhost:5000 +# Test health: curl http://localhost:5000/health +``` + +### CI/CD + +Automated deployments are configured via GitHub Actions: + +- **Backend**: Deploys to ECS on changes to `backend/` directory +- **Frontend**: Deploys to Amplify on changes to `frontend/` directory + +See `.github/workflows/` for workflow configurations. + +### Environment Variables + +**Backend** (AWS Secrets Manager): + +- `FLASK_ENV` - Application environment (production) +- `SECRET_KEY` - Flask secret key (generate securely) +- `FRONTEND_URL` - Frontend domain for CORS +- `ALLOWED_ORIGINS` - Comma-separated list of allowed origins + +**Frontend** (AWS Amplify Environment Variables): + +- `VITE_API_URL` - Backend API URL +- `VITE_SOCKET_URL` - Backend WebSocket URL + +See `env.template` and `backend/env.production.template` for examples. + +--- + ## πŸ“œ Contribution Notes - Use `Grid2` from `@mui/material` with the `size={{ xs, sm }}` prop. diff --git a/backend/app.py b/backend/app.py index 44615cd..f59ce7f 100644 --- a/backend/app.py +++ b/backend/app.py @@ -1,5 +1,5 @@ # backend/app.py -from flask import Flask +from flask import Flask, jsonify from flask_socketio import SocketIO from flask_cors import CORS from .config import Config @@ -10,9 +10,20 @@ app = Flask(__name__) app.config.from_object(Config) -CORS(app) -socketio = SocketIO(app, cors_allowed_origins="*", async_mode='eventlet') +# Configure CORS with allowed origins from config +CORS(app, origins=app.config.get('ALLOWED_ORIGINS', '*')) + +socketio = SocketIO(app, cors_allowed_origins=app.config.get('ALLOWED_ORIGINS', '*'), async_mode='eventlet') + +# Health check endpoint for ALB +@app.route('/health', methods=['GET']) +def health_check(): + """Health check endpoint for load balancer""" + return jsonify({ + 'status': 'healthy', + 'service': 'lockout-game' + }), 200 # Register REST API routes app.register_blueprint(lobby_bp, url_prefix='/api') @@ -23,4 +34,4 @@ register_game_socket_handlers(socketio) # Register our new game socket handlers if __name__ == '__main__': - socketio.run(app, debug=True) + socketio.run(app, debug=True, host='0.0.0.0', port=5000) diff --git a/backend/config.py b/backend/config.py index 05d280b..0bc2cca 100644 --- a/backend/config.py +++ b/backend/config.py @@ -5,4 +5,24 @@ load_dotenv() class Config: + # Environment + FLASK_ENV = os.getenv('FLASK_ENV', 'development') + DEBUG = FLASK_ENV == 'development' + + # Security + SECRET_KEY = os.getenv('SECRET_KEY', 'dev-secret-key-change-in-production') + + # URLs and Origins FRONTEND_URL = os.getenv('FRONTEND_URL', 'http://localhost:5173') # Default for local dev + + # Configure allowed origins based on environment + if FLASK_ENV == 'production': + # In production, use specific allowed origins + ALLOWED_ORIGINS = os.getenv('ALLOWED_ORIGINS', FRONTEND_URL).split(',') + else: + # In development, allow all origins for easier testing + ALLOWED_ORIGINS = '*' + + # Server configuration + PORT = int(os.getenv('PORT', 5000)) + HOST = os.getenv('HOST', '0.0.0.0') diff --git a/backend/env.production.template b/backend/env.production.template new file mode 100644 index 0000000..280706c --- /dev/null +++ b/backend/env.production.template @@ -0,0 +1,30 @@ +# Production Environment Configuration for Lockout Game +# These variables should be stored in AWS Secrets Manager +# This file serves as a template/reference only + +# Application Environment +FLASK_ENV=production + +# Security +# Generate a secure random key: python -c "import secrets; print(secrets.token_hex(32))" +SECRET_KEY= + +# Frontend URL (your custom domain) +FRONTEND_URL=https://lockout.yourdomain.com + +# Allowed Origins (comma-separated list) +ALLOWED_ORIGINS=https://lockout.yourdomain.com + +# Server Configuration +PORT=5000 +HOST=0.0.0.0 + +# AWS Secrets Manager +# Store this configuration as JSON in AWS Secrets Manager: +# { +# "FLASK_ENV": "production", +# "SECRET_KEY": "", +# "FRONTEND_URL": "https://lockout.yourdomain.com", +# "ALLOWED_ORIGINS": "https://lockout.yourdomain.com" +# } + diff --git a/backend/requirements.txt b/backend/requirements.txt index 12470af..7fe5d02 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -7,6 +7,7 @@ Flask==3.1.0 Flask-Cors==5.0.0 Flask-SocketIO==5.5.1 greenlet==3.1.1 +gunicorn==23.0.0 h11==0.14.0 itsdangerous==2.2.0 Jinja2==3.1.5 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..b82f0d5 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,19 @@ +services: + backend: + build: + context: . + dockerfile: Dockerfile + ports: + - '5000:5000' + environment: + - FLASK_ENV=development + - SECRET_KEY=local-dev-secret-key + - FRONTEND_URL=http://localhost:5173 + - ALLOWED_ORIGINS=http://localhost:5173,http://localhost:5174 + healthcheck: + test: ['CMD', 'curl', '-f', 'http://localhost:5000/health'] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + restart: unless-stopped diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md new file mode 100644 index 0000000..69a786c --- /dev/null +++ b/docs/DEPLOYMENT.md @@ -0,0 +1,344 @@ +# AWS Deployment Summary + +## Overview + +The Lockout Game has been configured for deployment on AWS using a modern, scalable architecture: + +- **Backend**: Flask + Socket.IO on AWS ECS Fargate +- **Frontend**: React on AWS Amplify with CloudFront CDN +- **CI/CD**: GitHub Actions for automated deployments +- **Secrets**: AWS Secrets Manager for secure configuration + +## Architecture Diagram + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Internet β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ β”‚ + β”‚ β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ CloudFront CDN β”‚ β”‚ Route53 DNS β”‚ + β”‚ (Amplify) β”‚ β”‚ api.domain.com β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ AWS Amplify β”‚ β”‚ ALB (HTTPS) β”‚ + β”‚ Static Hosting β”‚ β”‚ SSL Termination β”‚ + β”‚ React Frontend β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ Target Group β”‚ + β”‚ Health Checks β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ ECS Fargate β”‚ + β”‚ Service (2 tasks)β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ β”‚ β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β” + β”‚ Flask Containerβ”‚ β”‚Flask Containerβ”‚ β”‚AWS Secrets β”‚ + β”‚ Gunicorn β”‚ β”‚ Gunicorn β”‚ β”‚Manager β”‚ + β”‚ + eventlet β”‚ β”‚+ eventlet β”‚ β”‚(Env Vars) β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +## Files Created + +### Docker & Container Configuration + +- βœ… `Dockerfile` - Backend-only container (Python + Flask + Gunicorn) +- βœ… `.dockerignore` - Optimized build context +- βœ… `docker-compose.yml` - Local testing environment + +### Frontend Configuration + +- βœ… `frontend/amplify.yml` - Amplify build specification +- βœ… `frontend/src/config.js` - Environment-based configuration +- βœ… `frontend/env.template` - Local development template +- βœ… `frontend/env.production.template` - Production configuration reference + +### Backend Configuration + +- βœ… Updated `backend/app.py` - Health endpoint, CORS configuration +- βœ… Updated `backend/config.py` - Environment-aware settings +- βœ… Updated `backend/requirements.txt` - Added gunicorn +- βœ… `backend/env.production.template` - Production configuration reference + +### Environment Templates + +- βœ… `env.template` - Root-level environment template + +### CI/CD Workflows + +- βœ… `.github/workflows/deploy.yml` - Backend deployment to ECS +- βœ… `.github/workflows/deploy-frontend.yml` - Frontend deployment to Amplify + +### Documentation + +- βœ… `docs/aws-backend-deployment.md` - Complete ECS Fargate setup guide +- βœ… `docs/aws-frontend-deployment.md` - Complete Amplify setup guide +- βœ… `docs/deployment-troubleshooting.md` - Common issues and solutions +- βœ… `docs/deployment-quick-reference.md` - Quick command reference +- βœ… Updated `README.md` - Added deployment section + +## Deployment Steps + +### Initial Setup (One-time) + +1. **Backend Infrastructure** (~30-60 minutes) + + - Follow `docs/aws-backend-deployment.md` + - Create ECR repository, VPC, security groups, ALB, ECS cluster + - Configure AWS Secrets Manager + - Build and push initial Docker image + - Deploy ECS service + +2. **Frontend Infrastructure** (~15-30 minutes) + + - Follow `docs/aws-frontend-deployment.md` + - Connect GitHub repository to Amplify + - Configure environment variables + - Configure custom domain (optional) + - Deploy frontend + +3. **CI/CD Setup** (~5-10 minutes) + - Add GitHub secrets for AWS credentials + - Add Amplify app ID + - Push to main branch to trigger automatic deployment + +### Ongoing Deployments + +**Automated** (recommended): + +- Push code to `main` branch +- GitHub Actions automatically builds and deploys +- Backend changes trigger ECS deployment +- Frontend changes trigger Amplify deployment + +**Manual**: + +```bash +# Backend +docker build -t backend . +docker push :latest +aws ecs update-service --cluster lockout-game-cluster --service lockout-game-service --force-new-deployment + +# Frontend +aws amplify start-job --app-id --branch-name main --job-type RELEASE +``` + +## Configuration + +### Backend Environment Variables (AWS Secrets Manager) + +Stored in secret: `lockout-game/production` + +```json +{ + "FLASK_ENV": "production", + "SECRET_KEY": "", + "FRONTEND_URL": "https://yourdomain.com", + "ALLOWED_ORIGINS": "https://yourdomain.com,https://main.d123.amplifyapp.com" +} +``` + +**Generate SECRET_KEY**: + +```bash +python3 -c "import secrets; print(secrets.token_hex(32))" +``` + +### Frontend Environment Variables (Amplify Console) + +Set in Amplify Console β†’ Environment variables: + +| Variable | Example Value | +| ----------------- | ---------------------------- | +| `VITE_API_URL` | `https://api.yourdomain.com` | +| `VITE_SOCKET_URL` | `https://api.yourdomain.com` | + +## Features + +### Backend (ECS Fargate) + +- βœ… Containerized Flask application +- βœ… Gunicorn with eventlet worker for WebSocket support +- βœ… Health check endpoint (`/health`) +- βœ… Configurable CORS for multiple origins +- βœ… Secure secret management via AWS Secrets Manager +- βœ… CloudWatch logging +- βœ… Auto-scaling capable (2 tasks by default) +- βœ… High availability (multi-AZ deployment) + +### Frontend (Amplify) + +- βœ… Global CDN via CloudFront +- βœ… Automatic HTTPS with free SSL certificates +- βœ… Git-based deployments +- βœ… Environment-specific configuration +- βœ… SPA routing support +- βœ… Build-time environment variables +- βœ… Pull request previews (optional) + +### CI/CD + +- βœ… Automated testing before deployment +- βœ… Separate workflows for backend and frontend +- βœ… Path-based triggering (only deploy what changed) +- βœ… Deployment status notifications +- βœ… Rollback capability + +## Monitoring + +### Health Checks + +```bash +# Backend health +curl https://api.yourdomain.com/health + +# Frontend +curl https://yourdomain.com + +# ECS service status +aws ecs describe-services --cluster lockout-game-cluster --services lockout-game-service +``` + +### Logs + +```bash +# Backend logs (real-time) +aws logs tail /ecs/lockout-game --follow + +# Frontend build logs +# View in Amplify Console +``` + +### Metrics + +Available in CloudWatch: + +- ECS CPU/Memory utilization +- ALB request count, latency, errors +- Target health +- Amplify build success/failure rates + +## Security + +### Best Practices Implemented + +- βœ… HTTPS/TLS everywhere +- βœ… Secrets stored in AWS Secrets Manager (never in code) +- βœ… Configurable CORS (no wildcard in production) +- βœ… Security headers configured +- βœ… Private subnets for ECS tasks (optional) +- βœ… Least privilege IAM roles +- βœ… Container image scanning (ECR) + +### Additional Recommendations + +- Enable AWS WAF for DDoS protection +- Set up CloudTrail for audit logging +- Enable GuardDuty for threat detection +- Regular dependency updates (npm audit, pip audit) + +## Troubleshooting + +### Quick Diagnostics + +1. **Backend not responding** + + - Check ECS service running count + - View CloudWatch logs + - Verify target group health + +2. **CORS errors** + + - Update `ALLOWED_ORIGINS` in Secrets Manager + - Restart ECS service + - Clear browser cache + +3. **WebSocket connection fails** + - Verify ALB has sticky sessions enabled + - Check `VITE_SOCKET_URL` environment variable + - Confirm backend uses eventlet worker + +See `docs/deployment-troubleshooting.md` for comprehensive troubleshooting. + +## Next Steps + +### Post-Deployment + +1. βœ… Verify health endpoints +2. βœ… Test lobby creation and gameplay +3. βœ… Verify WebSocket real-time updates +4. βœ… Test from multiple devices/networks +5. βœ… Set up CloudWatch alarms +6. βœ… Configure auto-scaling policies (optional) + +### Future Enhancements + +- [ ] Multi-environment setup (dev, staging, production) +- [ ] Redis for Socket.IO adapter (multi-instance scaling) +- [ ] Database for persistent lobby/game state +- [ ] Enhanced monitoring (New Relic, Datadog, etc.) +- [ ] Automated backups +- [ ] Disaster recovery plan + +## Support Resources + +- **AWS Backend Guide**: `docs/aws-backend-deployment.md` +- **AWS Frontend Guide**: `docs/aws-frontend-deployment.md` +- **Troubleshooting**: `docs/deployment-troubleshooting.md` +- **Quick Reference**: `docs/deployment-quick-reference.md` +- **AWS Documentation**: https://docs.aws.amazon.com/ +- **GitHub Repository**: [Your repo URL] + +## Success Criteria + +Your deployment is successful when: + +- βœ… `https://api.yourdomain.com/health` returns `{"status": "healthy"}` +- βœ… Frontend loads at `https://yourdomain.com` +- βœ… Can create and join lobbies +- βœ… Real-time updates work (Socket.IO) +- βœ… No CORS errors in browser console +- βœ… GitHub Actions workflows run successfully +- βœ… ECS service shows 2/2 running tasks +- βœ… ALB target group shows healthy targets + +## Rollback Plan + +**Backend**: + +```bash +aws ecs update-service --cluster lockout-game-cluster --service lockout-game-service --task-definition lockout-game-task: +``` + +**Frontend**: + +- Amplify Console β†’ Select previous deployment β†’ Redeploy + +## Maintenance + +### Regular Tasks + +- Monitor CloudWatch logs and metrics +- Update dependencies quarterly +- Review and rotate secrets annually +- Test backup/recovery procedures + +### Updates + +- Backend: Push to main branch (auto-deploys via GitHub Actions) +- Frontend: Push to main branch (auto-deploys via GitHub Actions) +- Infrastructure: Update AWS resources via Console or CLI + +--- + +**Deployment completed successfully!** πŸš€ + +Your Lockout Game is now running on AWS with a production-grade, scalable architecture. diff --git a/docs/aws-backend-deployment.md b/docs/aws-backend-deployment.md new file mode 100644 index 0000000..c444b82 --- /dev/null +++ b/docs/aws-backend-deployment.md @@ -0,0 +1,427 @@ +# AWS Backend Deployment Guide (ECS Fargate) + +This guide covers deploying the Lockout Game backend (Flask + Socket.IO) to AWS ECS Fargate. + +## Architecture Overview + +The backend runs as a containerized Flask application with: + +- **Gunicorn** as the WSGI server with eventlet worker for WebSocket support +- **Flask + Flask-SocketIO** for REST API and real-time communication +- **ECS Fargate** for serverless container orchestration +- **Application Load Balancer** for SSL termination and routing +- **AWS Secrets Manager** for secure configuration management + +## Prerequisites + +- AWS CLI installed and configured +- Docker installed locally +- AWS Account with appropriate IAM permissions +- Domain configured in Route53 (for custom domain) + +## Step 1: Create ECR Repository + +Store your Docker images in Amazon Elastic Container Registry: + +```bash +aws ecr create-repository \ + --repository-name lockout-game-backend \ + --region us-east-1 +``` + +Note the repository URI (format: `.dkr.ecr..amazonaws.com/lockout-game-backend`) + +## Step 2: Set Up VPC and Networking + +### Option A: Use Default VPC (Simpler) + +Most AWS accounts have a default VPC with public subnets. You can use this. + +```bash +# List your VPCs +aws ec2 describe-vpcs --query 'Vpcs[*].[VpcId,IsDefault,CidrBlock]' --output table + +# List subnets in default VPC +aws ec2 describe-subnets \ + --filters "Name=vpc-id,Values=" \ + --query 'Subnets[*].[SubnetId,AvailabilityZone,CidrBlock,MapPublicIpOnLaunch]' \ + --output table +``` + +Note at least 2 subnet IDs in different availability zones. + +### Option B: Create New VPC (Recommended for Production) + +Use AWS VPC wizard or CloudFormation to create: + +- VPC with CIDR block (e.g., 10.0.0.0/16) +- 2+ public subnets in different AZs +- Internet Gateway attached to VPC +- Route table with route to Internet Gateway + +## Step 3: Create Security Groups + +### ALB Security Group + +```bash +aws ec2 create-security-group \ + --group-name lockout-alb-sg \ + --description "Security group for Lockout Game ALB" \ + --vpc-id + +# Note the security group ID +ALB_SG_ID= + +# Allow HTTP and HTTPS from internet +aws ec2 authorize-security-group-ingress \ + --group-id $ALB_SG_ID \ + --protocol tcp \ + --port 80 \ + --cidr 0.0.0.0/0 + +aws ec2 authorize-security-group-ingress \ + --group-id $ALB_SG_ID \ + --protocol tcp \ + --port 443 \ + --cidr 0.0.0.0/0 +``` + +### ECS Task Security Group + +```bash +aws ec2 create-security-group \ + --group-name lockout-ecs-sg \ + --description "Security group for Lockout Game ECS tasks" \ + --vpc-id + +# Note the security group ID +ECS_SG_ID= + +# Allow port 5000 from ALB only +aws ec2 authorize-security-group-ingress \ + --group-id $ECS_SG_ID \ + --protocol tcp \ + --port 5000 \ + --source-group $ALB_SG_ID +``` + +## Step 4: Request ACM Certificate + +```bash +# Request certificate for your domain +aws acm request-certificate \ + --domain-name api.yourdomain.com \ + --validation-method DNS \ + --region us-east-1 + +# Note the certificate ARN +# Follow the AWS Console to add DNS validation records to Route53 +# Wait for certificate status to become "ISSUED" + +# Check status +aws acm describe-certificate --certificate-arn +``` + +## Step 5: Create Application Load Balancer + +```bash +# Create ALB +aws elbv2 create-load-balancer \ + --name lockout-backend-alb \ + --subnets \ + --security-groups $ALB_SG_ID \ + --scheme internet-facing \ + --type application \ + --ip-address-type ipv4 + +# Note the ALB ARN and DNS name +ALB_ARN= +ALB_DNS= + +# Create target group +aws elbv2 create-target-group \ + --name lockout-backend-tg \ + --protocol HTTP \ + --port 5000 \ + --vpc-id \ + --target-type ip \ + --health-check-enabled \ + --health-check-path /health \ + --health-check-interval-seconds 30 \ + --health-check-timeout-seconds 10 \ + --healthy-threshold-count 2 \ + --unhealthy-threshold-count 3 + +# Note the target group ARN +TG_ARN= + +# Create HTTPS listener +aws elbv2 create-listener \ + --load-balancer-arn $ALB_ARN \ + --protocol HTTPS \ + --port 443 \ + --certificates CertificateArn= \ + --default-actions Type=forward,TargetGroupArn=$TG_ARN + +# Create HTTP listener (redirect to HTTPS) +aws elbv2 create-listener \ + --load-balancer-arn $ALB_ARN \ + --protocol HTTP \ + --port 80 \ + --default-actions Type=redirect,RedirectConfig="{Protocol=HTTPS,Port=443,StatusCode=HTTP_301}" +``` + +## Step 6: Configure AWS Secrets Manager + +Store your backend environment variables securely: + +```bash +# Generate a secure secret key +python3 -c "import secrets; print(secrets.token_hex(32))" + +# Create secret +aws secretsmanager create-secret \ + --name lockout-game/production \ + --description "Production configuration for Lockout Game backend" \ + --secret-string '{ + "FLASK_ENV": "production", + "SECRET_KEY": "", + "FRONTEND_URL": "https://yourdomain.com", + "ALLOWED_ORIGINS": "https://yourdomain.com,https://main.d1234567890abc.amplifyapp.com" + }' + +# Note the secret ARN +``` + +## Step 7: Create IAM Roles + +### ECS Task Execution Role + +```bash +# Create trust policy file +cat > trust-policy.json < secrets-policy.json <" + } + ] +} +EOF + +aws iam put-role-policy \ + --role-name lockout-ecs-execution-role \ + --policy-name SecretsManagerAccess \ + --policy-document file://secrets-policy.json + +# Note the role ARN +EXECUTION_ROLE_ARN=$(aws iam get-role --role-name lockout-ecs-execution-role --query 'Role.Arn' --output text) +``` + +## Step 8: Create ECS Cluster + +```bash +aws ecs create-cluster \ + --cluster-name lockout-game-cluster \ + --region us-east-1 +``` + +## Step 9: Register ECS Task Definition + +Create a file `task-definition.json`: + +```json +{ + "family": "lockout-game-task", + "networkMode": "awsvpc", + "requiresCompatibilities": ["FARGATE"], + "cpu": "512", + "memory": "1024", + "executionRoleArn": "", + "containerDefinitions": [ + { + "name": "lockout-game-backend", + "image": ":latest", + "essential": true, + "portMappings": [ + { + "containerPort": 5000, + "protocol": "tcp" + } + ], + "environment": [], + "secrets": [ + { + "name": "FLASK_ENV", + "valueFrom": ":FLASK_ENV::" + }, + { + "name": "SECRET_KEY", + "valueFrom": ":SECRET_KEY::" + }, + { + "name": "FRONTEND_URL", + "valueFrom": ":FRONTEND_URL::" + }, + { + "name": "ALLOWED_ORIGINS", + "valueFrom": ":ALLOWED_ORIGINS::" + } + ], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "/ecs/lockout-game", + "awslogs-region": "us-east-1", + "awslogs-stream-prefix": "backend", + "awslogs-create-group": "true" + } + }, + "healthCheck": { + "command": [ + "CMD-SHELL", + "curl -f http://localhost:5000/health || exit 1" + ], + "interval": 30, + "timeout": 10, + "retries": 3, + "startPeriod": 40 + } + } + ] +} +``` + +Register the task definition: + +```bash +aws ecs register-task-definition --cli-input-json file://task-definition.json +``` + +## Step 10: Create ECS Service + +```bash +aws ecs create-service \ + --cluster lockout-game-cluster \ + --service-name lockout-game-service \ + --task-definition lockout-game-task \ + --desired-count 2 \ + --launch-type FARGATE \ + --platform-version LATEST \ + --network-configuration "awsvpcConfiguration={subnets=[,],securityGroups=[$ECS_SG_ID],assignPublicIp=ENABLED}" \ + --load-balancers "targetGroupArn=$TG_ARN,containerName=lockout-game-backend,containerPort=5000" \ + --health-check-grace-period-seconds 60 +``` + +## Step 11: Configure Route53 + +```bash +# Create an A record pointing to the ALB +aws route53 change-resource-record-sets \ + --hosted-zone-id \ + --change-batch '{ + "Changes": [{ + "Action": "CREATE", + "ResourceRecordSet": { + "Name": "api.yourdomain.com", + "Type": "A", + "AliasTarget": { + "HostedZoneId": "", + "DNSName": "'$ALB_DNS'", + "EvaluateTargetHealth": true + } + } + }] + }' +``` + +Get ALB Hosted Zone ID from: https://docs.aws.amazon.com/general/latest/gr/elb.html + +## Step 12: Build and Push Initial Docker Image + +```bash +# Login to ECR +aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin .dkr.ecr.us-east-1.amazonaws.com + +# Build image +docker build -t lockout-game-backend . + +# Tag image +docker tag lockout-game-backend:latest :latest + +# Push image +docker push :latest + +# Force new deployment +aws ecs update-service \ + --cluster lockout-game-cluster \ + --service lockout-game-service \ + --force-new-deployment +``` + +## Step 13: Configure GitHub Secrets + +In your GitHub repository, add the following secrets: + +- `AWS_ACCESS_KEY_ID` - IAM user access key with ECR and ECS permissions +- `AWS_SECRET_ACCESS_KEY` - IAM user secret key +- `AWS_REGION` - e.g., `us-east-1` + +The GitHub Actions workflow will now automatically deploy on pushes to main. + +## Verification + +1. Check ECS service status: + + ```bash + aws ecs describe-services --cluster lockout-game-cluster --services lockout-game-service + ``` + +2. Test health endpoint: + + ```bash + curl https://api.yourdomain.com/health + ``` + +3. View logs: + ```bash + aws logs tail /ecs/lockout-game --follow + ``` + +## Next Steps + +- Set up CloudWatch alarms for service health +- Configure auto-scaling policies +- Set up AWS WAF for DDoS protection (optional) +- Enable ECS Exec for debugging running containers diff --git a/docs/aws-frontend-deployment.md b/docs/aws-frontend-deployment.md new file mode 100644 index 0000000..29b1e04 --- /dev/null +++ b/docs/aws-frontend-deployment.md @@ -0,0 +1,462 @@ +# AWS Frontend Deployment Guide (Amplify) + +This guide covers deploying the Lockout Game React frontend to AWS Amplify with CloudFront CDN. + +## Architecture Overview + +The frontend is deployed as a static site on AWS Amplify with: + +- **CloudFront CDN** for global content delivery +- **Automatic SSL/TLS** certificates +- **Git-based deployments** (auto-deploy on push) +- **Environment variables** for backend API configuration +- **Custom domain support** via Route53 + +## Prerequisites + +- AWS CLI installed and configured +- GitHub repository with your code +- Domain configured in Route53 (optional, for custom domain) +- Backend deployed and accessible (see `aws-backend-deployment.md`) + +## Step 1: Connect GitHub Repository + +### Via AWS Console (Recommended for Initial Setup) + +1. Go to the [AWS Amplify Console](https://console.aws.amazon.com/amplify/) +2. Click **"New app"** β†’ **"Host web app"** +3. Select **GitHub** as the repository service +4. Authorize AWS Amplify to access your GitHub account +5. Select your repository: `lockout-game` +6. Select branch: `main` + +### Via AWS CLI + +```bash +# First, create a personal access token in GitHub with repo permissions +# https://github.com/settings/tokens + +aws amplify create-app \ + --name lockout-game-frontend \ + --repository https://github.com/yourusername/lockout-game \ + --access-token \ + --enable-branch-auto-build + +# Note the App ID +APP_ID= +``` + +## Step 2: Configure Build Settings + +Amplify should auto-detect your build settings from `frontend/amplify.yml`. Verify the configuration: + +```yaml +version: 1 +frontend: + phases: + preBuild: + commands: + - npm ci + build: + commands: + - npm run build + artifacts: + baseDirectory: dist + files: + - '**/*' + cache: + paths: + - node_modules/**/* +``` + +### Update Build Settings in Console + +1. In Amplify Console, select your app +2. Go to **"App settings"** β†’ **"Build settings"** +3. Verify **Build specification** shows the above YAML +4. Set **Monorepo root directory**: `frontend` + +Or via CLI: + +```bash +aws amplify update-app \ + --app-id $APP_ID \ + --platform WEB \ + --build-spec "$(cat frontend/amplify.yml)" +``` + +## Step 3: Configure Environment Variables + +Set environment variables for the frontend to connect to your backend: + +### Via AWS Console + +1. Go to **"App settings"** β†’ **"Environment variables"** +2. Add the following variables: + +| Variable | Value | Description | +| ----------------- | ---------------------------- | --------------------- | +| `VITE_API_URL` | `https://api.yourdomain.com` | Backend API URL | +| `VITE_SOCKET_URL` | `https://api.yourdomain.com` | Backend WebSocket URL | + +### Via AWS CLI + +```bash +aws amplify update-app \ + --app-id $APP_ID \ + --environment-variables \ + VITE_API_URL=https://api.yourdomain.com \ + VITE_SOCKET_URL=https://api.yourdomain.com +``` + +**Important:** Replace `api.yourdomain.com` with your actual backend ALB domain or custom domain. + +## Step 4: Connect Branch and Enable Auto-Deploy + +### Via AWS Console (Recommended) + +1. In Amplify Console, select your app +2. Go to **"App settings"** β†’ **"General"** β†’ **"Branches"** +3. If the `main` branch is not connected: + - Click **"Connect branch"** + - Select `main` branch + - Confirm settings +4. Enable automatic deployments: + - Click on the `main` branch + - Go to **"Branch settings"** + - Ensure **"Auto build"** is enabled (toggle should be ON) +5. Trigger initial deployment by clicking **"Redeploy this version"** or push to main + +### Via AWS CLI + +```bash +# Connect the main branch with auto-build enabled +aws amplify create-branch \ + --app-id $APP_ID \ + --branch-name main \ + --enable-auto-build \ + --stage PRODUCTION + +# Trigger initial deployment +aws amplify start-job \ + --app-id $APP_ID \ + --branch-name main \ + --job-type RELEASE +``` + +### Verify Auto-Deploy is Enabled + +```bash +aws amplify get-branch \ + --app-id $APP_ID \ + --branch-name main \ + --query 'branch.{AutoBuild:enableAutoBuild,Stage:stage}' \ + --output table +``` + +You should see `AutoBuild: True`. + +### Monitor Deployment Progress + +In Amplify Console or via CLI: + +```bash +aws amplify list-jobs --app-id $APP_ID --branch-name main +``` + +**How Auto-Deploy Works:** + +1. Push code to `main` branch in GitHub +2. GitHub webhook notifies Amplify +3. Amplify automatically builds and deploys +4. Deployment status visible in Amplify Console + +## Step 5: Configure Custom Domain (Optional) + +### Prerequisites + +- Domain registered and Route53 hosted zone configured +- SSL certificate will be automatically provisioned by Amplify + +### Add Custom Domain + +#### Via AWS Console + +1. In Amplify Console, go to **"App settings"** β†’ **"Domain management"** +2. Click **"Add domain"** +3. Select your domain from Route53 or enter a custom domain +4. Configure subdomain: `lockout` or `www` (e.g., `lockout.yourdomain.com`) +5. Click **"Configure domain"** +6. Amplify will automatically: + - Request an ACM certificate + - Configure DNS records in Route53 + - Set up CloudFront distribution + +#### Via AWS CLI + +```bash +aws amplify create-domain-association \ + --app-id $APP_ID \ + --domain-name yourdomain.com \ + --sub-domain-settings '{ + "prefix": "lockout", + "branchName": "main" + }' +``` + +**DNS Verification:** + +- Amplify will automatically add CNAME records to Route53 +- Certificate validation takes 5-30 minutes +- Domain will be available at `https://lockout.yourdomain.com` + +## Step 6: Update Backend CORS + +Now that you know your Amplify URLs, update the backend CORS configuration: + +1. Go to AWS Secrets Manager +2. Edit the `lockout-game/production` secret +3. Update `ALLOWED_ORIGINS` to include your Amplify URLs: + +```json +{ + "FLASK_ENV": "production", + "SECRET_KEY": "your-secret-key", + "FRONTEND_URL": "https://lockout.yourdomain.com", + "ALLOWED_ORIGINS": "https://lockout.yourdomain.com,https://lockoutapi.yourdomain.com,https://*.amplifyapp.com" +} +``` + +**Note:** The `https://*.amplifyapp.com` wildcard allows: + +- Your main Amplify URL: `https://main.d1234567890abc.amplifyapp.com` +- All PR preview URLs: `https://pr-123.d1234567890abc.amplifyapp.com` + +4. Restart your ECS service to pick up the new configuration: + +```bash +aws ecs update-service \ + --cluster lockout-game-cluster \ + --service lockout-game-service \ + --force-new-deployment +``` + +## Step 7: Verify Automatic Deployments + +Amplify is configured to automatically deploy when you push to the `main` branch via its GitHub integration. + +**To verify automatic deployment is enabled:** + +```bash +# Check branch configuration +aws amplify get-branch \ + --app-id $APP_ID \ + --branch-name main \ + --query 'branch.{EnableAutoBuild:enableAutoBuild,Stage:stage}' \ + --output table +``` + +**How it works:** + +1. Push code to `main` branch +2. Amplify detects the push via GitHub webhook +3. Amplify automatically builds and deploys +4. Monitor progress in Amplify Console + +**No GitHub Actions needed!** Amplify handles deployments natively. + +## Step 8: Set Up Amplify Features (Optional) + +### Enable Pull Request Previews + +**⚠️ Important:** PR previews will use the same environment variables as your main branch by default, meaning they will connect to your **production backend**. + +```bash +aws amplify update-branch \ + --app-id $APP_ID \ + --branch-name main \ + --enable-pull-request-preview +``` + +Now, every PR will get a preview URL like `https://pr-123.d1234567890abc.amplifyapp.com` that connects to your production backend. + +**CORS Configuration:** Make sure your `ALLOWED_ORIGINS` in Secrets Manager includes `https://*.amplifyapp.com` to allow all PR previews to connect to the backend (see Step 6). + +### Enable Basic Authentication (for staging) + +```bash +aws amplify update-branch \ + --app-id $APP_ID \ + --branch-name main \ + --enable-basic-auth \ + --basic-auth-credentials username:password +``` + +### Configure Redirects for SPA + +Amplify should auto-detect React Router, but you can manually configure: + +In Amplify Console β†’ **"Rewrites and redirects"** β†’ Add rule: + +``` +Source: +Target: /index.html +Type: 200 (Rewrite) +``` + +Or in `frontend/amplify.yml`: + +```yaml +version: 1 +frontend: + phases: + preBuild: + commands: + - npm ci + build: + commands: + - npm run build + artifacts: + baseDirectory: dist + files: + - '**/*' + cache: + paths: + - node_modules/**/* +customHeaders: + - pattern: '**/*' + headers: + - key: 'Strict-Transport-Security' + value: 'max-age=31536000; includeSubDomains' + - key: 'X-Frame-Options' + value: 'SAMEORIGIN' + - key: 'X-Content-Type-Options' + value: 'nosniff' +``` + +## Verification + +### 1. Check Deployment Status + +```bash +aws amplify get-app --app-id $APP_ID +``` + +### 2. Test Your Application + +Visit your Amplify URL: + +- Default: `https://main.d1234567890abc.amplifyapp.com` +- Custom: `https://lockout.yourdomain.com` + +Test functionality: + +- βœ… Frontend loads +- βœ… Can create lobby (API connection works) +- βœ… WebSocket connections work (real-time updates) +- βœ… No CORS errors in browser console + +### 3. Monitor Logs + +In Amplify Console: + +- Go to your app +- Click on the branch deployment +- View build logs and deployment details + +## Troubleshooting + +### Build Failures + +**Problem**: Build fails with module not found + +**Solution**: Ensure `frontend/amplify.yml` has correct `baseDirectory`: + +```yaml +artifacts: + baseDirectory: dist # For Vite + # baseDirectory: build # For Create React App +``` + +**Problem**: Environment variables not working + +**Solution**: + +1. Verify variables are set in Amplify Console +2. Redeploy: `aws amplify start-job --app-id $APP_ID --branch-name main --job-type RELEASE` +3. Check build logs for `VITE_API_URL` value + +### CORS Errors + +**Problem**: Browser shows CORS errors + +**Solution**: + +1. Check backend logs: `aws logs tail /ecs/lockout-game --follow` +2. Verify `ALLOWED_ORIGINS` in Secrets Manager includes Amplify URL +3. Restart ECS service after updating secrets + +### WebSocket Connection Fails + +**Problem**: Socket.IO can't connect + +**Solution**: + +1. Verify `VITE_SOCKET_URL` environment variable +2. Check ALB security group allows inbound 443 +3. Verify backend health: `curl https://api.yourdomain.com/health` +4. Check browser console for specific error messages + +### Custom Domain Not Working + +**Problem**: Custom domain shows "Not found" + +**Solution**: + +1. Wait 5-30 minutes for DNS propagation +2. Check certificate status in ACM +3. Verify Route53 CNAME records were created +4. Try clearing browser cache / incognito mode + +## Rollback Deployments + +### Via Console + +1. Go to Amplify Console β†’ Your App β†’ Branch +2. Click on a previous deployment +3. Click **"Redeploy this version"** + +### Via CLI + +```bash +# List previous jobs +aws amplify list-jobs --app-id $APP_ID --branch-name main + +# Get specific job details +aws amplify get-job --app-id $APP_ID --branch-name main --job-id +``` + +## Performance Optimization + +1. **Enable Gzip Compression** (enabled by default via CloudFront) +2. **Set Cache-Control Headers**: Amplify does this automatically for static assets +3. **Use Lazy Loading**: Already implemented in React components +4. **Monitor Performance**: + - CloudFront metrics in CloudWatch + - Real User Monitoring (RUM) via CloudWatch RUM (optional add-on) + +## Security Best Practices + +1. **Enable HTTPS Only**: Automatic with Amplify +2. **Set Security Headers**: Configure in `amplify.yml` (see Step 8) +3. **Regular Dependency Updates**: Run `npm audit` and update packages +4. **Environment Variable Security**: Never commit API keys to Git +5. **Access Control**: Use Amplify basic auth for staging environments + +## Next Steps + +- Set up monitoring with CloudWatch +- Configure alarms for failed deployments +- Set up multiple environments (dev, staging, production) +- Enable AWS WAF for additional security (optional) +- Configure custom error pages diff --git a/docs/deployment-checklist.md b/docs/deployment-checklist.md new file mode 100644 index 0000000..7f0e4a6 --- /dev/null +++ b/docs/deployment-checklist.md @@ -0,0 +1,406 @@ +# AWS Deployment Checklist + +Use this checklist to ensure a successful deployment of the Lockout Game to AWS. + +## Pre-Deployment Preparation + +### AWS Account Setup + +- [ ] AWS account created and configured +- [ ] AWS CLI installed (`aws --version`) +- [ ] AWS CLI configured (`aws configure`) +- [ ] Billing alerts set up in AWS Console +- [ ] Domain registered or available in Route53 (if using custom domain) + +### Local Development Tools + +- [ ] Docker installed and running (`docker --version`) +- [ ] Git installed (`git --version`) +- [ ] Node.js v20 installed (`node --version`) +- [ ] Python 3.12+ installed (`python --version`) +- [ ] Application tested locally and working + +### Repository Setup + +- [ ] Code pushed to GitHub repository +- [ ] GitHub account has admin access to repository + +--- + +## Backend Deployment (ECS Fargate) + +### Step 1: ECR Repository + +- [ ] ECR repository created: `lockout-game-backend` +- [ ] Repository URI noted and saved +- [ ] ECR login tested: `aws ecr get-login-password | docker login ...` + +### Step 2: Networking + +- [ ] VPC selected or created +- [ ] At least 2 public subnets in different AZs identified +- [ ] Subnet IDs noted and saved +- [ ] Internet Gateway attached to VPC + +### Step 3: Security Groups + +- [ ] ALB security group created + - [ ] Inbound rule: Port 80 from 0.0.0.0/0 + - [ ] Inbound rule: Port 443 from 0.0.0.0/0 +- [ ] ECS security group created + - [ ] Inbound rule: Port 5000 from ALB security group +- [ ] Security group IDs noted and saved + +### Step 4: SSL Certificate + +- [ ] ACM certificate requested for backend domain +- [ ] DNS validation records added to Route53 +- [ ] Certificate status is "Issued" +- [ ] Certificate ARN noted and saved + +### Step 5: Application Load Balancer + +- [ ] ALB created (internet-facing) +- [ ] ALB attached to public subnets +- [ ] ALB security group attached +- [ ] Target group created: + - [ ] Protocol: HTTP + - [ ] Port: 5000 + - [ ] Target type: IP + - [ ] Health check path: `/health` + - [ ] Health check interval: 30s +- [ ] HTTPS listener created (port 443) with ACM certificate +- [ ] HTTP listener created (port 80) with redirect to HTTPS +- [ ] ALB DNS name and ARN noted and saved +- [ ] Target group ARN noted and saved + +### Step 6: IAM Roles + +- [ ] ECS task execution role created +- [ ] Managed policy attached: `AmazonECSTaskExecutionRolePolicy` +- [ ] Custom policy for Secrets Manager access created and attached +- [ ] Execution role ARN noted and saved + +### Step 7: Secrets Manager + +- [ ] Secret created: `lockout-game/production` +- [ ] SECRET_KEY generated (secure random 64-char hex) +- [ ] Secret JSON configured: + - [ ] `FLASK_ENV`: "production" + - [ ] `SECRET_KEY`: (generated key) + - [ ] `FRONTEND_URL`: (your frontend domain) + - [ ] `ALLOWED_ORIGINS`: (comma-separated list of allowed origins) +- [ ] Secret ARN noted and saved + +### Step 8: ECS Cluster + +- [ ] ECS cluster created: `lockout-game-cluster` +- [ ] Cluster ARN noted and saved + +### Step 9: Task Definition + +- [ ] task-definition.json file created with: + - [ ] Correct execution role ARN + - [ ] Correct ECR image URI + - [ ] Correct secret ARN for environment variables + - [ ] CPU: 512 + - [ ] Memory: 1024 + - [ ] Port mapping: 5000 + - [ ] CloudWatch logs configured + - [ ] Health check configured +- [ ] Task definition registered with ECS +- [ ] Task definition ARN noted and saved + +### Step 10: Docker Image + +- [ ] Docker image built locally and tested +- [ ] Image tagged with ECR URI +- [ ] Image pushed to ECR +- [ ] Image visible in ECR Console + +### Step 11: ECS Service + +- [ ] ECS service created: + - [ ] Service name: `lockout-game-service` + - [ ] Task definition: `lockout-game-task` + - [ ] Desired count: 2 + - [ ] Launch type: FARGATE + - [ ] VPC subnets configured + - [ ] Security group attached + - [ ] ALB target group attached + - [ ] Public IP enabled +- [ ] Service started successfully +- [ ] Tasks running (2/2) +- [ ] Tasks registered with target group + +### Step 12: Route53 + +- [ ] A record created for backend domain +- [ ] A record points to ALB (ALIAS record) +- [ ] DNS propagation verified (`dig api.yourdomain.com`) + +### Step 13: Backend Verification + +- [ ] Health endpoint accessible: `curl https://api.yourdomain.com/health` +- [ ] Returns `{"status": "healthy", "service": "lockout-game"}` +- [ ] No SSL certificate warnings +- [ ] CloudWatch logs show application startup + +--- + +## Frontend Deployment (Amplify) + +### Step 1: Amplify App + +- [ ] Amplify app created +- [ ] GitHub repository connected +- [ ] Branch connected: `main` +- [ ] App ID noted and saved + +### Step 2: Build Configuration + +- [ ] Build settings configured from `frontend/amplify.yml` +- [ ] Monorepo root directory set: `frontend` +- [ ] Build specification verified in Console + +### Step 3: Environment Variables + +- [ ] `VITE_API_URL` set to backend URL +- [ ] `VITE_SOCKET_URL` set to backend URL +- [ ] Environment variables visible in Amplify Console + +### Step 4: Initial Deployment + +- [ ] Initial build triggered +- [ ] Build completed successfully +- [ ] Deployment succeeded +- [ ] Default Amplify URL accessible +- [ ] Default URL noted and saved (e.g., `https://main.d123.amplifyapp.com`) + +### Step 5: Backend CORS Update + +- [ ] Amplify default URL added to backend `ALLOWED_ORIGINS` +- [ ] Secrets Manager secret updated +- [ ] ECS service restarted (`--force-new-deployment`) + +### Step 6: Custom Domain (Optional) + +- [ ] Custom domain added in Amplify Console +- [ ] Subdomain configured (e.g., `lockout`) +- [ ] DNS records created in Route53 +- [ ] SSL certificate issued by Amplify +- [ ] Custom domain accessible + +### Step 7: Frontend Verification + +- [ ] Frontend loads at Amplify URL +- [ ] Can create lobby (API connection works) +- [ ] Can join lobby +- [ ] Real-time updates work (Socket.IO) +- [ ] No CORS errors in browser console +- [ ] No JavaScript errors in console + +--- + +## CI/CD Setup (GitHub Actions) + +### Step 1: IAM User for GitHub Actions + +- [ ] IAM user created: `github-actions-lockout-game` +- [ ] Policies attached: + - [ ] ECR push permissions + - [ ] ECS update service permissions + - [ ] Amplify deployment permissions +- [ ] Access key created +- [ ] Access key ID and secret noted (keep secure!) + +### Step 2: GitHub Secrets + +- [ ] Repository Settings β†’ Secrets β†’ Actions opened +- [ ] `AWS_ACCESS_KEY_ID` added +- [ ] `AWS_SECRET_ACCESS_KEY` added +- [ ] `AWS_REGION` added (e.g., `us-east-1`) +- [ ] `AMPLIFY_APP_ID` added +- [ ] `AMPLIFY_BRANCH_NAME` added (e.g., `main`) + +### Step 3: Workflow Files + +- [ ] `.github/workflows/deploy.yml` exists (backend) +- [ ] `.github/workflows/deploy-frontend.yml` exists (frontend) +- [ ] Workflow files have correct values: + - [ ] ECR repository name + - [ ] ECS cluster name + - [ ] ECS service name + - [ ] Task definition name + - [ ] Container name + +### Step 4: Test CI/CD + +- [ ] Make a small change to backend code +- [ ] Push to main branch +- [ ] Backend workflow triggers +- [ ] Backend tests pass +- [ ] Docker image builds and pushes +- [ ] ECS service updates +- [ ] Backend deployment succeeds +- [ ] Make a small change to frontend code +- [ ] Push to main branch +- [ ] Frontend workflow triggers +- [ ] Frontend tests pass +- [ ] Amplify deployment triggers +- [ ] Frontend deployment succeeds + +--- + +## Post-Deployment Configuration + +### Monitoring Setup + +- [ ] CloudWatch Log Groups configured with retention policy +- [ ] CloudWatch alarm for ECS CPU > 80% +- [ ] CloudWatch alarm for ALB unhealthy targets +- [ ] CloudWatch alarm for ALB 5xx errors + +### Documentation + +- [ ] All ARNs and IDs documented in secure location +- [ ] Deployment runbook created for team +- [ ] Access credentials stored securely + +### Security Review + +- [ ] Security groups reviewed (least privilege) +- [ ] IAM roles reviewed (least privilege) +- [ ] Secrets rotation policy defined +- [ ] SSL/TLS certificates have auto-renewal enabled +- [ ] CORS configuration verified (no wildcards in production) + +--- + +## Final Verification + +### End-to-End Testing + +- [ ] Open frontend in browser +- [ ] Create a new lobby +- [ ] Copy lobby ID +- [ ] Open in incognito/private window +- [ ] Join lobby with different user +- [ ] Verify real-time updates work +- [ ] Select teams and roles +- [ ] Mark ready +- [ ] Start game (if implemented) +- [ ] Verify game functionality + +### Performance Testing + +- [ ] Page load time acceptable (< 3 seconds) +- [ ] API response time acceptable (< 500ms) +- [ ] WebSocket connection establishes quickly (< 2 seconds) +- [ ] No console errors or warnings + +### Cross-Browser Testing + +- [ ] Tested in Chrome +- [ ] Tested in Firefox +- [ ] Tested in Safari +- [ ] Tested in Edge +- [ ] Mobile browser tested (iOS/Android) + +### Load Testing (Optional) + +- [ ] Multiple concurrent lobbies tested +- [ ] Multiple users in single lobby tested +- [ ] ECS service scales appropriately +- [ ] No memory leaks observed + +--- + +## Rollback Plan + +### If Backend Deployment Fails + +- [ ] Previous task definition revision identified +- [ ] Rollback command ready: + ```bash + aws ecs update-service \ + --cluster lockout-game-cluster \ + --service lockout-game-service \ + --task-definition lockout-game-task: + ``` + +### If Frontend Deployment Fails + +- [ ] Previous Amplify deployment identified +- [ ] Rollback steps documented: + 1. Go to Amplify Console + 2. Select previous successful deployment + 3. Click "Redeploy this version" + +--- + +## Maintenance Schedule + +### Daily + +- [ ] Check CloudWatch metrics for anomalies +- [ ] Review error logs + +### Weekly + +- [ ] Check for security updates +- [ ] Review application logs for issues + +### Monthly + +- [ ] Update dependencies (npm, pip) +- [ ] Test disaster recovery procedures + +### Quarterly + +- [ ] Rotate secrets in Secrets Manager +- [ ] Review IAM permissions +- [ ] Update SSL certificates if needed +- [ ] Load testing + +--- + +## Success Criteria + +Deployment is complete and successful when: + +βœ… Backend health check returns 200 OK +βœ… Frontend loads without errors +βœ… Users can create and join lobbies +βœ… Real-time updates work via WebSocket +βœ… No CORS errors in browser console +βœ… ECS service shows 2/2 running tasks +βœ… ALB target group shows 2 healthy targets +βœ… GitHub Actions workflows complete successfully +βœ… Custom domain resolves correctly (if configured) +βœ… SSL/TLS certificates are valid +βœ… CloudWatch logs show no critical errors +βœ… Application tested end-to-end successfully + +--- + +## Troubleshooting Reference + +If any step fails, refer to: + +- **Backend issues**: `docs/aws-backend-deployment.md` +- **Frontend issues**: `docs/aws-frontend-deployment.md` +- **Common problems**: `docs/deployment-troubleshooting.md` +- **Quick commands**: `docs/deployment-quick-reference.md` + +--- + +## Sign-Off + +Deployment completed by: **\*\*\*\***\_\_\_**\*\*\*\*** +Date: **\*\*\*\***\_\_\_**\*\*\*\*** +Backend URL: **\*\*\*\***\_\_\_**\*\*\*\*** +Frontend URL: **\*\*\*\***\_\_\_**\*\*\*\*** +Verified by: **\*\*\*\***\_\_\_**\*\*\*\*** + +πŸŽ‰ **Congratulations! Your Lockout Game is live on AWS!** πŸŽ‰ diff --git a/docs/deployment-quick-reference.md b/docs/deployment-quick-reference.md new file mode 100644 index 0000000..5a79115 --- /dev/null +++ b/docs/deployment-quick-reference.md @@ -0,0 +1,371 @@ +# AWS Deployment Quick Reference + +Quick commands and reference for deploying and managing the Lockout Game on AWS. + +## Environment + +- **Backend**: AWS ECS Fargate (Flask + Socket.IO) +- **Frontend**: AWS Amplify (React) +- **Region**: us-east-1 (adjust as needed) + +## Prerequisites Setup + +```bash +# Install AWS CLI +# macOS: brew install awscli +# Linux: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html + +# Configure AWS CLI +aws configure + +# Install Docker +# https://docs.docker.com/get-docker/ +``` + +## Backend (ECS Fargate) Commands + +### Build and Push Docker Image + +```bash +# Login to ECR +aws ecr get-login-password --region us-east-1 | \ + docker login --username AWS --password-stdin .dkr.ecr.us-east-1.amazonaws.com + +# Build image +docker build -t lockout-game-backend . + +# Tag image +docker tag lockout-game-backend:latest :latest +docker tag lockout-game-backend:latest :$(git rev-parse --short HEAD) + +# Push image +docker push :latest +docker push :$(git rev-parse --short HEAD) +``` + +### Deploy to ECS + +```bash +# Force new deployment (pulls latest image) +aws ecs update-service \ + --cluster lockout-game-cluster \ + --service lockout-game-service \ + --force-new-deployment + +# Check deployment status +aws ecs describe-services \ + --cluster lockout-game-cluster \ + --services lockout-game-service \ + --query 'services[0].{desired:desiredCount,running:runningCount,status:status}' +``` + +### View Logs + +```bash +# Tail logs in real-time +aws logs tail /ecs/lockout-game --follow + +# Search for errors in last hour +aws logs filter-log-events \ + --log-group-name /ecs/lockout-game \ + --filter-pattern "ERROR" \ + --start-time $(date -d '1 hour ago' +%s)000 +``` + +### Manage Secrets + +```bash +# View current secrets +aws secretsmanager get-secret-value \ + --secret-id lockout-game/production \ + --query 'SecretString' --output text | jq . + +# Update secrets +aws secretsmanager update-secret \ + --secret-id lockout-game/production \ + --secret-string '{ + "FLASK_ENV": "production", + "SECRET_KEY": "your-key", + "FRONTEND_URL": "https://yourdomain.com", + "ALLOWED_ORIGINS": "https://yourdomain.com,https://main.d123.amplifyapp.com" + }' + +# After updating secrets, restart service +aws ecs update-service \ + --cluster lockout-game-cluster \ + --service lockout-game-service \ + --force-new-deployment +``` + +### Scale Service + +```bash +# Scale up to 3 tasks +aws ecs update-service \ + --cluster lockout-game-cluster \ + --service lockout-game-service \ + --desired-count 3 + +# Scale down to 1 task +aws ecs update-service \ + --cluster lockout-game-cluster \ + --service lockout-game-service \ + --desired-count 1 +``` + +## Frontend (Amplify) Commands + +### Trigger Deployment + +```bash +# Start deployment +aws amplify start-job \ + --app-id \ + --branch-name main \ + --job-type RELEASE + +# Check deployment status +aws amplify list-jobs \ + --app-id \ + --branch-name main \ + --max-results 5 +``` + +### Manage Environment Variables + +```bash +# Get current environment variables +aws amplify get-app \ + --app-id \ + --query 'app.environmentVariables' + +# Update environment variables +aws amplify update-app \ + --app-id \ + --environment-variables \ + VITE_API_URL=https://api.yourdomain.com \ + VITE_SOCKET_URL=https://api.yourdomain.com + +# Redeploy after changing environment variables +aws amplify start-job \ + --app-id \ + --branch-name main \ + --job-type RELEASE +``` + +### View Amplify App Info + +```bash +# Get app details +aws amplify get-app --app-id + +# Get default domain +aws amplify get-app \ + --app-id \ + --query 'app.defaultDomain' --output text + +# List all branches +aws amplify list-branches --app-id +``` + +## Health Checks + +### Quick Health Check Script + +```bash +#!/bin/bash +# Save as check-health.sh + +BACKEND_URL="https://api.yourdomain.com" +FRONTEND_URL="https://yourdomain.com" + +echo "Checking backend health..." +curl -f $BACKEND_URL/health && echo "βœ… Backend healthy" || echo "❌ Backend unhealthy" + +echo "Checking frontend..." +curl -f -I $FRONTEND_URL && echo "βœ… Frontend accessible" || echo "❌ Frontend inaccessible" + +echo "Checking ECS service..." +aws ecs describe-services \ + --cluster lockout-game-cluster \ + --services lockout-game-service \ + --query 'services[0].{desired:desiredCount,running:runningCount}' \ + --output table + +echo "Checking target health..." +aws elbv2 describe-target-health \ + --target-group-arn \ + --query 'TargetHealthDescriptions[*].{Target:Target.Id,Health:TargetHealth.State}' \ + --output table +``` + +## Monitoring + +### CloudWatch Metrics + +```bash +# ECS CPU usage (last hour) +aws cloudwatch get-metric-statistics \ + --namespace AWS/ECS \ + --metric-name CPUUtilization \ + --dimensions Name=ServiceName,Value=lockout-game-service Name=ClusterName,Value=lockout-game-cluster \ + --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%S) \ + --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \ + --period 300 \ + --statistics Average,Maximum \ + --output table + +# ECS Memory usage +aws cloudwatch get-metric-statistics \ + --namespace AWS/ECS \ + --metric-name MemoryUtilization \ + --dimensions Name=ServiceName,Value=lockout-game-service Name=ClusterName,Value=lockout-game-cluster \ + --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%S) \ + --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \ + --period 300 \ + --statistics Average,Maximum \ + --output table + +# ALB request count +aws cloudwatch get-metric-statistics \ + --namespace AWS/ApplicationELB \ + --metric-name RequestCount \ + --dimensions Name=LoadBalancer,Value= \ + --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%S) \ + --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \ + --period 300 \ + --statistics Sum \ + --output table +``` + +## Rollback + +### Backend Rollback + +```bash +# List task definition revisions +aws ecs list-task-definitions --family-prefix lockout-game-task + +# Rollback to previous revision +aws ecs update-service \ + --cluster lockout-game-cluster \ + --service lockout-game-service \ + --task-definition lockout-game-task: +``` + +### Frontend Rollback + +Use Amplify Console: + +1. Go to https://console.aws.amazon.com/amplify/ +2. Select app β†’ branch +3. Click on previous successful deployment +4. Click "Redeploy this version" + +## Common Issues + +### Backend not responding + +```bash +# Check if tasks are running +aws ecs list-tasks --cluster lockout-game-cluster --service lockout-game-service + +# Get task details +TASK_ARN=$(aws ecs list-tasks --cluster lockout-game-cluster --service lockout-game-service --query 'taskArns[0]' --output text) +aws ecs describe-tasks --cluster lockout-game-cluster --tasks $TASK_ARN + +# Check logs +aws logs tail /ecs/lockout-game --follow +``` + +### CORS errors + +```bash +# Update ALLOWED_ORIGINS +aws secretsmanager update-secret \ + --secret-id lockout-game/production \ + --secret-string '{"ALLOWED_ORIGINS":"https://yourdomain.com,https://main.d123.amplifyapp.com",...}' + +# Restart service +aws ecs update-service --cluster lockout-game-cluster --service lockout-game-service --force-new-deployment +``` + +### Frontend build fails + +```bash +# Check build logs +aws amplify get-job \ + --app-id \ + --branch-name main \ + --job-id + +# Retry deployment +aws amplify start-job \ + --app-id \ + --branch-name main \ + --job-type RELEASE +``` + +## Useful ARN Patterns + +```bash +# Replace and with your values + +# ECR Repository +arn:aws:ecr:::repository/lockout-game-backend + +# ECS Cluster +arn:aws:ecs:::cluster/lockout-game-cluster + +# ECS Service +arn:aws:ecs:::service/lockout-game-cluster/lockout-game-service + +# Secrets Manager +arn:aws:secretsmanager:::secret:lockout-game/production + +# CloudWatch Log Group +/ecs/lockout-game +``` + +## GitHub Actions Variables + +Set these in GitHub repository settings β†’ Secrets and variables β†’ Actions: + +### Secrets + +- `AWS_ACCESS_KEY_ID` +- `AWS_SECRET_ACCESS_KEY` +- `AMPLIFY_APP_ID` + +### Variables (optional) + +- `AWS_REGION` (default: us-east-1) +- `AMPLIFY_BRANCH_NAME` (default: main) + +## Local Development + +```bash +# Run backend in Docker +docker-compose up --build + +# Run frontend dev server +cd frontend +npm run dev + +# Configure frontend to use local backend +# Create frontend/.env.local: +# VITE_API_URL=http://localhost:5000 +# VITE_SOCKET_URL=http://localhost:5000 +``` + +## Documentation + +- [Backend Deployment Guide](./aws-backend-deployment.md) +- [Frontend Deployment Guide](./aws-frontend-deployment.md) +- [Troubleshooting Guide](./deployment-troubleshooting.md) + +## Support + +- AWS Documentation: https://docs.aws.amazon.com/ +- AWS Support: https://console.aws.amazon.com/support/ +- Project Issues: https://github.com/your-repo/lockout-game/issues diff --git a/docs/deployment-troubleshooting.md b/docs/deployment-troubleshooting.md new file mode 100644 index 0000000..7fe2652 --- /dev/null +++ b/docs/deployment-troubleshooting.md @@ -0,0 +1,601 @@ +# Deployment Troubleshooting Guide + +This guide covers common issues and solutions for the Lockout Game deployment on AWS (ECS Fargate + Amplify). + +## Table of Contents + +- [Backend Issues (ECS Fargate)](#backend-issues-ecs-fargate) +- [Frontend Issues (Amplify)](#frontend-issues-amplify) +- [CORS and Connectivity Issues](#cors-and-connectivity-issues) +- [WebSocket/Socket.IO Issues](#websocketsocketio-issues) +- [Performance Issues](#performance-issues) +- [Monitoring and Debugging](#monitoring-and-debugging) + +--- + +## Backend Issues (ECS Fargate) + +### ECS Task Keeps Restarting + +**Symptoms**: Tasks start and immediately stop, service never reaches desired count + +**Possible Causes**: + +1. **Health check failures** + + ```bash + # Check service events + aws ecs describe-services --cluster lockout-game-cluster --services lockout-game-service \ + --query 'services[0].events[0:10]' + + # Check task logs + aws logs tail /ecs/lockout-game --follow + ``` + + **Solution**: Verify `/health` endpoint is accessible: + + ```bash + # Test health endpoint + curl https://api.yourdomain.com/health + ``` + +2. **Port mismatch** + + - Task definition has port 5000 + - Container must EXPOSE 5000 + - Target group must target port 5000 + + **Solution**: Verify all ports match in task definition and target group + +3. **Environment variable issues** + + ```bash + # Check secrets are accessible + aws secretsmanager get-secret-value --secret-id lockout-game/production + ``` + + **Solution**: Ensure execution role has `secretsmanager:GetSecretValue` permission + +4. **Docker image issues** + + ```bash + # Test image locally + docker run -p 5000:5000 \ + -e FLASK_ENV=production \ + -e SECRET_KEY=test \ + -e FRONTEND_URL=http://localhost \ + -e ALLOWED_ORIGINS=http://localhost \ + :latest + + # Then test health endpoint + curl http://localhost:5000/health + ``` + +### Cannot Pull Docker Image from ECR + +**Symptoms**: Task fails with "CannotPullContainerError" + +**Solution**: + +```bash +# Check ECR repository exists +aws ecr describe-repositories --repository-names lockout-game-backend + +# Verify image exists +aws ecr list-images --repository-name lockout-game-backend + +# Check execution role has ECR permissions +aws iam get-role-policy --role-name lockout-ecs-execution-role --policy-name ECRAccess +``` + +Ensure execution role has these permissions: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ecr:GetAuthorizationToken", + "ecr:BatchCheckLayerAvailability", + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage" + ], + "Resource": "*" + } + ] +} +``` + +### High CPU/Memory Usage + +**Check resource utilization**: + +```bash +# View CloudWatch metrics +aws cloudwatch get-metric-statistics \ + --namespace AWS/ECS \ + --metric-name CPUUtilization \ + --dimensions Name=ServiceName,Value=lockout-game-service Name=ClusterName,Value=lockout-game-cluster \ + --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%S) \ + --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \ + --period 300 \ + --statistics Average +``` + +**Solutions**: + +1. **Increase task size**: Update task definition to use 1024 CPU / 2048 MB +2. **Scale horizontally**: Increase desired count +3. **Optimize code**: Profile application, check for memory leaks + +### Load Balancer Returns 502/503/504 + +**502 Bad Gateway**: Backend is not responding + +```bash +# Check target health +aws elbv2 describe-target-health --target-group-arn +``` + +**Common causes**: + +- Health check path incorrect (should be `/health`) +- Backend not listening on correct port +- Security group blocking traffic from ALB to ECS tasks + +**503 Service Unavailable**: No healthy targets + +**Solution**: Check why tasks are unhealthy (see "ECS Task Keeps Restarting" above) + +**504 Gateway Timeout**: Request taking too long + +**Solution**: + +- Check backend logs for slow queries/operations +- Increase timeout in target group settings +- Check network connectivity + +--- + +## Frontend Issues (Amplify) + +### Build Failures + +**Problem**: Amplify build fails + +**Check build logs**: + +1. Go to Amplify Console +2. Select your app β†’ branch +3. Click on failed deployment +4. View **Provision**, **Build**, **Deploy** logs + +**Common issues**: + +1. **Node version mismatch** + + Add to `frontend/amplify.yml`: + + ```yaml + frontend: + phases: + preBuild: + commands: + - nvm use 20 + - npm ci + ``` + +2. **Missing environment variables** + + Verify in Amplify Console β†’ Environment variables: + + - `VITE_API_URL` + - `VITE_SOCKET_URL` + +3. **Build command fails** + + Test locally: + + ```bash + cd frontend + npm ci + npm run build + ``` + +4. **Wrong artifacts directory** + + Ensure `amplify.yml` has: + + ```yaml + artifacts: + baseDirectory: dist # Vite outputs to dist/ + ``` + +### Frontend Loads but API Calls Fail + +**Check browser console**: + +1. Open browser DevTools (F12) +2. Look for network errors +3. Check API request URLs + +**Common issues**: + +1. **Wrong API URL** + + Check `VITE_API_URL` environment variable in Amplify + + ```bash + aws amplify get-app --app-id + ``` + +2. **CORS errors** (see [CORS section](#cors-and-connectivity-issues) below) + +3. **Backend is down** + + Test backend health: + + ```bash + curl https://api.yourdomain.com/health + ``` + +### Custom Domain Not Working + +**Problem**: Custom domain shows "Not found" or doesn't resolve + +**Solutions**: + +1. **Wait for propagation**: DNS changes take 5-30 minutes + +2. **Check domain association status**: + + ```bash + aws amplify get-domain-association --app-id --domain-name yourdomain.com + ``` + +3. **Verify Route53 records**: + + ```bash + aws route53 list-resource-record-sets --hosted-zone-id + ``` + + Should have CNAME records pointing to Amplify + +4. **Check certificate status**: + + - Go to AWS Console β†’ Certificate Manager + - Verify certificate is "Issued" (not "Pending validation") + +5. **Clear DNS cache**: + + ```bash + # macOS + sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder + + # Linux + sudo systemd-resolve --flush-caches + + # Windows + ipconfig /flushdns + ``` + +--- + +## CORS and Connectivity Issues + +### Browser Shows CORS Errors + +**Symptoms**: Console shows: + +``` +Access to XMLHttpRequest at 'https://api.yourdomain.com/api/lobby' from origin 'https://lockout.yourdomain.com' has been blocked by CORS policy +``` + +**Diagnosis**: + +1. **Check backend CORS configuration**: + + ```bash + # View current secrets + aws secretsmanager get-secret-value --secret-id lockout-game/production + ``` + +2. **Test with curl**: + + ```bash + curl -H "Origin: https://lockout.yourdomain.com" \ + -H "Access-Control-Request-Method: POST" \ + -H "Access-Control-Request-Headers: Content-Type" \ + -X OPTIONS \ + https://api.yourdomain.com/api/lobby -v + ``` + + Should return: + + ``` + Access-Control-Allow-Origin: https://lockout.yourdomain.com + Access-Control-Allow-Methods: POST, GET, OPTIONS + ``` + +**Solutions**: + +1. **Update ALLOWED_ORIGINS** in Secrets Manager: + + ```json + { + "ALLOWED_ORIGINS": "https://lockout.yourdomain.com,https://main.d123456.amplifyapp.com" + } + ``` + +2. **Restart ECS service** to pick up new configuration: + + ```bash + aws ecs update-service \ + --cluster lockout-game-cluster \ + --service lockout-game-service \ + --force-new-deployment + ``` + +3. **Wait 2-3 minutes** for new tasks to start + +4. **Verify in browser**: Hard refresh (Ctrl+Shift+R / Cmd+Shift+R) + +### Mixed Content Warnings + +**Problem**: Frontend is HTTPS, but trying to call HTTP backend + +**Solution**: Ensure `VITE_API_URL` uses `https://` not `http://` + +--- + +## WebSocket/Socket.IO Issues + +### Socket.IO Connection Fails + +**Symptoms**: + +- Console shows: `WebSocket connection failed` +- Lobby doesn't update in real-time +- Players can't see each other + +**Diagnosis**: + +1. **Check browser console** for Socket.IO errors + +2. **Verify WebSocket URL**: + + ```javascript + // In browser console + console.log(import.meta.env.VITE_SOCKET_URL); + ``` + +3. **Test WebSocket connection**: + ```bash + # Using wscat (install: npm install -g wscat) + wscat -c wss://api.yourdomain.com/socket.io/\?EIO\=4\&transport\=websocket + ``` + +**Common issues**: + +1. **ALB doesn't support WebSocket upgrade** + + **Solution**: Verify ALB target group has: + + - Protocol: HTTP + - Target type: IP + - Stickiness: Enabled (important for Socket.IO) + + Enable stickiness: + + ```bash + aws elbv2 modify-target-group-attributes \ + --target-group-arn \ + --attributes Key=stickiness.enabled,Value=true Key=stickiness.type,Value=lb_cookie + ``` + +2. **Backend not configured for WebSocket** + + Verify `backend/app.py` uses eventlet: + + ```python + socketio = SocketIO(app, cors_allowed_origins=app.config.get('ALLOWED_ORIGINS', '*'), async_mode='eventlet') + ``` + + And Dockerfile runs with eventlet worker: + + ```dockerfile + CMD ["gunicorn", "--worker-class", "eventlet", ...] + ``` + +3. **CORS for Socket.IO** + + Socket.IO needs same CORS origins as REST API. Verify `ALLOWED_ORIGINS` is set correctly. + +### Socket.IO Works Intermittently + +**Problem**: Sometimes works, sometimes doesn't + +**Cause**: Multiple backend tasks, but no sticky sessions + +**Solution**: Enable ALB stickiness (see above) + +--- + +## Performance Issues + +### Slow Page Load + +**Frontend**: + +1. **Check CloudFront caching**: + + ```bash + # View CloudFront distributions + aws cloudfront list-distributions --query 'DistributionList.Items[*].[Id,DomainName,Status]' + ``` + +2. **Analyze bundle size**: + + ```bash + cd frontend + npm run build -- --mode production + # Check dist/ folder size + ``` + +3. **Enable gzip**: Already enabled by CloudFront + +**Backend**: + +1. **Check ECS task performance**: + + - View CPU/Memory metrics in CloudWatch + - Increase task size if needed + +2. **Check database queries**: Add logging to identify slow operations + +3. **Enable backend caching**: Consider Redis for frequently accessed data + +--- + +## Monitoring and Debugging + +### View Backend Logs + +**Real-time logs**: + +```bash +aws logs tail /ecs/lockout-game --follow +``` + +**Query logs**: + +```bash +aws logs filter-log-events \ + --log-group-name /ecs/lockout-game \ + --filter-pattern "ERROR" \ + --start-time $(date -d '1 hour ago' +%s)000 +``` + +### View Frontend Logs + +**Build logs**: Amplify Console β†’ App β†’ Branch β†’ Build logs + +**Runtime logs**: Use browser console (F12) + +### SSH into Running ECS Task + +Enable ECS Exec: + +```bash +# Update service to enable exec +aws ecs update-service \ + --cluster lockout-game-cluster \ + --service lockout-game-service \ + --enable-execute-command + +# Find running task +TASK_ARN=$(aws ecs list-tasks --cluster lockout-game-cluster --service lockout-game-service --query 'taskArns[0]' --output text) + +# Connect to task +aws ecs execute-command \ + --cluster lockout-game-cluster \ + --task $TASK_ARN \ + --container lockout-game-backend \ + --interactive \ + --command "/bin/bash" +``` + +### Set Up CloudWatch Alarms + +**ECS Service Alarms**: + +```bash +# High CPU alarm +aws cloudwatch put-metric-alarm \ + --alarm-name lockout-ecs-high-cpu \ + --alarm-description "ECS CPU > 80%" \ + --metric-name CPUUtilization \ + --namespace AWS/ECS \ + --statistic Average \ + --period 300 \ + --evaluation-periods 2 \ + --threshold 80 \ + --comparison-operator GreaterThanThreshold \ + --dimensions Name=ServiceName,Value=lockout-game-service Name=ClusterName,Value=lockout-game-cluster + +# Unhealthy target alarm +aws cloudwatch put-metric-alarm \ + --alarm-name lockout-alb-unhealthy-targets \ + --metric-name UnHealthyHostCount \ + --namespace AWS/ApplicationELB \ + --statistic Average \ + --period 60 \ + --evaluation-periods 2 \ + --threshold 1 \ + --comparison-operator GreaterThanOrEqualToThreshold \ + --dimensions Name=TargetGroup,Value= Name=LoadBalancer,Value= +``` + +### Rollback Deployments + +**Backend (ECS)**: + +```bash +# List task definition revisions +aws ecs list-task-definitions --family-prefix lockout-game-task + +# Update service to previous revision +aws ecs update-service \ + --cluster lockout-game-cluster \ + --service lockout-game-service \ + --task-definition lockout-game-task:5 # previous revision +``` + +**Frontend (Amplify)**: + +In Amplify Console: + +1. Go to app β†’ branch +2. Click on previous successful deployment +3. Click "Redeploy this version" + +### Health Check Checklist + +Run these checks to verify full system health: + +```bash +# 1. Backend health +curl https://api.yourdomain.com/health + +# 2. Frontend loads +curl -I https://lockout.yourdomain.com + +# 3. ECS service running +aws ecs describe-services --cluster lockout-game-cluster --services lockout-game-service \ + --query 'services[0].{desired:desiredCount,running:runningCount,status:status}' + +# 4. Target health +aws elbv2 describe-target-health --target-group-arn + +# 5. Amplify deployment status +aws amplify get-app --app-id --query 'app.{name:name,status:defaultDomain}' +``` + +All checks should return healthy/successful status. + +--- + +## Getting Help + +If you're still stuck: + +1. **Check AWS Service Health**: https://status.aws.amazon.com/ +2. **Review AWS Documentation**: + - [ECS Troubleshooting](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/troubleshooting.html) + - [Amplify Troubleshooting](https://docs.aws.amazon.com/amplify/latest/userguide/troubleshooting.html) +3. **AWS Support**: Open a support ticket in AWS Console +4. **Community Forums**: + - AWS re:Post: https://repost.aws/ + - Stack Overflow with AWS tags diff --git a/env.template b/env.template new file mode 100644 index 0000000..ab1257d --- /dev/null +++ b/env.template @@ -0,0 +1,21 @@ +# Environment Configuration for Lockout Game +# Copy this file to .env and fill in your values + +# Application Environment +FLASK_ENV=development + +# Security +# IMPORTANT: Generate a secure random key for production +# Example: python -c "import secrets; print(secrets.token_hex(32))" +SECRET_KEY=your-secret-key-here + +# Frontend URL (used for CORS) +FRONTEND_URL=http://localhost:5173 + +# Server Configuration +PORT=5000 +HOST=0.0.0.0 + +# Production-specific (uncomment for production) +# ALLOWED_ORIGINS=https://yourdomain.com,https://www.yourdomain.com + diff --git a/frontend/amplify.yml b/frontend/amplify.yml new file mode 100644 index 0000000..ca8dac9 --- /dev/null +++ b/frontend/amplify.yml @@ -0,0 +1,16 @@ +version: 1 +frontend: + phases: + preBuild: + commands: + - npm ci + build: + commands: + - npm run build + artifacts: + baseDirectory: dist + files: + - '**/*' + cache: + paths: + - node_modules/**/* diff --git a/frontend/env.production.template b/frontend/env.production.template new file mode 100644 index 0000000..92145a7 --- /dev/null +++ b/frontend/env.production.template @@ -0,0 +1,12 @@ +# Production Environment Variables for Frontend +# These should be configured in AWS Amplify Console +# Environment variables -> Manage variables + +# Backend API URL (REST endpoints) +# Point to your ALB domain or custom domain for backend +VITE_API_URL=https://api.yourdomain.com + +# Backend WebSocket URL (Socket.IO) +# Same as API URL unless using separate WebSocket endpoint +VITE_SOCKET_URL=https://api.yourdomain.com + diff --git a/frontend/env.template b/frontend/env.template new file mode 100644 index 0000000..854d873 --- /dev/null +++ b/frontend/env.template @@ -0,0 +1,9 @@ +# Frontend Environment Variables Template +# Copy to .env.local for local development + +# Backend API URL (REST endpoints) +VITE_API_URL=http://localhost:5000 + +# Backend WebSocket URL (Socket.IO) +VITE_SOCKET_URL=http://localhost:5000 + diff --git a/frontend/src/components/__tests__/Lobby.test.jsx b/frontend/src/components/__tests__/Lobby.test.jsx index 4ed748b..dc3030c 100644 --- a/frontend/src/components/__tests__/Lobby.test.jsx +++ b/frontend/src/components/__tests__/Lobby.test.jsx @@ -5,6 +5,15 @@ import { screen } from '@testing-library/react'; import Lobby from '../Lobby'; import { renderWithRouter } from '../../test/testUtils'; +// Mock localStorage +const localStorageMock = { + getItem: vi.fn(), + setItem: vi.fn(), + removeItem: vi.fn(), + clear: vi.fn(), +}; +global.localStorage = localStorageMock; + vi.mock('../LobbyContent', () => ({ default: () => (
Lobby Content Rendered
@@ -14,6 +23,7 @@ vi.mock('../LobbyContent', () => ({ describe('Lobby', () => { beforeEach(() => { vi.clearAllMocks(); + localStorageMock.getItem.mockReturnValue(null); }); it('renders LobbyContent inside LobbyProvider when initialUser is provided', () => { diff --git a/frontend/src/config.js b/frontend/src/config.js new file mode 100644 index 0000000..45847b8 --- /dev/null +++ b/frontend/src/config.js @@ -0,0 +1,16 @@ +// Environment-based configuration for Lockout Game frontend + +const config = { + development: { + apiUrl: 'http://localhost:5000', + }, + production: { + // This will be replaced by environment variable during build + apiUrl: import.meta.env.VITE_API_URL || 'https://api.yourdomain.com', + }, +}; + +// Determine environment +const environment = import.meta.env.MODE || 'development'; + +export default config[environment]; diff --git a/frontend/src/context/__tests__/LobbyProvider.game.test.jsx b/frontend/src/context/__tests__/LobbyProvider.game.test.jsx index d0c39b9..2d937c6 100644 --- a/frontend/src/context/__tests__/LobbyProvider.game.test.jsx +++ b/frontend/src/context/__tests__/LobbyProvider.game.test.jsx @@ -6,6 +6,15 @@ import { LobbyProvider } from '../LobbyProvider'; import { LobbyContext } from '../LobbyContext'; import { SOCKET_EVENTS } from '../../constants'; +// Mock localStorage +const localStorageMock = { + getItem: vi.fn(), + setItem: vi.fn(), + removeItem: vi.fn(), + clear: vi.fn(), +}; +global.localStorage = localStorageMock; + // Create mock socket const mockSocketOn = vi.fn(); const mockSocketOff = vi.fn(); @@ -36,6 +45,7 @@ describe('LobbyProvider Game Events', () => { beforeEach(() => { // Reset mocks vi.clearAllMocks(); + localStorageMock.getItem.mockReturnValue(null); // Update on method to store event handlers mockSocketOn.mockImplementation((event, handler) => {