Các best pratice khi viết hạ tầng dưới dạng mã (IaS) với Terraform

0 0 0

Người đăng: Kubernetes

Theo Viblo Asia

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!

Bình luận

Bài viết tương tự

- vừa được xem lúc

The Twelve-Factor App, cẩm nang gối đầu giường trong xây dựng application (Phần 1)

Giới thiệu. Ngày nay các phần mềm được triển khai dưới dạng các dịch vụ, chúng được gọi là các web apps hay software-as-a-service (SaaS).

0 0 41

- vừa được xem lúc

8 Sai lầm phổ biến khi lập trình Android

1. Hard code.

0 0 201

- vừa được xem lúc

Popular interview question: What is the difference between Process and Thread? 10 seconds a day

Video được đăng tại channel Tips Javascript

0 0 39

- vừa được xem lúc

Thuật toán và ứng dụng - P1

Mục đích series. . Những bài toán gắn liền với thực tế. Từ đó thấy được tầm quan trọng của thuật toán trong lập trình.

0 0 43

- vừa được xem lúc

Tác dụng của Docker trong quá trình học tập

Docker bây giờ gần như là kiến thức bắt buộc đối với các anh em Dev và Devops, nhưng mà đối với sinh viên IT nói chung vẫn còn khá mơ hồ và không biết tác dụng thực tế của nó. Hôm nay mình sẽ chia sẻ

0 0 49

- vừa được xem lúc

Làm giàu trong ngành IT

Hầu như mọi người đều đi làm để kiếm tiền, ít người đi làm vì thấy cái nghề đó thú vị lắm. Bây giờ vất cho mình 100 tỷ bảo mình bỏ nghề thì mình cũng bỏ thôi.

0 0 50