Chapter 15: Amazon VPC — Subnets, Route Tables, NACL, NAT Gateway and Bastion Host
15.10 Project: build the 3-tier network and launch the servers
Come on, now the real project! A small "students list" website: the Nginx web server in a public subnet, the PHP-FPM app server in a private subnet, and the MySQL database in another private subnet. When you open the page in the browser, the request travels across three servers and the data from the database comes back. So the very first job – get the network and servers ready.
चला, आता खरा project! एक छोटी "students list" website: Nginx web server public subnet मध्ये, PHP-FPM app server private subnet मध्ये, आणि MySQL database दुसऱ्या private subnet मध्ये. Browser मध्ये page उघडलं की request तीन servers वरून जाते आणि database मधला data परत येतो. तर सगळ्यात पहिलं काम – network आणि servers तयार करणं.
चलो, अब असली project! एक छोटी "students list" website: Nginx web server public subnet में, PHP-FPM app server private subnet में, और MySQL database दूसरे private subnet में. Browser में page खोलते ही request तीन servers से होकर जाती है और database का data वापस आता है. तो सबसे पहला काम – network और servers तैयार करना.
The finished project looks like this:
Browser (HTTP 80) You (SSH 22)
| |
v v
[ Internet gateway: society-igw ]
|
+------------ society-vpc 10.0.0.0/16 ------------+
| web-public-1a 10.0.1.0/24 public-rt -> IGW |
| web: Nginx 10.0.1.10 bastion 10.0.1.20 |
| NAT gateway + Elastic IP |
| | fastcgi 9000 ^ outbound only |
| app-private-1a 10.0.2.0/24 private-rt -> NAT |
| app: PHP-FPM 10.0.2.10 |
| | MySQL 3306 |
| db-private-1a 10.0.3.0/24 private-rt -> NAT |
| db: MariaDB 10.0.3.10 |
+-------------------------------------------------+
Step 1 — Network
Build it as in the earlier lessons:
society-vpc10.0.0.0/16with DNS hostnames on (15.2).- Subnets
web-public-1a10.0.1.0/24(auto-assign public IPv4 on),app-private-1a10.0.2.0/24,db-private-1a10.0.3.0/24, all inap-south-1a(15.3). society-igwattached to the VPC (15.5).public-rtwith0.0.0.0/0→ IGW for the public subnet;private-rtfor both private subnets (15.6).society-natinweb-public-1awith an Elastic IP, and0.0.0.0/0→ NAT inprivate-rt(15.9). The private servers need it to install packages.
Keep the VPC's default network ACL (it allows all traffic); the security groups below do the precise work.
Step 2 — Security groups (the chain)
Create them in this order in EC2 → Security Groups → Create security group, with VPC society-vpc, because each one refers to the one before it:
| Security group | Inbound rule | Source | Why |
|---|---|---|---|
bastion-sg |
SSH TCP 22 | My IP | Only you can reach the bastion |
web-sg |
HTTP TCP 80 | 0.0.0.0/0 |
Visitors |
web-sg |
HTTPS TCP 443 | 0.0.0.0/0 |
Visitors, once you add a certificate |
web-sg |
SSH TCP 22 | bastion-sg |
Admin access only through the bastion |
app-sg |
Custom TCP 9000 | web-sg |
Only Nginx may talk to PHP-FPM |
app-sg |
SSH TCP 22 | bastion-sg |
Admin access |
db-sg |
MySQL/Aurora TCP 3306 | app-sg |
Only the app server may talk to MySQL |
db-sg |
SSH TCP 22 | bastion-sg |
Admin access |
Leave the outbound rules at the default (all traffic allowed). Using a security group as the source means "any instance that carries that group", so the rules keep working when private IPs change.
Step 3 — Launch four instances
Create a key pair society-key (EC2 → Key pairs), keep society-key.pem safe on your laptop and run chmod 400 society-key.pem. Then launch four Amazon Linux 2023 instances with a small free-tier eligible type such as t3.micro or t2.micro. In the launch wizard, open Network settings → Edit:
| Name | VPC | Subnet | Auto-assign public IP | Security group |
|---|---|---|---|---|
bastion |
society-vpc |
web-public-1a |
Enable | bastion-sg |
web |
society-vpc |
web-public-1a |
Enable | web-sg |
app |
society-vpc |
app-private-1a |
Disable | app-sg |
db |
society-vpc |
db-private-1a |
Disable | db-sg |
Step 4 — Write down the private IPs
AWS picks the private IPs from each subnet. This chapter uses these example values; replace them with yours in every command and config file that follows:
| Server | Example private IP | Public IP |
|---|---|---|
web |
10.0.1.10 |
<WEB_PUBLIC_IP> |
bastion |
10.0.1.20 |
<BASTION_PUBLIC_IP> |
app |
10.0.2.10 |
none |
db |
10.0.3.10 |
none |
Step 5 — First check: reach every server through the bastion
On your laptop:
ssh-add society-key.pem
ssh -J ec2-user@<BASTION_PUBLIC_IP> ec2-user@10.0.1.10 # web
ssh -J ec2-user@<BASTION_PUBLIC_IP> ec2-user@10.0.2.10 # app
ssh -J ec2-user@<BASTION_PUBLIC_IP> ec2-user@10.0.3.10 # db
Each one should end at a prompt such as [ec2-user@ip-10-0-2-10 ~]$. On app and db, run curl -s https://checkip.amazonaws.com: it prints the NAT gateway's Elastic IP, so package installs will work.
Ravindra Bagale's Tip
The order matters when you create the security groups: first bastion-sg, then web-sg, then app-sg, and db-sg last – because each group uses the previous one as its source. Many students put an IP like 10.0.1.10/32 as the source; when a server is replaced the IP changes and the project breaks. Choose the security group as the source. A rock-solid design!
Ravindra Bagale's Tip – मराठी
Security groups बनवताना क्रम महत्त्वाचा आहे: आधी bastion-sg, मग web-sg, मग app-sg, शेवटी db-sg – कारण पुढचा group मागच्या group ला source म्हणून वापरतो. बरेच students source मध्ये IP 10.0.1.10/32 टाकतात; server replace केला की IP बदलतो आणि project तुटतो. Source मध्ये security group निवडा. एकदम पक्का design!
Ravindra Bagale's Tip – हिंदी
Security groups बनाते समय क्रम ज़रूरी है: पहले bastion-sg, फिर web-sg, फिर app-sg, आखिर में db-sg – क्योंकि अगला group पिछले group को source की तरह इस्तेमाल करता है. बहुत से students source में IP 10.0.1.10/32 डाल देते हैं; server replace होते ही IP बदलता है और project टूट जाता है. Source में security group चुनो. एकदम पक्का design!
Lab
Chala, build the network, the four security groups and the four instances. Fill in the private IP table with your own values, and connect to all three servers with ssh -J before you go on.
Step-by-step guide