From 420d894744ba61edfbe3a1c8325a82c20e3e7ab4 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 13 May 2026 07:03:15 +0000 Subject: [PATCH 1/3] Add tier AMI config and EC2 user-data scripts for web, app, and db tiers - tier-ami-config.json: AMI IDs for Amazon Linux 2 across 13 regions (web + app tiers), plus Aurora MySQL config reference for db tier - web-tier-userdata.sh: bootstraps Nginx + Node 16 + React build on web EC2 - app-tier-userdata.sh: bootstraps Node 16 + PM2 + writes DbConfig on app EC2 - find-ami.sh: resolves latest Amazon Linux 2 AMI for any region via SSM https://claude.ai/code/session_01JeU1JS9MBg7zouezB1o4kr --- infrastructure/app-tier-userdata.sh | 48 ++++++++++++++++ infrastructure/find-ami.sh | 18 ++++++ infrastructure/tier-ami-config.json | 85 +++++++++++++++++++++++++++++ infrastructure/web-tier-userdata.sh | 77 ++++++++++++++++++++++++++ 4 files changed, 228 insertions(+) create mode 100644 infrastructure/app-tier-userdata.sh create mode 100644 infrastructure/find-ami.sh create mode 100644 infrastructure/tier-ami-config.json create mode 100644 infrastructure/web-tier-userdata.sh diff --git a/infrastructure/app-tier-userdata.sh b/infrastructure/app-tier-userdata.sh new file mode 100644 index 000000000..33063b3be --- /dev/null +++ b/infrastructure/app-tier-userdata.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# App Tier EC2 user-data — Amazon Linux 2 +# Installs Node 16 + PM2, deploys Node.js API, writes DbConfig. +# Run as root at first boot. + +set -euxo pipefail + +# ── Variables (set before launch or via SSM Parameter Store) ────────────────── +S3_BUCKET="${S3_BUCKET:-YOUR-S3-BUCKET-NAME}" +DB_HOST="${DB_HOST:-REPLACE-WITH-AURORA-WRITER-ENDPOINT}" +DB_USER="${DB_USER:-admin}" +DB_PWD="${DB_PWD:-REPLACE-WITH-DB-PASSWORD}" +DB_DATABASE="${DB_DATABASE:-webappdb}" + +# ── System update ───────────────────────────────────────────────────────────── +yum update -y + +# ── Node.js 16 ──────────────────────────────────────────────────────────────── +curl -fsSL https://rpm.nodesource.com/setup_16.x | bash - +yum install -y nodejs + +# ── PM2 (process manager) ───────────────────────────────────────────────────── +npm install -g pm2 +pm2 startup systemd -u ec2-user --hp /home/ec2-user +systemctl enable pm2-ec2-user + +# ── Deploy app from S3 ──────────────────────────────────────────────────────── +mkdir -p /home/ec2-user/app-tier +aws s3 cp s3://${S3_BUCKET}/app-tier /home/ec2-user/app-tier --recursive + +# ── Write database config ───────────────────────────────────────────────────── +cat > /home/ec2-user/app-tier/DbConfig.js < --query 'Parameter.Value' --output text" + ], + "tiers": { + "web_tier": { + "purpose": "Public-facing Nginx reverse proxy serving React SPA + forwarding /api/* to internal LB", + "instance_type": "t2.micro", + "os": "Amazon Linux 2", + "ami_ssm_parameter": "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2", + "known_amis_by_region": { + "us-east-1": "ami-0c02fb55956c7d316", + "us-east-2": "ami-0b0af3577fe5e3532", + "us-west-1": "ami-0d9858aa3c6322f73", + "us-west-2": "ami-098e42ae54c764c35", + "eu-west-1": "ami-0d71ea30463e0ff49", + "eu-west-2": "ami-0bd2099338bc55e6d", + "eu-central-1": "ami-0a1ee2fb28fe05df3", + "ap-southeast-1": "ami-0c802847a501da816", + "ap-southeast-2": "ami-07620139298af599e", + "ap-northeast-1": "ami-0218d08a1f9dac831", + "ap-south-1": "ami-0cca134ec43cf708f", + "ca-central-1": "ami-00f881f027a6d74a0", + "sa-east-1": "ami-05aa753c043f1dcd3" + }, + "userdata_script": "infrastructure/web-tier-userdata.sh", + "ports": { + "inbound": [80, 443], + "health_check": "/health" + }, + "software": ["nginx", "nodejs-16", "npm"], + "app_path": "/home/ec2-user/web-tier" + }, + "app_tier": { + "purpose": "Private Node.js API server behind internal load balancer, connects to Aurora", + "instance_type": "t2.micro", + "os": "Amazon Linux 2", + "ami_ssm_parameter": "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2", + "known_amis_by_region": { + "us-east-1": "ami-0c02fb55956c7d316", + "us-east-2": "ami-0b0af3577fe5e3532", + "us-west-1": "ami-0d9858aa3c6322f73", + "us-west-2": "ami-098e42ae54c764c35", + "eu-west-1": "ami-0d71ea30463e0ff49", + "eu-west-2": "ami-0bd2099338bc55e6d", + "eu-central-1": "ami-0a1ee2fb28fe05df3", + "ap-southeast-1": "ami-0c802847a501da816", + "ap-southeast-2": "ami-07620139298af599e", + "ap-northeast-1": "ami-0218d08a1f9dac831", + "ap-south-1": "ami-0cca134ec43cf708f", + "ca-central-1": "ami-00f881f027a6d74a0", + "sa-east-1": "ami-05aa753c043f1dcd3" + }, + "userdata_script": "infrastructure/app-tier-userdata.sh", + "ports": { + "inbound": [4000], + "health_check": "/health" + }, + "software": ["nodejs-16", "npm", "pm2"], + "app_path": "/home/ec2-user/app-tier" + }, + "database_tier": { + "purpose": "Amazon Aurora MySQL — fully managed, no EC2 AMI required", + "type": "Amazon Aurora MySQL", + "engine": "aurora-mysql", + "engine_version": "8.0", + "instance_class": "db.t3.micro", + "multi_az": true, + "port": 3306, + "note": "Aurora is provisioned via RDS console or CloudFormation. Use the cluster writer endpoint as DB_HOST in DbConfig.js.", + "dbconfig_file": "application-code/app-tier/DbConfig.js", + "required_parameters": { + "DB_HOST": "", + "DB_USER": "", + "DB_PWD": "", + "DB_DATABASE": "webappdb" + } + } + } +} diff --git a/infrastructure/web-tier-userdata.sh b/infrastructure/web-tier-userdata.sh new file mode 100644 index 000000000..b626201ac --- /dev/null +++ b/infrastructure/web-tier-userdata.sh @@ -0,0 +1,77 @@ +#!/bin/bash +# Web Tier EC2 user-data — Amazon Linux 2 +# Installs Nginx + Node 16, deploys React build, configures reverse proxy. +# Run as root at first boot. + +set -euxo pipefail + +# ── Variables (set before launch or via SSM Parameter Store) ────────────────── +S3_BUCKET="${S3_BUCKET:-YOUR-S3-BUCKET-NAME}" +INTERNAL_LB_DNS="${INTERNAL_LB_DNS:-REPLACE-WITH-INTERNAL-LB-DNS}" + +# ── System update ───────────────────────────────────────────────────────────── +yum update -y + +# ── Node.js 16 ──────────────────────────────────────────────────────────────── +curl -fsSL https://rpm.nodesource.com/setup_16.x | bash - +yum install -y nodejs + +# ── Nginx ───────────────────────────────────────────────────────────────────── +yum install -y nginx +systemctl enable nginx + +# ── Deploy React app from S3 ───────────────────────────────────────────────── +mkdir -p /home/ec2-user/web-tier +aws s3 cp s3://${S3_BUCKET}/web-tier /home/ec2-user/web-tier --recursive +cd /home/ec2-user/web-tier +npm install +npm run build + +# ── Nginx config ───────────────────────────────────────────────────────────── +cat > /etc/nginx/nginx.conf <

