Bài giảng: hiểu luồng trước khi chạy lệnh
Bài toán: Ghép networking, database và autoscaling thành một root module có ranh giới rõ ràng, output rõ và dễ thay đổi.
terraform fmt
terraform init
terraform validate
terraform plan
terraform apply
terraform plan -destroy
terraform destroyTư 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.
Bài lab cố ý rút gọn để bạn triển khai từng module theo từng commit; không dùng cấu hình này làm production blueprint nếu chưa bổ sung backup, observability, security review và capacity test.
Expected state và integration gate
Bắt đầu bằng plan-only với environment = "dev", NAT/RDS/ALB cost được ghi rõ và tag Training = terraform-06. Expected state của root module là networking expose đúng contract, database chỉ nhận private subnet/security group, autoscaling nhận output cần thiết, không có secret plaintext và không có dependency xuyên vào resource nội bộ module.
Thực hiện các drill:
- Chạy plan từng module rồi full plan, so sánh dependency graph/output; xác nhận
-targetchỉ dùng bootstrap/recovery và full plan sau đó không còn phần bị bỏ sót. - Đổi output name/type hoặc xóa một subnet key trong module networking, quan sát consumer plan và đánh giá replacement/route/DB impact trước khi apply.
- Tạo dev database với deletion protection/snapshot policy phù hợp, thử teardown plan và xác nhận không thể xóa dữ liệu production-like ngoài approval; kiểm tra password không xuất hiện trong plan/log/artifact.
- Làm module autoscaling fail bằng AMI/launch template/health check sai trong sandbox, đọc ASG/ALB/instance evidence và rollback module input; không tăng desired capacity để che health failure.
Quality gate
Pass khi root module có dependency/output contract rõ, plan dev/prod tách biệt, database lifecycle/backup/security được review, ALB→ASG health và DB private connectivity có acceptance evidence, cost NAT/RDS/ALB được kiểm soát, và teardown có plan -destroy/snapshot/owner gate. Không dùng depends_on toàn cục hay -target làm deploy thường ngày.