TLDR: Sử dụng cách đặt tên và tổ chức tệp nhất quán. Phiên bản nhà cung cấp pin. Giữ các mô-đun nhỏ và tập trung. Không bao giờ bí mật commit. Sử dụng nguồn dữ liệu thay vì giá trị mã hóa cứng. Kiểm tra các thay đổi trong dev trước production. Luôn chạy kế hoạch trước khi áp dụng. Ghi lại cơ sở hạ tầng của bạn. Triển khai xác thực tự động trong CI/CD.
Sau khi làm việc với Terraform một thời gian, một số mẫu nhất định xuất hiện giúp mã dễ bảo trì hơn và hoạt động an toàn hơn. Phần này bao gồm các bài thực hành đã học được khi chạy Terraform trong production.
Mã tổ chức
Cấu trúc tệp
Sử dụng cách đặt tên tệp nhất quán cho các dự án:
terraform/
├── main.tf # Primary resource definitions
├── variables.tf # Input variable declarations
├── outputs.tf # Output declarations
├── providers.tf # Provider configurations
├── backend.tf # Backend configuration
├── data.tf # Data source definitions
├── locals.tf # Local value definitions
├── versions.tf # Terraform and provider version constraints
└── README.md # Documentation
Đối với các dự án lớn hơn, hãy chia main.tf theo loại tài nguyên:
terraform/
├── network.tf # VPC, subnets, route tables
├── compute.tf # EC2 instances, ASGs
├── database.tf # RDS, ElastiCache
├── security.tf # Security groups, IAM roles
├── monitoring.tf # CloudWatch alarms, dashboards
├── variables.tf
├── outputs.tf
├── providers.tf
└── backend.tf
Quy ước đặt tên
Sử dụng cách đặt tên nhất quán cho tài nguyên:
# Pattern: <resource_type>_<descriptive_name>
resource "aws_vpc" "main" { ... }
resource "aws_subnet" "public_web" { ... }
resource "aws_instance" "web_server" { ... }
# Not: generic names
resource "aws_vpc" "vpc1" { ... } # Too generic
resource "aws_instance" "server" { ... } # Not descriptive
Sử dụng tên có ý nghĩa trong thẻ:
resource "aws_instance" "web" {
# ...
tags = {
Name = "${var.project}-${var.environment}-web-${count.index + 1}"
Environment = var.environment
Project = var.project
ManagedBy = "terraform"
Component = "web-tier"
}
}
Điều này giúp dễ dàng xác định tài nguyên trong bảng điều khiển AWS và trong báo cáo chi phí.
Ghim phiên bản
Luôn ghim các phiên bản Terraform và nhà cung cấp để tránh những thay đổi không mong muốn:
terraform {
required_version = "~> 1.6.0" # Allow 1.6.x but not 1.7.0
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0" # Allow 5.x but not 6.0
}
random = {
source = "hashicorp/random"
version = "~> 3.5"
}
}
}
Nếu không ghim, việc chạy terraform init có thể tải xuống phiên bản nhà cung cấp mới có những thay đổi đáng chú ý.
Toán tử ~> cho phép thành phần phiên bản ngoài cùng bên phải tăng:
~> 1.6.0khớp với1.6.1,1.6.2, nhưng không khớp với1.7.0~> 1.6khớp với1.6.0,1.7.0, nhưng không khớp với2.0.0
Quản lý biến
Sử dụng xác thực
Bắt lỗi trước khi thay đổi cơ sở hạ tầng:
variable "environment" {
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}
variable "instance_count" {
type = number
validation {
condition = var.instance_count > 0 && var.instance_count <= 10
error_message = "Instance count must be between 1 and 10."
}
}
variable "cidr_block" {
type = string
validation {
condition = can(cidrhost(var.cidr_block, 0))
error_message = "Must be a valid CIDR block."
}
}
Cung cấp mô tả
Ghi lại công dụng của từng biến:
variable "instance_type" {
description = "EC2 instance type. Use t3.micro for dev/staging, t3.large for prod"
type = string
default = "t3.micro"
}
variable "backup_retention_days" {
description = <<-EOT
Number of days to retain backups.
Minimum 7 for compliance.
Recommended: 30 for production, 7 for non-production.
EOT
type = number
default = 7
validation {
condition = var.backup_retention_days >= 7
error_message = "Backup retention must be at least 7 days for compliance."
}
}
Sử dụng ràng buộc loại
Chỉ định chính xác các loại để sớm phát hiện lỗi:
variable "server_config" {
description = "Server configuration"
type = object({
instance_type = string
disk_size = number
ami_id = string
tags = map(string)
})
default = {
instance_type = "t3.micro"
disk_size = 20
ami_id = "ami-0c55b159cbfafe1f0"
tags = {}
}
}
Điều này ngăn chặn việc truyền cấu trúc dữ liệu không chính xác.
Quản lý tài nguyên
Sử dụng nguồn dữ liệu
Truy vấn cơ sở hạ tầng hiện có thay vì các giá trị mã hóa cứng:
# Good - dynamic
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
# ...
}
# Bad - hardcoded AMI ID becomes outdated
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
# ...
}
Sử dụng quy tắc vòng đời một cách thích hợp
Bảo vệ các tài nguyên quan trọng:
resource "aws_db_instance" "production" {
identifier = "prod-db"
# ...
lifecycle {
prevent_destroy = true # Can't accidentally delete production database
}
}
resource "aws_launch_template" "web" {
name_prefix = "web-"
# ...
lifecycle {
create_before_destroy = true # Zero-downtime updates
}
}
Tránh tính số lượng tài nguyên có trạng thái
Sử dụng for_each thay vì count cho cơ sở dữ liệu, volume và các tài nguyên có trạng thái khác:
# Good - adding/removing items doesn't affect others
variable "databases" {
type = map(object({
allocated_storage = number
instance_class = string
}))
default = {
users = {
allocated_storage = 100
instance_class = "db.t3.medium"
}
orders = {
allocated_storage = 200
instance_class = "db.t3.large"
}
}
}
resource "aws_db_instance" "databases" {
for_each = var.databases
identifier = each.key
allocated_storage = each.value.allocated_storage
instance_class = each.value.instance_class
# ...
}
# Bad - removing the first item renumbers everything
resource "aws_db_instance" "databases" {
count = length(var.database_names)
identifier = var.database_names[count.index]
# ...
}
Với count, việc xóa cơ sở dữ liệu đầu tiên trong danh sách sẽ khiến Terraform phá hủy và tạo lại tất cả các cơ sở dữ liệu khác.
Mẫu mô-đun
Giữ mô-đun nhỏ
Mỗi mô-đun nên làm tốt một việc:
# Good - focused modules
modules/
├── vpc/ # Just networking
├── ec2_instance/ # Just compute
├── rds_database/ # Just database
└── alb/ # Just load balancing
# Bad - too broad
modules/
└── complete_infrastructure/ # Everything in one module
Sử dụng thành phần mô-đun
Kết hợp các mô-đun nhỏ thành các mẫu lớn hơn:
# High-level module that composes smaller modules
module "web_application" {
source = "./modules/web_application"
# This module internally uses:
# - vpc module
# - ec2_instance module
# - alb module
# - rds_database module
}
Phiên bản mô-đun của bạn
Gắn thẻ các mô-đun với phiên bản ngữ nghĩa:
git tag -a v1.0.0 -m "Initial release"
git tag -a v1.1.0 -m "Added monitoring"
git tag -a v2.0.0 -m "Breaking: changed variable names"
git push --tags
Tham khảo các phiên bản cụ thể:
module "vpc" {
source = "git::https://github.com/myorg/terraform-modules.git//vpc?ref=v1.0.0"
# ...
}
Điều này ngăn chặn những thay đổi đột phá ảnh hưởng đến cơ sở hạ tầng hiện có.
Quản lý trạng thái
Sử dụng Remote trạng thái có khóa
Không bao giờ sử dụng trạng thái cục bộ cho production. Luôn định cấu hình backend từ xa:
terraform {
backend "s3" {
bucket = "mycompany-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
Tách trạng thái theo môi trường
Không bao giờ chia sẻ trạng thái giữa các môi trường:
state/
├── dev/terraform.tfstate
├── staging/terraform.tfstate
└── prod/terraform.tfstate
Kích hoạt phiên bản trạng thái
Sử dụng phiên bản S3 hoặc tương tự để duy trì lịch sử trạng thái:
aws s3api put-bucket-versioning \
--bucket mycompany-terraform-state \
--versioning-configuration Status=Enabled
Điều này cho phép bạn khôi phục sau khi trạng thái bị hỏng.
Thực hành bảo mật
Không bao giờ có bí mật commit
Sử dụng các biến môi trường hoặc trình quản lý bí mật:
# Good - from environment variable
variable "database_password" {
type = string
sensitive = true
}
# Set via: export TF_VAR_database_password="secret"
# Better - from secret manager
data "aws_secretsmanager_secret_version" "db_password" {
secret_id = "prod/database/password"
}
resource "aws_db_instance" "main" {
password = data.aws_secretsmanager_secret_version.db_password.secret_string
# ...
}
Sử dụng đặc quyền tối thiểu IAM
Chỉ cấp các quyền cần thiết:
data "aws_iam_policy_document" "instance_assume_role" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ec2.amazonaws.com"]
}
}
}
resource "aws_iam_role" "instance" {
name = "instance-role"
assume_role_policy = data.aws_iam_policy_document.instance_assume_role.json
}
# Grant specific permissions, not full access
data "aws_iam_policy_document" "instance" {
statement {
actions = [
"s3:GetObject",
"s3:ListBucket",
]
resources = [
aws_s3_bucket.app_data.arn,
"${aws_s3_bucket.app_data.arn}/*",
]
}
}
resource "aws_iam_role_policy" "instance" {
name = "instance-policy"
role = aws_iam_role.instance.id
policy = data.aws_iam_policy_document.instance.json
}
Thẻ đầu ra nhạy cảm
Ngăn chặn bí mật xuất hiện trong nhật ký:
output "database_endpoint" {
value = aws_db_instance.main.endpoint
}
output "database_password" {
value = aws_db_instance.main.password
sensitive = true # Won't show in console output
}
Kiểm tra và xác nhận
Sử dụng địa hình fmt
Định dạng mã nhất quán:
terraform fmt -recursive
Thêm phần này vào CI pipeline của bạn:
terraform fmt -check -recursive || exit 1
Xác thực cấu hình
Kiểm tra lỗi cú pháp:
terraform validate
Sử dụng tflint
Cài đặt xác nhận bổ sung:
# Install tflint
brew install tflint # macOS
# or download from https://github.com/terraform-linters/tflint
# Run it
tflint
Định cấu hình tflint trong .tflint.hcl:
plugin "aws" {
enabled = true
version = "0.27.0"
source = "github.com/terraform-linters/tflint-ruleset-aws"
}
rule "terraform_naming_convention" {
enabled = true
}
rule "terraform_documented_variables" {
enabled = true
}
Lên kế hoạch trước khi nộp đơn
Luôn xem xét các thay đổi:
# Save the plan
terraform plan -out=tfplan
# Review it carefully
terraform show tfplan
# Apply the exact plan
terraform apply tfplan
Không bao giờ chạy terraform apply -auto-approve trong production mà không xem lại kế hoạch trước.
Thử nghiệm trong quá trình phát triển đầu tiên
Không bao giờ kiểm tra các thay đổi trực tiếp trong production:
- Áp dụng cho môi trường dev
- Xác minh mọi thứ hoạt động
- Áp dụng cho staging
- Xác minh lại
- Áp dụng cho production
Tích hợp CI/CD
Tự động hóa các hoạt động của Terraform để đảm bảo tính nhất quán:
# .github/workflows/terraform.yml
name: Terraform
on:
pull_request:
paths:
- 'terraform/**'
push:
branches: [main]
paths:
- 'terraform/**'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: 1.6.5
- name: Terraform Format Check
run: terraform fmt -check -recursive
working-directory: terraform
- name: Terraform Init
run: terraform init
working-directory: terraform/environments/dev
- name: Terraform Validate
run: terraform validate
working-directory: terraform/environments/dev
- name: Run tflint
uses: terraform-linters/setup-tflint@v3
with:
tflint_version: latest
- name: Initialize tflint
run: tflint --init
working-directory: terraform/environments/dev
- name: Run tflint
run: tflint --recursive
working-directory: terraform/environments/dev
plan:
needs: validate
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
- name: Terraform Init
run: terraform init
working-directory: terraform/environments/dev
- name: Terraform Plan
run: terraform plan -no-color
working-directory: terraform/environments/dev
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
apply:
needs: validate
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
- name: Terraform Init
run: terraform init
working-directory: terraform/environments/dev
- name: Terraform Apply
run: terraform apply -auto-approve
working-directory: terraform/environments/dev
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
Quy trình công việc này:
- Xác thực định dạng và cú pháp trên mỗi PR
- Chạy kế hoạch PR để cho thấy những gì sẽ thay đổi
- Áp dụng các thay đổi khi hợp nhất vào chính
- Bắt lỗi trước khi chúng đạt tới production
Tài liệu
Tệp README
Mọi dự án Terraform đều phải có README:
# Production Infrastructure
Terraform configuration for production environment.
## Prerequisites
- Terraform 1.6.x
- AWS CLI configured
- Access to terraform-state S3 bucket
## Usage
\`\`\`bash
cd terraform/environments/prod
terraform init
terraform plan
terraform apply
\`\`\`
## Architecture
- VPC: 10.1.0.0/16
- 3 AZs with public and private subnets
- NAT gateways for private subnet internet access
- Application load balancer for web tier
- RDS Multi-AZ for database
## Variables
See `variables.tf` for all available options.
Required variables:
- `database_password`: Set via `TF_VAR_database_password`
## Outputs
After applying:
- `load_balancer_dns`: Application endpoint
- `database_endpoint`: Database connection string
## Disaster Recovery
State is versioned in S3. To restore:
\`\`\`bash
aws s3api list-object-versions --bucket mycompany-terraform-state --prefix prod/
# Find the version ID you want to restore
aws s3api get-object --bucket mycompany-terraform-state --key prod/terraform.tfstate --version-id VERSION_ID terraform.tfstate
\`\`\`
Tài liệu nội tuyến
Sử dụng nhận xét cho các quyết định không rõ ràng:
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
# Disable source/dest check for NAT functionality
source_dest_check = false
# Use GP3 for better price/performance ratio
root_block_device {
volume_type = "gp3"
volume_size = 20
iops = 3000 # GP3 includes 3000 IOPS at base cost
}
# User data runs on first boot only
# For configuration changes, update the AMI instead
user_data = templatefile("${path.module}/init.sh", {
environment = var.environment
})
}
Giám sát và cảnh báo
Theo dõi hoạt động của Terraform:
resource "aws_cloudwatch_log_group" "terraform" {
name = "/terraform/runs"
retention_in_days = 30
}
resource "aws_sns_topic" "terraform_alerts" {
name = "terraform-alerts"
}
resource "aws_cloudwatch_metric_alarm" "state_lock_held" {
alarm_name = "terraform-state-lock-held-too-long"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 1
metric_name = "LockHeldDuration"
namespace = "Terraform"
period = 3600
statistic = "Maximum"
threshold = 1800 # 30 minutes
alarm_actions = [aws_sns_topic.terraform_alerts.arn]
}
Phát hiện trôi dạt
Kiểm tra xem cơ sở hạ tầng có được sửa đổi bên ngoài Terraform hay không:
# Show differences between state and reality
terraform plan -detailed-exitcode
# Exit code 2 means drift detected
Tự động hóa việc này bằng các lần chạy theo lịch trình:
# .github/workflows/drift-detection.yml
name: Drift Detection
on:
schedule:
- cron: '0 */6 * * *' # Every 6 hours
jobs:
detect-drift:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
- name: Terraform Init
run: terraform init
working-directory: terraform/environments/prod
- name: Detect Drift
run: terraform plan -detailed-exitcode
working-directory: terraform/environments/prod
continue-on-error: true
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Notify on Drift
if: failure()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "Drift detected in production infrastructure!"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
Các bước tiếp theo
Bây giờ bạn đã có nền tảng để sử dụng Terraform một cách hiệu quả. Hãy cân nhắc việc khám phá:
- Terraform Cloud: Dịch vụ được quản lý với nhiều tính năng hơn
- Chính sách dưới dạng Code: Sử dụng Sentinel hoặc OPA để tuân thủ
- Testing: Các công cụ như Terratest để kiểm tra cơ sở hạ tầng
- Mô-đun nâng cao: Nghiên cứu các mô-đun phổ biến cho các mẫu
- Đa khu vực: Chiến lược cho cơ sở hạ tầng toàn cầu
Hệ sinh thái Terraform rất lớn và năng động. Tham gia cộng đồng, đọc tài liệu của nhà cung cấp và tiếp tục học hỏi kinh nghiệm của người khác. Cơ sở hạ tầng dưới dạng mã là một hành trình - những phương pháp thực hành này sẽ giúp bạn xây dựng các hệ thống có thể bảo trì và mở rộng quy mô.