Web Tier Health Check

\n"; + } + + location / { + root /home/ec2-user/web-tier/build; + index index.html index.htm; + try_files \$uri /index.html; + } + + location /api/ { + proxy_pass http://${INTERNAL_LB_DNS}:80/; + } + } +} +NGINX + +systemctl start nginx + +# ── Fix permissions ─────────────────────────────────────────────────────────── +chown -R ec2-user:ec2-user /home/ec2-user/web-tier +chmod -R 755 /home/ec2-user/web-tier/build From 1c7394e47fa3a950d245d21b7864d29c2356604a Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 13 May 2026 07:11:47 +0000 Subject: [PATCH 2/3] Add complete instance setup and connection guide for all three tiers Step-by-step instructions covering DB setup (Aurora RDS + EC2 MySQL), App Tier (Node.js + PM2 + DbConfig), Web Tier (Nginx + React build), required code changes, end-to-end testing, and AMI creation for ASGs. https://claude.ai/code/session_01JeU1JS9MBg7zouezB1o4kr --- SETUP-GUIDE.md | 430 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 430 insertions(+) create mode 100644 SETUP-GUIDE.md diff --git a/SETUP-GUIDE.md b/SETUP-GUIDE.md new file mode 100644 index 000000000..4decca5f8 --- /dev/null +++ b/SETUP-GUIDE.md @@ -0,0 +1,430 @@ +# Three-Tier AWS Architecture — Complete Instance Setup Guide + +This guide walks you through configuring each EC2 instance from scratch before +you create AMIs for your Auto Scaling Groups. +Follow the order: **DB Tier → App Tier → Web Tier**. + +--- + +## Architecture Connection Map + +``` +Internet + │ + ▼ +External ALB (port 80, public subnets) + │ + ▼ +Web Tier EC2 (Nginx, port 80, public subnets) + │ /api/* requests proxied + ▼ +Internal ALB (port 80, private subnets) + │ + ▼ +App Tier EC2 (Node.js, port 4000, private subnets) + │ + ▼ +Aurora MySQL / EC2 MySQL (port 3306, private subnets) +``` + +--- + +## Prerequisites (all tiers) + +- All three instances are launched and running (Amazon Linux 2) +- Security groups are configured: + - Web Tier SG: inbound 80 from External ALB SG + - App Tier SG: inbound 4000 from Internal ALB SG + - DB SG: inbound 3306 from App Tier SG only +- EC2 instance role has `AmazonS3ReadOnlyAccess` and `AmazonSSMManagedInstanceCore` attached +- You are SSH'd into each instance as `ec2-user` + +--- + +## Part 1 — Database Tier + +### Option A: Amazon Aurora MySQL (RDS) — Recommended + +**In the RDS Console:** + +1. Go to **RDS → Create database** +2. Choose **Standard Create** +3. Engine: **Aurora (MySQL Compatible)**, version `8.0` +4. Template: **Dev/Test** (or Production for HA) +5. DB cluster identifier: `three-tier-aurora-cluster` +6. Master username: `admin` (note this down) +7. Master password: set a strong password (note this down) +8. Instance class: `db.t3.micro` +9. Multi-AZ: **Enable** (creates one writer + one reader) +10. VPC: select your workshop VPC +11. Subnet group: select private subnets only +12. Public access: **No** +13. Security group: attach the DB security group (inbound 3306 from App Tier SG) +14. Additional config → Initial database name: `webappdb` +15. Click **Create database** + +**After cluster is created (~5 min), note down:** +- Writer endpoint: `three-tier-aurora-cluster.cluster-xxxx..rds.amazonaws.com` +- Port: `3306` + +**Create the transactions table** (run from your App Tier instance after Node.js is set up, or from any instance with MySQL client): + +```sql +mysql -h -u admin -p webappdb + +CREATE TABLE IF NOT EXISTS transactions ( + id INT AUTO_INCREMENT PRIMARY KEY, + amount DECIMAL(10,2) NOT NULL, + description VARCHAR(255) NOT NULL +); + +EXIT; +``` + +--- + +### Option B: MySQL on EC2 (if not using RDS) + +SSH into your DB Tier EC2 instance and run: + +```bash +# Update system +sudo yum update -y + +# Install MySQL 8.0 +sudo rpm -Uvh https://dev.mysql.com/get/mysql80-community-release-el7-5.noarch.rpm +sudo yum install -y mysql-community-server + +# Start and enable MySQL +sudo systemctl start mysqld +sudo systemctl enable mysqld + +# Get the temporary root password +sudo grep 'temporary password' /var/log/mysqld.log +``` + +```bash +# Secure the installation and set a new root password +sudo mysql_secure_installation +# Follow prompts: set new password, remove anonymous users, disallow remote root, remove test DB +``` + +```bash +# Log in as root +mysql -u root -p + +# Create app database and user +CREATE DATABASE webappdb; +CREATE USER 'appuser'@'%' IDENTIFIED BY 'YourStrongPassword123!'; +GRANT ALL PRIVILEGES ON webappdb.* TO 'appuser'@'%'; +FLUSH PRIVILEGES; + +USE webappdb; + +CREATE TABLE IF NOT EXISTS transactions ( + id INT AUTO_INCREMENT PRIMARY KEY, + amount DECIMAL(10,2) NOT NULL, + description VARCHAR(255) NOT NULL +); + +EXIT; +``` + +> Note down your EC2 DB instance's **private IP address** — this will be your `DB_HOST`. + +--- + +## Part 2 — App Tier Instance + +SSH into your **App Tier EC2** (private subnet, accessed via bastion or SSM Session Manager). + +### Step 1 — Install Node.js 16 + +```bash +sudo yum update -y +curl -fsSL https://rpm.nodesource.com/setup_16.x | sudo bash - +sudo yum install -y nodejs +node -v # should print v16.x.x +npm -v +``` + +### Step 2 — Install PM2 + +```bash +sudo npm install -g pm2 +``` + +### Step 3 — Clone the repository + +```bash +cd /home/ec2-user +git clone https://github.com/Asadkhanrtx/aws-three-tier-web-architecture-workshop.git +cd aws-three-tier-web-architecture-workshop +``` + +### Step 4 — Configure the database connection ⚠️ CODE CHANGE REQUIRED + +Open the database config file: + +```bash +nano application-code/app-tier/DbConfig.js +``` + +Replace the empty strings with your actual values: + +```js +// BEFORE (default — do not leave empty) +module.exports = Object.freeze({ + DB_HOST : '', + DB_USER : '', + DB_PWD : '', + DB_DATABASE : '' +}); +``` + +```js +// AFTER — fill in your values +module.exports = Object.freeze({ + DB_HOST : 'three-tier-aurora-cluster.cluster-xxxx..rds.amazonaws.com', + // If using EC2 MySQL: DB_HOST : '10.0.x.x' (private IP of DB instance) + DB_USER : 'admin', + DB_PWD : 'YourStrongPassword123!', + DB_DATABASE : 'webappdb' +}); +``` + +Save and close (`Ctrl+O`, `Enter`, `Ctrl+X`). + +### Step 5 — Install dependencies and start the app + +```bash +cd /home/ec2-user/aws-three-tier-web-architecture-workshop/application-code/app-tier +npm install +pm2 start index.js --name "app-tier" +pm2 save +``` + +Enable PM2 to restart on reboot: + +```bash +sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u ec2-user --hp /home/ec2-user +sudo systemctl enable pm2-ec2-user +``` + +### Step 6 — Verify the app tier is running + +```bash +# Check PM2 status +pm2 status + +# Test health endpoint locally +curl http://localhost:4000/health +# Expected response: "This is the health check" + +# Test DB connection via transaction endpoint +curl http://localhost:4000/transaction +# Expected: {"result":[]} or a list of transactions +``` + +If you see a MySQL connection error, double-check: +- `DbConfig.js` has the correct endpoint/IP and credentials +- DB security group allows port 3306 from this instance's security group +- Aurora cluster is in **Available** state + +--- + +## Part 3 — Web Tier Instance + +SSH into your **Web Tier EC2** (public subnet). + +### Step 1 — Install Node.js 16 and Nginx + +```bash +sudo yum update -y +curl -fsSL https://rpm.nodesource.com/setup_16.x | sudo bash - +sudo yum install -y nodejs nginx +node -v +``` + +### Step 2 — Clone the repository + +```bash +cd /home/ec2-user +git clone https://github.com/Asadkhanrtx/aws-three-tier-web-architecture-workshop.git +cd aws-three-tier-web-architecture-workshop +``` + +### Step 3 — Build the React app + +```bash +cd /home/ec2-user/aws-three-tier-web-architecture-workshop/application-code/web-tier +npm install +npm run build +``` + +This creates `/home/ec2-user/aws-three-tier-web-architecture-workshop/application-code/web-tier/build/`. + +### Step 4 — Configure Nginx ⚠️ CODE CHANGE REQUIRED + +Open the nginx config: + +```bash +sudo nano /etc/nginx/nginx.conf +``` + +Replace the entire content with the config below. +**Replace `REPLACE-WITH-INTERNAL-LB-DNS` with your Internal ALB DNS name.** + +```nginx +user nginx; +worker_processes auto; +error_log /var/log/nginx/error.log; +pid /run/nginx.pid; +include /usr/share/nginx/modules/*.conf; + +events { worker_connections 1024; } + +http { + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + access_log /var/log/nginx/access.log main; + sendfile on; tcp_nopush on; tcp_nodelay on; + keepalive_timeout 65; types_hash_max_size 4096; + include /etc/nginx/mime.types; + default_type application/octet-stream; + + server { + listen 80; + listen [::]:80; + server_name _; + + # Health check for External ALB + location /health { + default_type text/html; + return 200 "

