|
| 1 | +# AWS VPC + ALB + Private EC2 Lab (2-AZ Network Architecture) |
| 2 | + |
| 3 | +This lab demonstrates a production-style AWS network like you’d design as a Solutions Architect: |
| 4 | + |
| 5 | +- Custom **VPC** |
| 6 | +- **Public** and **private** subnets across **2 Availability Zones** |
| 7 | +- **Internet Gateway** and **NAT Gateway** |
| 8 | +- **Application Load Balancer (ALB)** in public subnets |
| 9 | +- **EC2 web servers** in private subnets |
| 10 | +- Layered **route tables** and **security groups** |
| 11 | + |
| 12 | +> Traffic flow: **Client → ALB (public subnet) → EC2 (private subnet)** |
| 13 | +
|
| 14 | +I built this to deepen my understanding of AWS networking, high availability, and exam-style architectures for the AWS Solutions Architect Associate. |
| 15 | + |
| 16 | +--- |
| 17 | + |
| 18 | +## High-Level Architecture |
| 19 | + |
| 20 | +**Region:** `us-east-1` (N. Virginia) |
| 21 | +**VPC CIDR:** `10.0.0.0/16` |
| 22 | + |
| 23 | +### Subnets |
| 24 | + |
| 25 | +| Subnet name | Type | AZ | CIDR | Purpose | |
| 26 | +|------------|---------|-------------|---------------|---------------------------------| |
| 27 | +| `public-a` | Public | `us-east-1a` | `10.0.1.0/24` | ALB, NAT gateway | |
| 28 | +| `public-b` | Public | `us-east-1b` | `10.0.2.0/24` | ALB | |
| 29 | +| `private-a`| Private | `us-east-1a` | `10.0.11.0/24`| EC2 web server | |
| 30 | +| `private-b`| Private | `us-east-1b` | `10.0.12.0/24`| EC2 web server | |
| 31 | + |
| 32 | +### Internet Connectivity |
| 33 | + |
| 34 | +- **Internet Gateway (`sa-lab-igw`)** |
| 35 | + - Attached to the VPC |
| 36 | + - Public route table sends `0.0.0.0/0` → IGW |
| 37 | + |
| 38 | +- **NAT Gateway (`sa-lab-nat-a`)** |
| 39 | + - Lives in `public-a` |
| 40 | + - Private route table sends `0.0.0.0/0` → NAT |
| 41 | + - Allows private EC2 instances to reach the internet **outbound only** (e.g., OS updates) while remaining non-public. |
| 42 | + |
| 43 | +### Route Tables |
| 44 | + |
| 45 | +- **Public Route Table (`sa-lab-public-rt`)** |
| 46 | + - Associated with `public-a`, `public-b` |
| 47 | + - Routes: |
| 48 | + - `10.0.0.0/16` → local |
| 49 | + - `0.0.0.0/0` → Internet Gateway |
| 50 | + |
| 51 | +- **Private Route Table (`sa-lab-private-rt`)** |
| 52 | + - Associated with `private-a`, `private-b` |
| 53 | + - Routes: |
| 54 | + - `10.0.0.0/16` → local |
| 55 | + - `0.0.0.0/0` → NAT Gateway |
| 56 | + |
| 57 | +### Compute & Load Balancing |
| 58 | + |
| 59 | +- **EC2 Instances** |
| 60 | + - AMI: Amazon Linux |
| 61 | + - Type: `t2.micro`/`t3.micro` |
| 62 | + - Subnets: |
| 63 | + - `sa-lab-web-a` in `private-a` |
| 64 | + - `sa-lab-web-b` in `private-b` |
| 65 | + - **No public IPs** |
| 66 | + - Bootstrapped with a user data script to install Apache and serve a simple page: |
| 67 | + [`user-data/webserver.sh`](user-data/webserver.sh) |
| 68 | + |
| 69 | +- **Application Load Balancer (`sa-lab-alb`)** |
| 70 | + - Scheme: Internet-facing |
| 71 | + - Subnets: `public-a`, `public-b` |
| 72 | + - Listener: HTTP :80 → Target group `sa-lab-tg-web` |
| 73 | + - Target type: **Instance** |
| 74 | + - Health checks: HTTP `/` |
| 75 | + |
| 76 | +### Security Groups |
| 77 | + |
| 78 | +- **ALB Security Group (`sa-lab-alb-sg`)** |
| 79 | + - Inbound: |
| 80 | + - HTTP 80 from `0.0.0.0/0` (internet) — for the lab |
| 81 | + - Outbound: |
| 82 | + - All traffic (default) |
| 83 | + |
| 84 | +- **Web Server Security Group (`sa-lab-web-sg`)** |
| 85 | + - Inbound: |
| 86 | + - HTTP 80 **from `sa-lab-alb-sg` only** |
| 87 | + - Outbound: |
| 88 | + - All traffic (default) |
| 89 | + |
| 90 | +This creates a proper layered security model: |
| 91 | + |
| 92 | +- Internet can reach **only** the ALB |
| 93 | +- ALB can reach the web servers |
| 94 | +- Web servers are not directly reachable from the internet |
| 95 | + |
| 96 | +--- |
| 97 | + |
| 98 | +## Build Steps (Summary) |
| 99 | + |
| 100 | +I created everything using the AWS Console to really see how the pieces fit: |
| 101 | + |
| 102 | +1. **VPC** |
| 103 | + - Created `sa-lab-vpc` with CIDR `10.0.0.0/16` |
| 104 | + |
| 105 | +2. **Subnets** |
| 106 | + - Created two public and two private subnets across `us-east-1a` and `us-east-1b` |
| 107 | + |
| 108 | +3. **Internet Gateway & NAT** |
| 109 | + - Created and attached `sa-lab-igw` |
| 110 | + - Created `sa-lab-nat-a` in `public-a` with an Elastic IP |
| 111 | + |
| 112 | +4. **Route Tables** |
| 113 | + - Public RT: associated with public subnets, default route to IGW |
| 114 | + - Private RT: associated with private subnets, default route to NAT |
| 115 | + |
| 116 | +5. **EC2 Instances in Private Subnets** |
| 117 | + - Launched `sa-lab-web-a` in `private-a` and `sa-lab-web-b` in `private-b` |
| 118 | + - Disabled public IPs |
| 119 | + - Added user data to install Apache and serve a simple HTML page |
| 120 | + |
| 121 | +6. **Security Groups** |
| 122 | + - `sa-lab-alb-sg`: HTTP from internet |
| 123 | + - `sa-lab-web-sg`: HTTP only from `sa-lab-alb-sg` |
| 124 | + |
| 125 | +7. **Target Group & ALB** |
| 126 | + - Created `sa-lab-tg-web` (instance target type, HTTP:80, health check `/`) |
| 127 | + - Registered both EC2 instances |
| 128 | + - Created ALB `sa-lab-alb` targeting `sa-lab-tg-web` and mapped it to `public-a` and `public-b` |
| 129 | + |
| 130 | +--- |
| 131 | + |
| 132 | +## How to Test |
| 133 | + |
| 134 | +1. Go to **EC2 → Load Balancers** in the AWS Console. |
| 135 | +2. Select `sa-lab-alb`. |
| 136 | +3. Copy the **DNS name**, e.g.: |
| 137 | + |
| 138 | + ```text |
| 139 | + sa-lab-alb-1234567890.us-east-1.elb.amazonaws.com |
0 commit comments