Back to Tutorial Overview

AWS Cloud Deployment - Complete Guide

Deploy full-stack applications to AWS with EC2, S3, RDS, and CI/CD pipelines

Section 1: AWS Account & CLI Setup

Install and Configure AWS CLI

# Install AWS CLI (Windows)
msiexec.exe /i https://awscli.amazonaws.com/AWSCLIV2.msi

# Install AWS CLI (Mac)
brew install awscli

# Install AWS CLI (Linux)
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

# Verify installation
aws --version

# Configure AWS credentials
aws configure
# AWS Access Key ID: YOUR_ACCESS_KEY
# AWS Secret Access Key: YOUR_SECRET_KEY
# Default region: us-east-1
# Default output format: json

# Test configuration
aws s3 ls

Section 2: Deploy Backend to EC2

Launch and Configure EC2 Instance

# Launch EC2 instance (AWS Console or CLI)
aws ec2 run-instances \
  --image-id ami-0c55b159cbfafe1f0 \
  --instance-type t2.micro \
  --key-name my-key-pair \
  --security-group-ids sg-xxxxxx \
  --subnet-id subnet-xxxxxx

# Connect to instance
ssh -i "my-key-pair.pem" ubuntu@ec2-xx-xx-xx-xx.compute.amazonaws.com

# Update system
sudo apt update && sudo apt upgrade -y

# Install Node.js
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs

# Install PM2 for process management
sudo npm install -g pm2

# Clone your repository
git clone https://github.com/yourusername/your-api.git
cd your-api

# Install dependencies
npm install

# Create .env file
nano .env
# Add your environment variables

# Start application with PM2
pm2 start npm --name "api" -- start
pm2 startup
pm2 save

# Check status
pm2 status
pm2 logs api

Configure Nginx as Reverse Proxy

# Install Nginx
sudo apt install nginx -y

# Create Nginx configuration
sudo nano /etc/nginx/sites-available/api