Web Tier Health Check

\n"; + } + + # Serve React SPA + location / { + root /home/ec2-user/aws-three-tier-web-architecture-workshop/application-code/web-tier/build; + index index.html index.htm; + try_files $uri /index.html; + } + + # Proxy API calls to Internal ALB → App Tier + location /api/ { + proxy_pass http://REPLACE-WITH-INTERNAL-LB-DNS:80/; + } + } +} +``` + +> **Where to find the Internal ALB DNS name:** +> AWS Console → EC2 → Load Balancers → select your Internal ALB → copy the DNS name +> It looks like: `internal-three-tier-internal-alb-xxxx.us-east-1.elb.amazonaws.com` + +Fix nginx file permissions so it can read the React build: + +```bash +sudo chmod 755 /home/ec2-user +sudo chmod -R 755 /home/ec2-user/aws-three-tier-web-architecture-workshop/application-code/web-tier/build +``` + +### Step 5 — Start Nginx + +```bash +sudo nginx -t # test config — must say "syntax is ok" +sudo systemctl start nginx +sudo systemctl enable nginx +``` + +### Step 6 — Verify the web tier is running + +```bash +# Health check +curl http://localhost/health +# Expected:

Web Tier Health Check

+ +# Check if React build is served +curl -s http://localhost/ | head -5 +# Expected: HTML content starting with +``` + +Also open the instance's public IP in a browser — you should see the React app. + +--- + +## Part 4 — Summary of All Code Changes + +| File | Change | Where | +|---|---|---| +| `application-code/app-tier/DbConfig.js` | Fill `DB_HOST`, `DB_USER`, `DB_PWD`, `DB_DATABASE` | App Tier EC2 | +| `/etc/nginx/nginx.conf` | Replace `REPLACE-WITH-INTERNAL-LB-DNS` with Internal ALB DNS | Web Tier EC2 | + +No changes are needed to the React frontend code — it uses relative `/api/` calls which Nginx proxies automatically. + +--- + +## Part 5 — Test End-to-End Before Creating AMIs + +1. **From Web Tier EC2**, test that the proxy reaches the app tier: + ```bash + curl http://localhost/api/health + # Expected: "This is the health check" + ``` + +2. **Open the web app in a browser** at the web tier's public IP. + Navigate to the **DB Demo page** (hamburger menu → DB Demo). + Add a transaction — if it saves and loads, all three tiers are connected. + +3. **Check PM2 logs** on the App Tier for any DB errors: + ```bash + pm2 logs app-tier --lines 50 + ``` + +--- + +## Part 6 — Create AMIs for Auto Scaling + +Once each instance is fully working, create an AMI from it: + +**AWS Console → EC2 → Instances → select instance → Actions → Image and templates → Create image** + +| Tier | AMI Name (suggestion) | What's baked in | +|---|---|---| +| App Tier | `app-tier-ami-v1` | Node 16, PM2, app code, `DbConfig.js` | +| Web Tier | `app-web-tier-ami-v1` | Node 16, Nginx, React build, nginx.conf with Internal LB DNS | + +> The DB Tier (Aurora RDS) has no AMI — it is a managed service. + +**After AMI is available (~5 min):** +1. Create a **Launch Template** using the AMI +2. Set instance type `t2.micro`, attach the correct security group and IAM role +3. Create an **Auto Scaling Group** using that Launch Template +4. Set min/max/desired capacity and attach to the corresponding ALB target group + +--- + +## Quick Reference — Ports and Endpoints + +| Component | Port | Protocol | Notes | +|---|---|---|---| +| External ALB | 80 | HTTP | Public-facing, routes to Web Tier | +| Web Tier (Nginx) | 80 | HTTP | Health check: `GET /health` | +| Internal ALB | 80 | HTTP | Private, routes to App Tier | +| App Tier (Node.js) | 4000 | HTTP | Health check: `GET /health` | +| Aurora MySQL / EC2 MySQL | 3306 | TCP | Accessible from App Tier SG only | + +--- + +## Troubleshooting + +| Symptom | Likely cause | Fix | +|---|---|---| +| `/api/health` returns 502 Bad Gateway | Internal LB DNS wrong in nginx.conf, or App Tier not running | Check nginx.conf proxy_pass value; run `pm2 status` on App Tier | +| App Tier `pm2 logs` shows `ECONNREFUSED` or `ENOTFOUND` | DB_HOST in DbConfig.js is wrong | Verify Aurora endpoint or EC2 DB private IP | +| App Tier `pm2 logs` shows `Access denied for user` | DB credentials wrong | Re-check DB_USER and DB_PWD in DbConfig.js | +| React app loads but DB Demo shows nothing | Internal ALB not yet configured, or App Tier SG blocks port 4000 | Confirm Internal ALB target group has App Tier registered and healthy | +| `sudo nginx -t` fails | Syntax error in nginx.conf | Check for missing semicolons; re-paste config from this guide | +| AMI instances in ASG fail health checks | app or nginx not started at boot | Ensure `pm2 startup` and `systemctl enable nginx` were run before AMI creation | From c7aba10388a132a0f01c7418c46eb5529c368377 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 13 May 2026 07:16:41 +0000 Subject: [PATCH 3/3] Add Ubuntu-specific three-tier instance setup guide Covers Ubuntu 22.04 LTS setup for all three tiers: Aurora/EC2 MySQL DB, Node.js + PM2 App Tier, Nginx + React Web Tier. Includes apt commands, nginx sites-available config, MySQL bind-address fix, PM2 startup for ubuntu user, and a diff table comparing Ubuntu vs Amazon Linux 2. https://claude.ai/code/session_01JeU1JS9MBg7zouezB1o4kr --- SETUP-GUIDE-UBUNTU.md | 484 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 484 insertions(+) create mode 100644 SETUP-GUIDE-UBUNTU.md diff --git a/SETUP-GUIDE-UBUNTU.md b/SETUP-GUIDE-UBUNTU.md new file mode 100644 index 000000000..07337b63a --- /dev/null +++ b/SETUP-GUIDE-UBUNTU.md @@ -0,0 +1,484 @@ +# Three-Tier AWS Architecture — Ubuntu Instance Setup Guide + +Same architecture, Ubuntu flavour. +All commands run as the default `ubuntu` user unless `sudo` is shown. +Follow the order: **DB Tier → App Tier → Web Tier**. + +--- + +## Architecture Connection Map + +``` +Internet + │ + ▼ +External ALB (port 80, public subnets) + │ + ▼ +Web Tier EC2 (Nginx, port 80, public subnets) + │ /api/* requests proxied + ▼ +Internal ALB (port 80, private subnets) + │ + ▼ +App Tier EC2 (Node.js, port 4000, private subnets) + │ + ▼ +Aurora MySQL / EC2 MySQL (port 3306, private subnets) +``` + +--- + +## Recommended Ubuntu AMI + +Use **Ubuntu Server 22.04 LTS (HVM), SSD Volume Type** — search for it in the +EC2 Launch Wizard. +Architecture: `x86_64` | Instance type: `t2.micro` (or `t3.micro`) + +Resolve the latest AMI ID for your region via AWS CLI: + +```bash +aws ec2 describe-images \ + --owners 099720109477 \ + --filters "Name=name,Values=ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*" \ + "Name=state,Values=available" \ + --query 'sort_by(Images,&CreationDate)[-1].ImageId' \ + --output text \ + --region +``` + +--- + +## Prerequisites (all tiers) + +- Instances launched with Ubuntu 22.04 LTS +- Security groups configured: + - Web Tier SG: inbound **80** from External ALB SG + - App Tier SG: inbound **4000** from Internal ALB SG + - DB SG: inbound **3306** from App Tier SG only +- EC2 instance role has `AmazonS3ReadOnlyAccess` and `AmazonSSMManagedInstanceCore` +- SSH user is `ubuntu` (not `ec2-user`) + +--- + +## Part 1 — Database Tier + +### Option A: Amazon Aurora MySQL (RDS) — Recommended + +Steps are identical to the Amazon Linux guide — Aurora is a managed service, +no OS differences apply. + +1. **RDS Console → Create database** +2. Engine: **Aurora (MySQL Compatible)** `8.0` +3. Template: Dev/Test or Production +4. Cluster identifier: `three-tier-aurora-cluster` +5. Master username: `admin` | Master password: set and note it down +6. Instance class: `db.t3.micro` +7. Multi-AZ: **Enable** +8. VPC: workshop VPC | Subnets: private subnets only | Public access: **No** +9. Security group: DB security group (inbound 3306 from App Tier SG) +10. Additional config → Initial database name: `webappdb` +11. **Create database** (takes ~5 min) + +Note down the **writer endpoint** once the cluster is available. + +**Create the transactions table** (run from App Tier after Node.js is ready): + +```bash +sudo apt install -y mysql-client + +mysql -h -u admin -p webappdb < Note down this instance's **private IP** — it will be your `DB_HOST` in `DbConfig.js`. + +--- + +## Part 2 — App Tier Instance + +SSH into your **App Tier Ubuntu EC2**. + +### Step 1 — System update + +```bash +sudo apt update && sudo apt upgrade -y +``` + +### Step 2 — Install Node.js 16 + +```bash +curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash - +sudo apt install -y nodejs +node -v # v16.x.x +npm -v +``` + +### Step 3 — Install Git + +```bash +sudo apt install -y git +``` + +### Step 4 — Install PM2 + +```bash +sudo npm install -g pm2 +``` + +### Step 5 — Clone the repository + +```bash +cd /home/ubuntu +git clone https://github.com/Asadkhanrtx/aws-three-tier-web-architecture-workshop.git +cd aws-three-tier-web-architecture-workshop +``` + +### Step 6 — Configure the database connection ⚠️ CODE CHANGE REQUIRED + +```bash +nano application-code/app-tier/DbConfig.js +``` + +```js +// BEFORE (do not leave empty strings) +module.exports = Object.freeze({ + DB_HOST : '', + DB_USER : '', + DB_PWD : '', + DB_DATABASE : '' +}); +``` + +```js +// AFTER — fill in your actual values +module.exports = Object.freeze({ + DB_HOST : 'three-tier-aurora-cluster.cluster-xxxx..rds.amazonaws.com', + // If using EC2 MySQL: DB_HOST : '10.0.x.x' (private IP of DB EC2) + DB_USER : 'admin', + DB_PWD : 'YourStrongPassword123!', + DB_DATABASE : 'webappdb' +}); +``` + +Save: `Ctrl+O` → `Enter` → `Ctrl+X` + +### Step 7 — Install dependencies and start the app + +```bash +cd /home/ubuntu/aws-three-tier-web-architecture-workshop/application-code/app-tier +npm install +pm2 start index.js --name "app-tier" +pm2 save +``` + +Enable PM2 on reboot: + +```bash +sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u ubuntu --hp /home/ubuntu +# Copy and run the command that pm2 prints out, then: +pm2 save +``` + +### Step 8 — Verify the app tier + +```bash +pm2 status + +# Health check +curl http://localhost:4000/health +# Expected: "This is the health check" + +# DB connection check +curl http://localhost:4000/transaction +# Expected: {"result":[]} +``` + +If you see a MySQL connection error, check: +- `DbConfig.js` has the correct endpoint/IP and credentials +- DB security group allows port 3306 from App Tier SG +- For EC2 MySQL: confirm `bind-address = 0.0.0.0` and MySQL is running + +--- + +## Part 3 — Web Tier Instance + +SSH into your **Web Tier Ubuntu EC2**. + +### Step 1 — System update + +```bash +sudo apt update && sudo apt upgrade -y +``` + +### Step 2 — Install Node.js 16 and Nginx + +```bash +# Node.js 16 +curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash - +sudo apt install -y nodejs + +# Nginx +sudo apt install -y nginx git + +node -v +nginx -v +``` + +### Step 3 — Clone the repository + +```bash +cd /home/ubuntu +git clone https://github.com/Asadkhanrtx/aws-three-tier-web-architecture-workshop.git +cd aws-three-tier-web-architecture-workshop +``` + +### Step 4 — Build the React app + +```bash +cd /home/ubuntu/aws-three-tier-web-architecture-workshop/application-code/web-tier +npm install +npm run build +``` + +Build output lands at: +`/home/ubuntu/aws-three-tier-web-architecture-workshop/application-code/web-tier/build/` + +### Step 5 — Configure Nginx ⚠️ CODE CHANGE REQUIRED + +```bash +sudo nano /etc/nginx/sites-available/default +``` + +Replace the entire file with: + +```nginx +server { + listen 80 default_server; + listen [::]:80 default_server; + + # Health check for External ALB + location /health { + default_type text/html; + return 200 "

