
Terraform với AWS: Hướng dẫn xây dựng infrastructure thực tế
Sau bài giới thiệu Terraform cơ bản, hôm nay chúng ta sẽ đi sâu vào thực hành với AWS – cloud provider phổ biến nhất. Bài viết này hướng dẫn tạo một infrastructure hoàn chỉnh bao gồm VPC, EC2, RDS và ALB.
Nội dung chính
Chuẩn bị
AWS Credentials
# Cấu hình AWS CLI
aws configure
# AWS Access Key ID: AKIAXXXXXXXX
# AWS Secret Access Key: xxxxxxxxxxxxx
# Default region: ap-northeast-1
# Default output format: jsonProject Structure
aws-infrastructure/
├── main.tf
├── variables.tf
├── outputs.tf
├── vpc.tf
├── ec2.tf
├── rds.tf
├── alb.tf
└── security-groups.tfVPC và Networking (vpc.tf)
# VPC
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.project_name}-vpc"
}
}
# Internet Gateway
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
}
# Public Subnets
resource "aws_subnet" "public" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = "10.0.${count.index + 1}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.project_name}-public-${count.index + 1}"
}
}
# Private Subnets
resource "aws_subnet" "private" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = "10.0.${count.index + 10}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {
Name = "${var.project_name}-private-${count.index + 1}"
}
}
# NAT Gateway
resource "aws_eip" "nat" {
domain = "vpc"
}
resource "aws_nat_gateway" "main" {
allocation_id = aws_eip.nat.id
subnet_id = aws_subnet.public[0].id
}
# Route Tables
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
}
resource "aws_route_table" "private" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main.id
}
}Security Groups (security-groups.tf)
# ALB Security Group
resource "aws_security_group" "alb" {
name = "${var.project_name}-alb-sg"
description = "Security group for ALB"
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
# EC2 Security Group
resource "aws_security_group" "ec2" {
name = "${var.project_name}-ec2-sg"
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
security_groups = [aws_security_group.alb.id]
}
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [var.allowed_ssh_cidr]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
# RDS Security Group
resource "aws_security_group" "rds" {
name = "${var.project_name}-rds-sg"
vpc_id = aws_vpc.main.id
ingress {
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups = [aws_security_group.ec2.id]
}
}EC2 Instance (ec2.tf)
resource "aws_instance" "web" {
count = 2
ami = data.aws_ami.amazon_linux.id
instance_type = var.instance_type
subnet_id = aws_subnet.private[count.index].id
vpc_security_group_ids = [aws_security_group.ec2.id]
key_name = var.key_pair_name
user_data = <<-EOF
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
echo "Hello from $(hostname)" > /var/www/html/index.html
EOF
tags = {
Name = "${var.project_name}-web-${count.index + 1}"
}
}RDS Database (rds.tf)
resource "aws_db_subnet_group" "main" {
name = "${var.project_name}-db-subnet"
subnet_ids = aws_subnet.private[*].id
}
resource "aws_db_instance" "main" {
identifier = "${var.project_name}-db"
engine = "mysql"
engine_version = "8.0"
instance_class = "db.t3.micro"
allocated_storage = 20
storage_type = "gp2"
db_name = var.db_name
username = var.db_username
password = var.db_password
db_subnet_group_name = aws_db_subnet_group.main.name
vpc_security_group_ids = [aws_security_group.rds.id]
skip_final_snapshot = true
multi_az = false
tags = {
Name = "${var.project_name}-db"
}
}Application Load Balancer (alb.tf)
resource "aws_lb" "main" {
name = "${var.project_name}-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.alb.id]
subnets = aws_subnet.public[*].id
}
resource "aws_lb_target_group" "main" {
name = "${var.project_name}-tg"
port = 80
protocol = "HTTP"
vpc_id = aws_vpc.main.id
health_check {
path = "/"
healthy_threshold = 2
unhealthy_threshold = 10
}
}
resource "aws_lb_target_group_attachment" "main" {
count = 2
target_group_arn = aws_lb_target_group.main.arn
target_id = aws_instance.web[count.index].id
port = 80
}
resource "aws_lb_listener" "http" {
load_balancer_arn = aws_lb.main.arn
port = "80"
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.main.arn
}
}Deploy
# Khởi tạo và download providers
terraform init
# Xem kế hoạch
terraform plan
# Apply
terraform apply
# Output
terraform output alb_dns_nameChi phí ước tính
- 2x t3.micro EC2: ~$15/tháng
- 1x db.t3.micro RDS: ~$13/tháng
- ALB: ~$16/tháng + data transfer
- NAT Gateway: ~$32/tháng
- Tổng: ~$76/tháng
Fullstack Station Tips
Infrastructure này là nền tảng tốt cho production workloads nhỏ. Một số cải tiến có thể làm:
- Thêm Auto Scaling Group cho EC2
- Bật Multi-AZ cho RDS
- Thêm CloudFront cho static assets
- Sử dụng Secrets Manager cho credentials
Trong bài tiếp theo, mình sẽ hướng dẫn về Terraform Modules để tái sử dụng code hiệu quả hơn.
