Tư duy từ root module
Ở bài trước module là VPC. Với một ứng dụng thật, root module nên đóng vai trò “lắp ráp”:
root
├── module.networking
├── module.database
└── module.autoscaling
Module networking sở hữu VPC/subnet; database nhận private subnet và security group; autoscaling nhận subnet public/private, AMI hoặc launch template và target group. Mỗi module chỉ nên expose contract cần thiết qua output.
Gọi các module theo dependency
module "networking" {
source = "./modules/networking"
environment = var.environment
}
module "database" {
source = "./modules/database"
environment = var.environment
vpc_id = module.networking.vpc_id
private_subnet_ids = module.networking.private_subnet_ids
}
module "autoscaling" {
source = "./modules/autoscaling"
environment = var.environment
private_subnet_ids = module.networking.private_subnet_ids
database_endpoint = module.database.endpoint
}
Tham chiếu output tạo dependency tự nhiên. Không dùng depends_on toàn cục nếu chỉ cần một dependency cụ thể; dependency quá rộng làm plan chậm và dễ replace nhiều thứ.
Database: secret và lifecycle
resource "aws_db_instance" "this" {
identifier = "${var.environment}-app-db"
engine = "postgres"
instance_class = var.db_instance_class
allocated_storage = 20
db_subnet_group_name = aws_db_subnet_group.this.name
skip_final_snapshot = var.environment != "production"
deletion_protection = var.environment == "production"
publicly_accessible = false
}
Trong production, dùng Secrets Manager/SSM hoặc cơ chế secret của CI; không đặt password plaintext trong module. skip_final_snapshot = false và deletion protection là hai lớp bảo vệ dữ liệu, nhưng cần plan/qui trình phá bỏ rõ ràng khi teardown.
Autoscaling không phải chỉ là “tạo vài EC2”
Một module ASG nên bao gồm launch template, IAM instance profile, security group, target group, load balancer và scaling policy. Tách phần base image/user data khỏi logic module giúp thay image mà không phải sửa toàn mạng.
resource "aws_autoscaling_group" "app" {
min_size = var.min_size
max_size = var.max_size
desired_capacity = var.desired_capacity
vpc_zone_identifier = var.private_subnet_ids
launch_template {
id = aws_launch_template.app.id
version = aws_launch_template.app.latest_version
}
}
Checklist thiết kế module
- Input có type, description và validation.
- Output chỉ expose ID/endpoint cần thiết.
- Không hard-code region, account ID hoặc secret.
- Có tag thống nhất và tên resource dễ truy vết.
- Database production có backup, encryption, deletion protection.
- Plan của dev và production được review riêng.
Cách triển khai theo từng lớp
Đừng bắt đầu bằng một apply tạo cả hệ thống. Tách thay đổi thành các checkpoint:
terraform plan -target=module.networking
terraform apply -target=module.networking
terraform plan
-target hữu ích cho bootstrap/recovery có chủ đích, nhưng không nên trở thành quy trình thường ngày vì nó có thể bỏ qua dependency ngoài target. Sau khi network ổn, apply database, kiểm tra connectivity, rồi mới bật autoscaling.
Một module tốt có thể được thay thế mà root module ít đổi. Nếu database module cần biết chi tiết resource bên trong networking module, ranh giới đang quá lộ implementation; hãy sửa contract bằng output như vpc_id, private_subnet_ids, database_security_group_id.
Đọc bản gốc tại Infrastructure for a Real Application. Ví dụ ở đây cố ý rút gọn để bạn triển khai từng module theo từng commit.