0

Destroy Worlds với Terraform #1: terraform-null-label

Tính linh hoạt của Terraform là điều khó có công cụ nào sánh được. Nó giúp Devops team xây dựng và cung cấp những platforms có năng lực đáp ứng đa dạng nhu cầu của khách hàng từ đơn giản đến phức tạp. Một trong những thách thức lớn nhất mà tôi thường gặp trong quá trình làm việc với infrastructure là vấn đề Technical Debt và Resource Sprawl (tình trạng tài nguyên ngày càng phình to, phân tán và khó kiểm soát).

Vấn đề này thường bắt nguồn từ việc thiếu nhất quán trong các tiêu chuẩn đặt tên (Naming) và gắn thẻ (Tagging). Sự thiếu nhất quán này dẫn đến nhiều hệ quả:

  • Disorganization: Infrastructure trở nên thiếu tổ chức và khó quản lý.
  • Slow Delivery: Quá trình triển khai và bàn giao công việc bị chậm lại.
  • Lack of Cost Intelligence: Khó theo dõi và phân tích chi phí sử dụng infrastructure.
  • Security Vulnerabilities: Trong những trường hợp nghiêm trọng nhất, các resources không được kiểm kê hoặc quản lý đầy đủ có thể tạo ra những lỗ hổng bảo mật.
  • Đây là một vấn đề rất phổ biến trong lĩnh vực infrastructure và cũng là một trong những vấn đề mà cộng đồng DevOps luôn nỗ lực tìm cách giải quyết.

Một trong những giải pháp từng được AWS đưa ra nhằm hỗ trợ vấn đề này là tính năng Default Tags trong AWS Provider.

Tham khảo: AWS Provider – Default Tags

Tính năng này cho phép người dùng tự động gán Tags cho tất cả resources được tạo thông qua AWS Provider. Điều này giúp việc tổ chức và nhận diện resources trở nên dễ dàng hơn. Tuy nhiên, giải pháp này vẫn còn nhiều hạn chế và chưa thực sự hoàn hảo. Cách triển khai tương đối phức tạp và chưa giải quyết triệt để vấn đề Naming Convention. Có thể xem đây chỉ là một giải pháp mang tính tạm thời (Band-aid Solution) – giống như việc dùng băng cá nhân để xử lý một vấn đề lớn hơn, thay vì giải quyết tận gốc nguyên nhân. Và tất nhiên, giải pháp này cũng chỉ áp dụng cho AWS Provider.

Đây là lúc mà terraform-null-label xuất hiện trong cuộc chơi, giải pháp này mang đến một cách tiếp cận gọn gàng và hợp lý hơn để giải quyết những vấn đề nói trên. Module này có khả năng tạo và chuẩn hóa Names và Tags cho các resources trên toàn bộ Terraform environment, qua đó đảm bảo tính nhất quán trong cách quản lý infrastructure. Trong bài viết này, chúng ta sẽ cùng tìm hiểu sâu hơn về module này.

1. Tầm quan trọng của việc đặt tên nhất quán

Trong giai đoạn đầu của một startup hoặc một project, infrastructure thường khá đơn giản. Ví dụ:

  • Server được đặt tên là server01.
  • Database được đặt tên là database01.

Lúc này, số lượng resource ít, team nhỏ và mọi người đều hiểu rõ kiến trúc hệ thống. Vì vậy, việc đặt tên thường đơn giản và dễ quản lý.

Tuy nhiên, khi project phát triển:

  • Số lượng application tăng lên.
  • Có thêm nhiều customer-facing applications.
  • Số lượng cloud resources ngày càng nhiều.
  • Infrastructure as Code (IaC) được triển khai để quản lý infrastructure.

Lúc này, vấn đề naming convention bắt đầu xuất hiện.

Ban đầu có database tên: database01

Sau này, một database mới được tạo với naming convention chi tiết hơn: database10-us-east1-prod

Tên mới giúp chúng ta dễ nhận biết:

  • Resource thuộc region nào?
  • Được sử dụng trong environment nào?
  • Resource có mục đích gì?

Nhưng vấn đề là không ai muốn đổi tên database01, vì tên này có thể đã được hardcode ở nhiều vị trí trong hệ thống.

Vấn đề tương tự cũng xảy ra với Tags. Ví dụ, ban đầu chỉ có tag: backend Sau đó, hệ thống phát triển và xuất hiện thêm: backend-auth, backend-job-processor

Nếu không có naming và tagging convention nhất quán, chúng ta sẽ gặp khó khăn trong việc:

  • Quản lý và phân loại resources.
  • Xác định resource nào thuộc team nào.
  • Theo dõi chi phí của từng service.
  • Xác định resource không còn được sử dụng.
  • Kiểm soát security và giảm attack surface.

