Triển Khai Laravel Lên AWS (Phần 1): Kiến Trúc — VPC, Subnets & Security Groups

· 6 min read

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

  1. Vào VPC → Your VPCs → Create VPC
  2. Chọn VPC and more (tự tạo subnets)
  3. 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_IDAWS_SECRET_ACCESS_KEY trong 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:

Bình luận