Terraform đã trở thành tiêu chuẩn trong ngành cho hạ tầng dưới dạng mã (Infrastructure as Code - IaC), cho phép các nhóm triển khai và quản lý tài nguyên đám mây thông qua các tệp cấu hình khai báo. Tuy nhiên, khi hạ tầng của bạn mở rộng, việc duy trì mã Terraform có thể trở nên khó khăn nếu không tuân theo các thực tiễn phù hợp.
Trong hướng dẫn này, bạn sẽ học được các thực tiễn tốt đã được kiểm chứng trong thực tế để tổ chức, viết và quản lý mã Terraform sao cho có thể mở rộng theo nhu cầu hạ tầng của bạn.
Sử dụng cấu trúc thư mục nhất quán
Tổ chức mã Terraform với một cấu trúc thư mục hợp lý để dễ bảo trì hơn:
terraform-project/
├── environments/
│ ├── dev/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── outputs.tf
│ │ └── terraform.tfvars
│ ├── staging/
│ └── production/
├── modules/
│ ├── networking/
│ ├── compute/
│ └── database/
└── .gitignore
Cấu trúc này tách biệt các môi trường triển khai với các module có thể tái sử dụng, cho phép tổ chức rõ ràng và triển khai đồng nhất trên nhiều môi trường.
Tách tài nguyên thành các Module hợp lý
Thay vì định nghĩa tất cả tài nguyên trong một tệp, hãy tổ chức chúng thành các module theo chức năng:
# modules/networking/main.tf
resource "aws_vpc" "main" { cidr_block = var.vpc_cidr tags = { Name = "${var.project}-vpc" Environment = var.environment Terraform = "true" }
} resource "aws_subnet" "public" { count = length(var.public_subnet_cidrs) vpc_id = aws_vpc.main.id cidr_block = var.public_subnet_cidrs[count.index] availability_zone = var.availability_zones[count.index] tags = { Name = "${var.project}-public-subnet-${count.index}" Environment = var.environment Terraform = "true" }
} # Additional networking resources...
Mỗi module nên đại diện cho một thành phần logic của hạ tầng và xử lý một mối quan tâm cụ thể. Cách tiếp cận này giúp mã dễ bảo trì và tái sử dụng.
Sử dụng Biến để cấu hình
Định nghĩa biến cho tất cả các tham số có thể cấu hình để làm cho module linh hoạt:
# modules/database/variables.tf
variable "instance_class" { description = "The instance type of the RDS instance" type = string default = "db.t3.micro"
} variable "allocated_storage" { description = "The allocated storage in gigabytes" type = number default = 20
} variable "engine_version" { description = "The engine version to use" type = string default = "13.7"
} variable "database_name" { description = "The name of the database to create" type = string
} variable "environment" { description = "The deployment environment (dev, staging, prod)" type = string
}
Luôn thêm mô tả, kiểu dữ liệu và giá trị mặc định (nếu phù hợp). Điều này giúp người khác hiểu rõ ý nghĩa và yêu cầu của từng tham số.
Áp dụng quy ước đặt tên và gắn thẻ nhất quán
Việc đặt tên và gắn thẻ tài nguyên một cách nhất quán giúp quản lý và tổ chức hạ tầng dễ dàng hơn rất nhiều:
# Create a standardized tagging function
locals { common_tags = { Project = var.project_name Environment = var.environment Owner = var.team ManagedBy = "Terraform" }
} resource "aws_s3_bucket" "logs" { bucket = "${var.project_name}-${var.environment}-logs" tags = merge(local.common_tags, { Name = "${var.project_name}-${var.environment}-logs" Description = "Bucket for application logs" })
}
Hãy xác định một quy tắc đặt tên chuẩn cho từng loại tài nguyên và áp dụng nhất quán trong toàn bộ hạ tầng. Điều này giúp tài nguyên dễ nhận diện, đơn giản hóa việc vận hành và xử lý sự cố.
Sử dụng Data Source để tham chiếu tài nguyên bên ngoài
Hãy sử dụng data thay vì hardcode giá trị khi cần tham chiếu đến tài nguyên đã tồn tại:
# Instead of hardcoding AMI IDs
data "aws_ami" "ubuntu" { most_recent = true filter { name = "name" values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] } filter { name = "virtualization-type" values = ["hvm"] } owners = ["099720109477"] # Canonical's AWS account ID
} resource "aws_instance" "web" { ami = data.aws_ami.ubuntu.id instance_type = var.instance_type # Other instance configuration...
}
Cách làm này giúp mã linh hoạt và dễ bảo trì khi tài nguyên bên ngoài thay đổi.
Giữ cấu hình Backend nhất quán
Lưu trạng thái Terraform vào backend từ xa (ví dụ S3) cùng với cơ chế khóa trạng thái để hỗ trợ làm việc nhóm:
# environments/dev/backend.tf
terraform { backend "s3" { bucket = "company-terraform-states" key = "dev/terraform.tfstate" region = "us-west-2" encrypt = true dynamodb_table = "terraform-state-locks" }
}
Sử dụng mẫu tên nhất quán cho tệp trạng thái, ví dụ:
dev/terraform.tfstate
staging/terraform.tfstate
production/terraform.tfstate
Phiên bản hóa Provider và Module
Luôn chỉ định phiên bản cụ thể cho provider và module để đảm bảo tính tái tạo:
terraform { required_version = ">= 1.0.0, < 2.0.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 4.16.0" } cloudflare = { source = "cloudflare/cloudflare" version = "~> 3.18.0" } }
}
Đối với các mô-đun, hãy chỉ định phiên bản trong thuộc tính nguồn:
module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "3.14.0" # Module parameters...
}
Việc này giúp tránh lỗi do cập nhật phiên bản bất ngờ.
Sử dụng vòng lặp và điều kiện để tránh lặp code
Terraform hỗ trợ for_each
, count
, và biểu thức điều kiện:
# Create multiple similar resources
resource "aws_security_group_rule" "ingress" { for_each = { http = { port = 80, cidr = ["0.0.0.0/0"] } https = { port = 443, cidr = ["0.0.0.0/0"] } ssh = { port = 22, cidr = ["10.0.0.0/8"] } } type = "ingress" security_group_id = aws_security_group.web.id from_port = each.value.port to_port = each.value.port protocol = "tcp" cidr_blocks = each.value.cidr description = "Allow ${each.key} traffic"
} # Conditional resource creation
resource "aws_route53_record" "www" { count = var.create_dns_record ? 1 : 0 zone_id = var.zone_id name = "www.${var.domain_name}" type = "A" alias { name = aws_cloudfront_distribution.cdn.domain_name zone_id = aws_cloudfront_distribution.cdn.hosted_zone_id evaluate_target_health = false }
}
Cách tiếp cận này làm cho mã của bạn ngắn gọn hơn và dễ bảo trì hơn.
Thêm kiểm thử tự động
Bạn có thể viết kiểm thử đơn giản:
# testing/main.tf
module "test_vpc" { source = "../../modules/networking" project = "test" environment = "dev" vpc_cidr = "10.0.0.0/16" # Other required variables...
} # Output test results
output "validation" { value = { vpc_created = module.test_vpc.vpc_id != "" num_subnets = length(module.test_vpc.subnet_ids) }
}
Sử dụng Terratest, kitchen-terraform
, hoặc shell script để kiểm thử.
Sử dụng Workspace một cách cẩn trọng
Workspaces có thể hữu ích nhưng không nên dùng thay cho việc tách môi trường rõ ràng:
Cách nên làm:
cd environments/dev
terraform apply
Không nên:
terraform workspace select dev
terraform apply
Bảo mật cấu hình Terraform
Không hardcode thông tin nhạy cảm:
variable "database_password" { description = "Mật khẩu truy cập DB" type = string sensitive = true
}
Sử dụng IAM theo nguyên tắc quyền tối thiểu
Định nghĩa group và ACL hợp lý:
resource "aws_security_group" "database" { name = "${var.project}-${var.environment}-db-sg" description = "Security group cho DB" vpc_id = var.vpc_id ingress { from_port = 5432 to_port = 5432 protocol = "tcp" security_groups = [var.app_security_group_id] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] }
}
Tài liệu hóa hạ tầng
Thêm README và chú thích đầy đủ:
# modules/database/README.md ## Module Cơ sở dữ liệu Module này triển khai một DB PostgreSQL với security group và cấu hình backup phù hợp. ### Sử dụng:
module "database" { source = "../modules/database" project = "ecommerce" environment = "production" instance_class = "db.r5.large"
}
Thiết lập CI/CD Pipeline
Tự động hóa kiểm tra, plan, apply:
# .github/workflows/terraform.yml
name: 'Terraform CI/CD' on: push: branches: [main] pull_request: branches: [main] jobs: terraform: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: hashicorp/setup-terraform@v2 with: terraform_version: 1.2.3 - run: terraform fmt -check -recursive - run: cd environments/dev && terraform init - run: cd environments/dev && terraform validate -no-color - run: cd environments/dev && terraform plan -no-color if: github.event_name == 'pull_request' continue-on-error: true
Kết luận
Tuân thủ các thực tiễn tốt khi sử dụng Terraform sẽ giúp mã hạ tầng của bạn dễ bảo trì, có thể mở rộng và bảo mật. Hạ tầng dưới dạng mã không chỉ là tự động hóa, mà còn là cách tiếp cận chuyên nghiệp và có chiến lược.
Hãy bắt đầu bằng việc:
- Tách mã thành module
- Đặt tên & gắn thẻ nhất quán
- Quản lý state cẩn thận
Sau đó mới dần áp dụng kiểm thử tự động và CI/CD.
Cảm ơn các bạn đã theo dõi!