Khi infrastructure ngày càng lớn, những vấn đề này khiến việc onboarding engineer mới cũng mất nhiều thời gian hơn.

AWS Default Tags có giải quyết được vấn đề không?

AWS Provider của Terraform cung cấp tính năng default_tags.

Tính năng này cho phép tự động gán Tags cho các resources được tạo thông qua AWS Provider.

Tuy nhiên, giải pháp này vẫn có một số hạn chế:

  • Chỉ giải quyết vấn đề tagging, chưa giải quyết triệt để naming convention.
  • Cách triển khai chưa thực sự linh hoạt.
  • Chỉ áp dụng cho AWS Provider.

2. Giải pháp: terraform-null-label

GitHub: terraform-null-label repository

Mục đích của module này là:

  • Tạo naming convention thống nhất.
  • Chuẩn hóa Tags cho resources.
  • Tái sử dụng naming convention giữa nhiều Terraform modules.
  • Giảm việc phải tự đặt tên cho từng resource.
  • Giảm thiểu sai sót do con người.

2.1. ID Elements

Module cho phép định nghĩa tối đa 6 thành phần đầu vào để tạo ra một ID thống nhất.

Thành phần Ý nghĩa
Namespace Tên viết tắt của công ty, thường từ 2–4 ký tự
Tenant Đại diện cho customer hoặc team cụ thể
Environment Đại diện cho AWS region
Stage Giai đoạn của application, ví dụ dev, uat, prod
Name Tên component hoặc service, ví dụ rds, eks, backend-auth
Attributes Danh sách các chuỗi bổ sung để mô tả resource, ví dụ primary, secondary

Lưu ý về Environment và Stage: Đây là điểm khá dễ nhầm lẫn. Trong module này:

  • Environment được sử dụng để biểu thị region.
  • Stage được sử dụng để biểu thị deployment environment như dev, stage, prod.

Điều này hơi khác với cách đặt tên thông thường mà nhiều engineer quen sử dụng.

2.2. Ví dụ tạo ID

Giả sử chúng ta có một module tên frontend_label với cấu hình:

namespace   = "mp"       # Masterpoint
environment = "uw2"      # AWS us-west-2
stage       = "prod"
name        = "frontend"
attributes  = ["public"]

Module sẽ kết hợp các thành phần trên để tạo ra: mp-uw2-prod-frontend-public

Đây chính là giá trị của: module.frontend_label.id

Ngoài ra, module còn cung cấp output tags, chứa các ID elements kết hợp với những Tags được định nghĩa bổ sung.

Ví dụ:

tags = {
  Team        = "Reporting"
  BusinessUnit = "ENG"
}

Những output này có thể được sử dụng trực tiếp trong các Terraform resources hoặc modules. Nhờ vậy, engineer không cần phải tự nhớ và khai báo naming convention cho từng resource.

2.3. Một số tính năng bổ sung

Configurable Delimiters

Có thể thay đổi ký tự phân cách giữa các thành phần.

Mặc định: mp-uw2-prod-frontend-public

Nếu muốn sử dụng dấu _: delimiter = "_"

Kết quả: mp_uw2_prod_frontend_public

Configurable Ordering

Có thể thay đổi thứ tự các thành phần trong ID.

Ví dụ:

label_order = [
  "environment",
  "stage",
  "name",
  "attributes"
]

Cấu hình này sẽ loại bỏ namespace khỏi ID và thay đổi thứ tự các thành phần.

2.4. Lợi ích đối với FinOps

Một trong những lợi ích quan trọng của việc chuẩn hóa naming và tagging là hỗ trợ FinOps. Trong môi trường Cloud có nhiều team và business units, việc xác định chính xác resource nào đang tiêu thụ chi phí là một thách thức lớn. Nếu không có tagging convention thống nhất, rất khó để:

  • Phân bổ chi phí theo team.
  • Xác định chi phí của từng application.
  • Theo dõi mức tiêu thụ tài nguyên, Cost Optimization.

terraform-null-label giúp giải quyết vấn đề này thông qua việc chuẩn hóa Tags.

3. Sử dụng terraform-null-label module trong thực tế

Ví dụ thực tế triển khai một AWS Application Load Balancer (ALB). Thay vì khai báo tên và Tags riêng lẻ cho từng resource, chúng ta tạo một label module trước.

3.1. Khai báo label module

module "public_alb_label" {
  source  = "cloudposse/label/null"
  version = "0.25.0"

  namespace   = "mp"
  stage       = "prod"
  environment = "uw2"
  name        = "alb"
  attributes  = ["public"]

  tags = {
    BusinessUnit       = "ENG"
    Team               = "Reporting"
    ManagedByTerraform = "True"
  }
}