Web Tier Health Check

\n"; + } + + # Serve React SPA + location / { + root /home/ubuntu/aws-three-tier-web-architecture-workshop/application-code/web-tier/build; + index index.html index.htm; + try_files $uri /index.html; + } + + # Proxy API calls → Internal ALB → App Tier + location /api/ { + proxy_pass http://REPLACE-WITH-INTERNAL-LB-DNS:80/; + } +} +``` + +> **Where to find the Internal ALB DNS:** +> AWS Console → EC2 → Load Balancers → select Internal ALB → copy DNS name. +> Example: `internal-three-tier-internal-alb-xxxx.us-east-1.elb.amazonaws.com` + +Remove the default symlink and re-enable: + +```bash +sudo rm /etc/nginx/sites-enabled/default +sudo ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default +``` + +Fix permissions so Nginx can read the build folder: + +```bash +sudo chmod 755 /home/ubuntu +sudo chmod -R 755 /home/ubuntu/aws-three-tier-web-architecture-workshop/application-code/web-tier/build +``` + +### Step 6 — Start Nginx + +```bash +sudo nginx -t # must print "syntax is ok" +sudo systemctl restart nginx +sudo systemctl enable nginx +``` + +### Step 7 — Verify the web tier + +```bash +# Health check +curl http://localhost/health +# Expected:

Web Tier Health Check

+ +# API proxy test (Internal LB must be up and App Tier must be running) +curl http://localhost/api/health +# Expected: "This is the health check" +``` + +Open the instance's public IP in a browser — React app should load. +Go to the **DB Demo page** (hamburger menu → DB Demo) and add a transaction to +confirm full end-to-end connectivity. + +--- + +## Part 4 — Summary of All Code Changes + +| File | What to change | Instance | +|---|---|---| +| `application-code/app-tier/DbConfig.js` | `DB_HOST`, `DB_USER`, `DB_PWD`, `DB_DATABASE` | App Tier EC2 | +| `/etc/nginx/sites-available/default` | Replace `REPLACE-WITH-INTERNAL-LB-DNS` with actual Internal ALB DNS | Web Tier EC2 | + +No changes needed in React source code — it uses relative `/api/` paths that Nginx proxies automatically. + +--- + +## Part 5 — End-to-End Test Before Creating AMIs + +Run these in order: + +```bash +# 1. On App Tier — DB connection +curl http://localhost:4000/transaction +# {"result":[]} + +# 2. On Web Tier — proxy through Internal LB to App Tier +curl http://localhost/api/health +# "This is the health check" + +# 3. On Web Tier — full stack (web → internal LB → app → DB) +curl http://localhost/api/transaction +# {"result":[]} +``` + +All three must succeed before you create AMIs. + +--- + +## Part 6 — Create AMIs for Auto Scaling + +**AWS Console → EC2 → Instances → select instance → Actions → Image and templates → Create image** + +| Tier | Suggested AMI name | What's captured | +|---|---|---| +| App Tier | `ubuntu-app-tier-ami-v1` | Node 16, PM2, cloned repo, filled `DbConfig.js`, pm2 startup configured | +| Web Tier | `ubuntu-web-tier-ami-v1` | Node 16, Nginx, React build, nginx config with Internal LB DNS | + +> Aurora DB Tier has no AMI — it is a managed RDS service. + +**After AMI creation (~5 min):** + +1. **Launch Template** — use the new AMI, instance type `t2.micro`, correct SG and IAM role +2. **Auto Scaling Group** — attach Launch Template, set min/max/desired, link to ALB target group + +--- + +## Quick Reference — Ports and Services + +| Component | Port | Health check path | Ubuntu service | +|---|---|---|---| +| Nginx (Web Tier) | 80 | `GET /health` | `nginx` | +| Node.js (App Tier) | 4000 | `GET /health` | `pm2` (app-tier process) | +| MySQL (EC2 DB) | 3306 | — | `mysql` | +| Aurora (RDS) | 3306 | — | managed | + +Useful service commands on Ubuntu: + +```bash +# Nginx +sudo systemctl status nginx +sudo systemctl restart nginx +sudo tail -f /var/log/nginx/error.log + +# PM2 / Node.js +pm2 status +pm2 logs app-tier --lines 50 +pm2 restart app-tier + +# MySQL (EC2 option) +sudo systemctl status mysql +sudo journalctl -u mysql -n 50 +``` + +--- + +## Troubleshooting + +| Symptom | Likely cause | Fix | +|---|---|---| +| `curl /api/health` returns 502 Bad Gateway | Internal LB DNS wrong in nginx config, or App Tier not running | Check `/etc/nginx/sites-available/default` proxy_pass; run `pm2 status` on App Tier | +| PM2 logs show `ECONNREFUSED` or `ENOTFOUND` | Wrong `DB_HOST` in `DbConfig.js` | Verify Aurora writer endpoint or EC2 DB private IP | +| PM2 logs show `Access denied for user` | Wrong credentials in `DbConfig.js` | Re-check `DB_USER` and `DB_PWD` | +| Nginx returns 403 Forbidden | Build directory not readable by nginx | `sudo chmod 755 /home/ubuntu` and build folder | +| `sudo nginx -t` fails | Config syntax error | Re-check `sites-available/default`; look for missing semicolons | +| React app loads, DB Demo shows nothing | App Tier SG or Internal ALB not configured | Confirm Internal ALB target group shows App Tier as **healthy** | +| ASG instances fail health checks after launch | PM2 or Nginx not auto-starting | Confirm `pm2 startup` and `systemctl enable nginx` were run before AMI snapshot | +| EC2 MySQL refuses remote connections | bind-address still 127.0.0.1 | Set `bind-address = 0.0.0.0` in `/etc/mysql/mysql.conf.d/mysqld.cnf` and restart | + +--- + +## Key Differences vs Amazon Linux 2 + +| Item | Amazon Linux 2 | Ubuntu 22.04 | +|---|---|---| +| Package manager | `yum` | `apt` | +| Default SSH user | `ec2-user` | `ubuntu` | +| Node.js setup script | `nodesource.com/setup_16.x \| bash -` | `nodesource.com/setup_16.x \| sudo -E bash -` | +| Nginx config location | `/etc/nginx/nginx.conf` | `/etc/nginx/sites-available/default` | +| Nginx sites-enabled | not used | `/etc/nginx/sites-enabled/` (symlink required) | +| MySQL package | `mysql-community-server` (via rpm) | `mysql-server` (via apt) | +| MySQL root login | `mysql -u root -p` | `sudo mysql` (auth_socket) | +| Home directory | `/home/ec2-user` | `/home/ubuntu` |