Terraform
← Tổng quan series

Dựng hạ tầng cho ứng dụng thật bằng nhiều module

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.

Instructor walkthrough

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.

01Chuẩn bịkiểm tra account, region, provider và chi phí
02Đọc planđối chiếu resource address, diff và dependency
03Thực hànhapply mutation nhỏ trong sandbox, lưu evidence
04Khôi phụcre-run, failure drill, destroy hoặc rollback
Command reference · baseline
terraform fmt
terraform init
terraform validate
terraform plan
terraform apply
terraform plan -destroy
terraform destroy

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.

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:

  1. Chạy plan từng module rồi full plan, so sánh dependency graph/output; xác nhận -target chỉ dùng bootstrap/recovery và full plan sau đó không còn phần bị bỏ sót.
  2. Đổ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.
  3. 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.
  4. 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.