Ở đây, chúng ta định nghĩa naming convention và Tags tại một nơi duy nhất.

3.2. Sử dụng output cho AWS Resources

Sau khi khai báo label module, chúng ta có thể sử dụng output của nó trong các resources khác.

Ví dụ:

resource "aws_lb" "default" {

  name = module.public_alb_label.id

  internal           = false
  load_balancer_type = "application"

  security_groups = [
    aws_security_group.alb_sg.id
  ]

  subnets = [
    "subnet-abc12345",
    "subnet-xyz67890"
  ]

  tags = module.public_alb_label.tags
}

Tương tự, Security Group cũng có thể sử dụng chung naming convention:

resource "aws_security_group" "alb_sg" {
  name        = module.public_alb_label.id
  description = "Allow inbound traffic over HTTPS"
  vpc_id      = "vpc-12345678"

  tags = module.public_alb_label.tags
}

Kết quả:

Cả ALB và Security Group đều sử dụng chung ID: mp-uw2-prod-alb-public

Đồng thời, cả hai đều được gán cùng một bộ Tags. Điểm quan trọng: terraform-null-label không chỉ dành riêng cho AWS. Module này có thể được sử dụng với bất kỳ Terraform Provider nào.

Nó giúp áp dụng nguyên tắc:

  • Write once, read many.
  • DRY (Don't Repeat Yourself).
  • Reusability.
  • Consistency.

3.3. Sử dụng Context để tái sử dụng cấu hình

Đây là một trong những tính năng quan trọng nhất của terraform-null-label.

Hãy tưởng tượng chúng ta cần triển khai thêm một Internal Load Balancer bên cạnh Public Load Balancer hiện tại.

Hai resources này có phần lớn các thông tin naming giống nhau:

Thành phần Public ALB Private ALB
Namespace mp mp
Stage prod prod
Environment uw2 uw2
Name alb alb
Attributes public private

Nếu không sử dụng context, chúng ta phải khai báo lại toàn bộ những giá trị này trong module mới. Điều đó tạo ra sự trùng lặp không cần thiết.

Giải pháp: sử dụng context.

Ví dụ:

module "public_alb_label" {
  source  = "cloudposse/label/null"
  version = "0.25.0"

  namespace   = "mp"
  stage       = "prod"
  environment = "uw2"
  name        = "alb"
  attributes  = ["public"]

  tags = {
    BusinessUnit       = "ENG"
    Team               = "Reporting"
    ManagedByTerraform = "True"
  }
}
module "private_alb_label" {
  source  = "cloudposse/label/null"
  version = "0.25.0"

  attributes = ["private"]

  context = module.public_alb_label.context
}

Điểm quan trọng nằm ở: context = module.public_alb_label.context

Thay vì khai báo lại toàn bộ thông tin, chúng ta truyền context từ module đầu tiên sang module thứ hai. Module thứ hai sẽ kế thừa các giá trị ban đầu và chỉ override những giá trị được khai báo mới.

Kết quả:

Public ALB

  • module.public_alb_label.id
  • mp-uw2-prod-alb-public

Private ALB

  • module.private_alb_label.id
  • mp-uw2-prod-alb-private

Các Tags cũng được kế thừa.

Ví dụ, Private ALB vẫn giữ nguyên:

  • Namespace: mp
  • Environment: uw2
  • Stage: prod
  • Name: alb
  • BusinessUnit: ENG
  • Team: Reporting
  • ManagedByTerraform: True

Chỉ có Attributes thay đổi từ public thành private. Như vậy, chúng ta không cần khai báo lại các giá trị đã tồn tại.

4. Tổng kết

terraform-null-label là một trong những giải pháp tốt để giải quyết bài toán chuẩn hóa naming convention và tagging trong một tổ chức có quy mô infrastructure lớn.

Các lợi ích chính:

  1. Consistency: Đảm bảo tất cả resources sử dụng cùng một naming convention.
  2. Reusability: Tái sử dụng cấu hình giữa nhiều Terraform modules.
  3. Maintainability: Giảm việc phải khai báo và quản lý naming convention ở nhiều nơi.
  4. Reduced Cognitive Load: Giảm thời gian và công sức engineer phải bỏ ra để ghi nhớ naming convention.
  5. Cost Management: Giúp FinOps và engineering teams phối hợp quản lý Cloud costs.
  6. Security: Hỗ trợ quản lý, phân loại và nhận diện resources tốt hơn, giảm nguy cơ bỏ sót các resources không được quản lý.

Ngoài ra, module còn có những cách triển khai nâng cao thông qua context.tf, đặc biệt hữu ích khi làm việc với nhiều Root Modules, Child Modules và Terraform Workspaces.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.