# Add configuration:
server {
    listen 80;
    server_name api.yourdomain.com;

    location / {
        proxy_pass http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

# Enable site
sudo ln -s /etc/nginx/sites-available/api /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx

# Install SSL with Let's Encrypt
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d api.yourdomain.com

Section 3: S3 for Static Files & Frontend

Deploy React/Next.js to S3

# Create S3 bucket
aws s3 mb s3://my-app-frontend

# Enable static website hosting
aws s3 website s3://my-app-frontend \
  --index-document index.html \
  --error-document index.html

# Set bucket policy for public access
cat > policy.json <<EOF
{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "PublicReadGetObject",
    "Effect": "Allow",
    "Principal": "*",
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::my-app-frontend/*"
  }]
}
EOF

aws s3api put-bucket-policy \
  --bucket my-app-frontend \
  --policy file://policy.json

# Build and upload
npm run build
aws s3 sync build/ s3://my-app-frontend --delete

# Get website URL
aws s3api get-bucket-website \
  --bucket my-app-frontend

CloudFront CDN Setup

# Create CloudFront distribution
aws cloudfront create-distribution \
  --origin-domain-name my-app-frontend.s3-website-us-east-1.amazonaws.com \
  --default-root-object index.html

# Invalidate cache after updates
aws cloudfront create-invalidation \
  --distribution-id DISTRIBUTION_ID \
  --paths "/*"

Section 4: RDS Database Setup

Create PostgreSQL RDS Instance

# Create RDS instance
aws rds create-db-instance \
  --db-instance-identifier myapp-db \
  --db-instance-class db.t3.micro \
  --engine postgres \
  --master-username admin \
  --master-user-password YourPassword123 \
  --allocated-storage 20 \
  --vpc-security-group-ids sg-xxxxxx \
  --db-subnet-group-name my-subnet-group \
  --backup-retention-period 7 \
  --preferred-backup-window "03:00-04:00"

# Get endpoint
aws rds describe-db-instances \
  --db-instance-identifier myapp-db \
  --query 'DBInstances[0].Endpoint.Address'

# Connect from EC2
psql -h myapp-db.xxxxxx.us-east-1.rds.amazonaws.com \
     -U admin \
     -d postgres

Section 5: CI/CD Pipeline with GitHub Actions

GitHub Actions Workflow

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

on:
  push:
    branches: [ main ]

jobs:
  deploy-backend:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run tests
        run: npm test
      
      - name: Deploy to EC2
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ubuntu
          key: ${{ secrets.EC2_SSH_KEY }}
          script: |
            cd /home/ubuntu/my-api
            git pull origin main
            npm install
            pm2 restart api

  deploy-frontend:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build
        run: npm run build
        env:
          REACT_APP_API_URL: ${{ secrets.API_URL }}
      
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1
      
      - name: Deploy to S3
        run: aws s3 sync build/ s3://my-app-frontend --delete
      
      - name: Invalidate CloudFront
        run: |
          aws cloudfront create-invalidation \
            --distribution-id ${{ secrets.CLOUDFRONT_ID }} \
            --paths "/*"

Section 6: Serverless with AWS Lambda

Create Lambda Function

// index.js - Lambda function
export const handler = async (event) => {
    try {
        const body = JSON.parse(event.body || '{}');
        
        // Your business logic here
        const result = {
            message: 'Success',
            data: body,
            timestamp: new Date().toISOString()
        };
        
        return {
            statusCode: 200,
            headers: {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            },
            body: JSON.stringify(result)
        };
    } catch (error) {
        return {
            statusCode: 500,
            body: JSON.stringify({
                error: error.message
            })
        };
    }
};

Deploy Lambda with AWS CLI

# Package function
zip -r function.zip index.js node_modules

# Create IAM role for Lambda
aws iam create-role \
  --role-name lambda-execution-role \
  --assume-role-policy-document file://trust-policy.json

# Attach policy
aws iam attach-role-policy \
  --role-name lambda-execution-role \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

# Create function
aws lambda create-function \
  --function-name myFunction \
  --runtime nodejs18.x \
  --role arn:aws:iam::ACCOUNT_ID:role/lambda-execution-role \
  --handler index.handler \
  --zip-file fileb://function.zip

# Invoke function
aws lambda invoke \
  --function-name myFunction \
  --payload '{"key": "value"}' \
  response.json

cat response.json

Section 7: API Gateway & Domain Setup

Create REST API with API Gateway

# Create REST API
aws apigateway create-rest-api \
  --name "MyAPI" \
  --description "My REST API"

# Get API ID
API_ID=$(aws apigateway get-rest-apis \
  --query 'items[?name==`MyAPI`].id' \
  --output text)

# Get root resource ID
ROOT_ID=$(aws apigateway get-resources \
  --rest-api-id $API_ID \
  --query 'items[?path==`/`].id' \
  --output text)

# Create resource
RESOURCE_ID=$(aws apigateway create-resource \
  --rest-api-id $API_ID \
  --parent-id $ROOT_ID \
  --path-part users \
  --query 'id' \
  --output text)

# Create GET method
aws apigateway put-method \
  --rest-api-id $API_ID \
  --resource-id $RESOURCE_ID \
  --http-method GET \
  --authorization-type NONE

# Integrate with Lambda
aws apigateway put-integration \
  --rest-api-id $API_ID \
  --resource-id $RESOURCE_ID \
  --http-method GET \
  --type AWS_PROXY \
  --integration-http-method POST \
  --uri arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:ACCOUNT_ID:function:myFunction/invocations

# Deploy API
aws apigateway create-deployment \
  --rest-api-id $API_ID \
  --stage-name prod

Section 8: Monitoring & Logging

CloudWatch Logs and Metrics

# View Lambda logs
aws logs tail /aws/lambda/myFunction --follow

# Create CloudWatch alarm for errors
aws cloudwatch put-metric-alarm \
  --alarm-name lambda-errors \
  --alarm-description "Alert on Lambda errors" \
  --metric-name Errors \
  --namespace AWS/Lambda \
  --statistic Sum \
  --period 300 \
  --threshold 5 \
  --comparison-operator GreaterThanThreshold \
  --evaluation-periods 1

# View EC2 metrics
aws cloudwatch get-metric-statistics \
  --namespace AWS/EC2 \
  --metric-name CPUUtilization \
  --dimensions Name=InstanceId,Value=i-xxxxxx \
  --start-time 2024-01-01T00:00:00Z \
  --end-time 2024-01-01T23:59:59Z \
  --period 3600 \
  --statistics Average

Section 9: Production Best Practices

1. Use Environment Variables

Store secrets in AWS Systems Manager Parameter Store or Secrets Manager

2. Enable Auto Scaling

Configure Auto Scaling Groups for EC2 instances to handle traffic spikes

3. Implement Health Checks

Add health check endpoints and configure Load Balancer health checks

4. Set Up Backups

Enable automated backups for RDS and regular snapshots for EC2

5. Monitor Costs

Set up AWS Budgets and CloudWatch billing alarms

You're Now an AWS Expert! ☁️

You've learned EC2, S3, RDS, Lambda, CloudFront, API Gateway, and CI/CD pipelines for production deployments!

Back to Tutorial Overview