Triển Khai Laravel Lên AWS (Phần 1): Kiến Trúc — VPC, Subnets & Security Groups
Bạn đã xây xong app Laravel ở local. Giờ thì sao? Ném lên một EC2 duy nhất với mọi thứ trên một server thì ok cho demo, nhưng production đòi hỏi nhiều hơn: mạng cách ly, database quản lý, CDN, cân bằng tải, và triển khai tự động.
Series này hướng dẫn triển khai ứng dụng Laravel lên AWS đúng cách — cùng kiến trúc mà tôi dùng trên production.
Chúng Ta Sẽ Xây Gì
Sau 5 phần của series, bạn sẽ có:
| Thành phần | Dịch vụ AWS | Mục đích |
|---|---|---|
| Mạng | VPC + Subnets | Mạng cách ly với vùng public/private |
| Compute | EC2 (Amazon Linux 2023) | PHP-FPM + Nginx web server |
| Database | RDS (MySQL 8.0) | Database quản lý trong private subnet |
| Lưu trữ | S3 | File upload, backup, assets |
| Cân bằng tải | ALB | Phân phối traffic, SSL termination |
| CDN | CloudFront | Cache assets tĩnh toàn cầu |
| SSL | ACM | Chứng chỉ SSL miễn phí |
| DNS | Route 53 | Quản lý domain |
| Deploy | GitHub Actions + Envoy | Triển khai zero-downtime |
Tổng Quan Kiến Trúc
┌───────────────────────────────────────┐
│ Route 53 (DNS) │
└──────────────────┬────────────────────┘
│
┌──────────────────┴────────────────────┐
│ CloudFront (CDN) │
│ Assets tĩnh: /build/*, /img/* │
└──────────────────┬────────────────────┘
│
┌──────────────────┴────────────────────┐
│ ALB (Application Load Balancer) │
│ SSL termination (ACM Certificate) │
│ Port 443 → Target Group :80 │
└───┬──────────────────────────────┬────┘
│ VPC 10.0.0.0/16 │
┌───────────┴───────────┐ ┌─────────────┴──────────┐
│ Public Subnet A │ │ Public Subnet C │
│ 10.0.1.0/24 │ │ 10.0.2.0/24 │
│ AZ: ap-northeast-1a │ │ AZ: ap-northeast-1c │
│ │ │ │
│ ┌─────────────────┐ │ │ ┌─────────────────┐ │
│ │ EC2 Instance │ │ │ │ EC2 Instance │ │
│ │ Amazon Linux │ │ │ │ (mở rộng sau) │ │
│ │ 2023 │ │ │ │ │ │
│ │ Nginx+PHP-FPM │ │ │ │ │ │
│ └─────────────────┘ │ │ └─────────────────┘ │
└───────────────────────┘ └────────────────────────┘
┌───────────────────────┐ ┌────────────────────────┐
│ Private Subnet A │ │ Private Subnet C │
│ 10.0.11.0/24 │ │ 10.0.12.0/24 │
│ │ │ │
│ ┌─────────────────┐ │ │ │
│ │ RDS MySQL 8.0 │ │ │ (RDS Standby - │
│ │ Primary │ │ │ Multi-AZ replica) │
│ └─────────────────┘ │ │ │
└───────────────────────┘ └────────────────────────┘
│
┌───┴───────────────────────────────────┐
│ S3 Bucket │
│ uploads / backups / assets │
└───────────────────────────────────────┘
Tại Sao Kiến Trúc Này?
Public subnets chứa tài nguyên cần truy cập internet (EC2 qua ALB). Private subnets chứa tài nguyên không bao giờ được truy cập trực tiếp từ internet (RDS database).
Hai Availability Zones (AZ-a và AZ-c) cung cấp dự phòng. Nếu một AZ gặp sự cố, AZ còn lại vẫn chạy. RDS Multi-AZ tự động failover database.
Bước 1: Tạo VPC
Qua AWS Console
- Vào VPC → Your VPCs → Create VPC
- Chọn VPC and more (tự tạo subnets)
- Cấu hình:
| Thiết lập | Giá trị |
|---|---|
| Name | laravel-production |
| IPv4 CIDR | 10.0.0.0/16 |
| Số AZs | 2 |
| Public subnets | 2 |
| Private subnets | 2 |
| NAT gateways | None (tiết kiệm; thêm sau nếu cần) |
| VPC endpoints | S3 Gateway |
Qua AWS CLI
# Tạo VPC
aws ec2 create-vpc \
--cidr-block 10.0.0.0/16 \
--tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=laravel-production}]'
# Bật DNS hostnames (bắt buộc cho RDS)
aws ec2 modify-vpc-attribute \
--vpc-id vpc-xxxx \
--enable-dns-hostnames
Bước 2: Internet Gateway & Route Tables
# Tạo và gắn Internet Gateway
aws ec2 create-internet-gateway \
--tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=laravel-igw}]'
aws ec2 attach-internet-gateway \
--internet-gateway-id igw-xxxx \
--vpc-id vpc-xxxx
# Route table public — route ra internet qua IGW
aws ec2 create-route-table \
--vpc-id vpc-xxxx \
--tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=laravel-public-rt}]'
aws ec2 create-route \
--route-table-id rtb-xxxx \
--destination-cidr-block 0.0.0.0/0 \
--gateway-id igw-xxxx
Private subnets dùng main route table (không có internet route) — chỉ giao tiếp trong VPC.
Bước 3: Security Groups
Security groups hoạt động như tường lửa ảo. Chúng ta cần ba nhóm:
ALB Security Group
aws ec2 create-security-group \
--group-name laravel-alb-sg \
--description "ALB - Cho phep HTTP/HTTPS tu internet" \
--vpc-id vpc-xxxx
# Cho phép HTTP và HTTPS từ mọi nơi
aws ec2 authorize-security-group-ingress \
--group-id sg-alb \
--ip-permissions \
IpProtocol=tcp,FromPort=80,ToPort=80,IpRanges='[{CidrIp=0.0.0.0/0}]' \
IpProtocol=tcp,FromPort=443,ToPort=443,IpRanges='[{CidrIp=0.0.0.0/0}]'
EC2 Security Group
aws ec2 create-security-group \
--group-name laravel-ec2-sg \
--description "EC2 - Chi cho phep traffic tu ALB" \
--vpc-id vpc-xxxx
# Cho phép HTTP chỉ từ ALB (không phải từ internet!)
aws ec2 authorize-security-group-ingress \
--group-id sg-ec2 \
--protocol tcp --port 80 \
--source-group sg-alb
# Cho phép SSH chỉ từ IP của bạn
aws ec2 authorize-security-group-ingress \
--group-id sg-ec2 \
--protocol tcp --port 22 \
--cidr YOUR_IP/32
RDS Security Group
aws ec2 create-security-group \
--group-name laravel-rds-sg \
--description "RDS - Chi cho phep MySQL tu EC2" \
--vpc-id vpc-xxxx
# Cho phép MySQL chỉ từ EC2 security group
aws ec2 authorize-security-group-ingress \
--group-id sg-rds \
--protocol tcp --port 3306 \
--source-group sg-ec2
Luồng Security Group
Internet → [sg-alb :80/:443] → ALB → [sg-ec2 :80] → EC2 → [sg-rds :3306] → RDS
Nguyên tắc chính: mỗi lớp chỉ nhận traffic từ lớp phía trên. RDS không bao giờ thấy traffic internet. EC2 không nhận HTTP trực tiếp từ internet.
Bước 4: IAM Role Cho EC2
Thay vì lưu AWS credentials trên server, tạo IAM role mà EC2 tự động assume:
aws iam create-role \
--role-name laravel-ec2-role \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "ec2.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}'
# Gắn policy truy cập S3
aws iam put-role-policy \
--role-name laravel-ec2-role \
--policy-name s3-access \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::your-laravel-bucket/*"
}, {
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": "arn:aws:s3:::your-laravel-bucket"
}]
}'
Bảo mật: Không bao giờ hardcode
AWS_ACCESS_KEY_IDvàAWS_SECRET_ACCESS_KEYtrong file.env. IAM role tự động cung cấp credentials cho EC2 instance.
Bước 5: SSH Key Pair
aws ec2 create-key-pair \
--key-name laravel-production \
--query 'KeyMaterial' \
--output text > laravel-production.pem
chmod 400 laravel-production.pem
Ước Tính Chi Phí
Cho một Laravel SaaS nhỏ-vừa:
| Dịch vụ | Spec | Chi phí/tháng (Tokyo) |
|---|---|---|
| EC2 | t3.small (2 vCPU, 2GB) | ~$19 |
| RDS | db.t3.micro (1 vCPU, 1GB) | ~$16 |
| ALB | Cố định + traffic | ~$18 |
| S3 | 10GB storage | ~$0.25 |
| CloudFront | 50GB transfer | ~$4 |
| Route 53 | 1 hosted zone | $0.50 |
| ACM | Chứng chỉ SSL | Miễn phí |
| Tổng | ~$58/tháng |
Tiếp Theo
Ở Phần 2, chúng ta sẽ khởi chạy EC2 instance với Amazon Linux 2023, cài đặt Nginx + PHP-FPM 8.4, cấu hình server từ đầu, và triển khai app Laravel thủ công lần đầu tiên.
Điều hướng Series:
- ← Phần 0: Chuẩn Bị
- Phần 1: Kiến Trúc & VPC (Bạn đang ở đây)
- Phần 2: EC2 & Amazon Linux 2023 →
- Phần 3: RDS, S3 & ElastiCache →
- Phần 4: ALB, CloudFront & SSL →
- Phần 5: CI/CD & Zero-Downtime Deploy →