DRY Configuration với Terragrunt: include, generate và read_terragrunt_config
Trong bài trước, chúng ta đã xây dựng một repository Terragrunt theo chuẩn production.
Repository được chia thành:
- Modules
- Live Infrastructure
- Environment
- Terraform Projects
Nhưng vẫn còn một câu hỏi quan trọng:
Làm thế nào để hàng trăm Terraform project có thể chia sẻ cùng một cấu hình mà không cần copy & paste?
Đây chính là lúc Terragrunt thể hiện sức mạnh thực sự.
Ba tính năng quan trọng nhất của Terragrunt là:
includegenerateread_terragrunt_config
Ba tính năng này là nền tảng giúp Terragrunt loại bỏ gần như toàn bộ cấu hình bị lặp lại trong một repository lớn.
Vấn đề của Terraform
Giả sử bạn có 40 Terraform project.
Mỗi project đều cần:
- Backend
- Provider
- Default Tags
- Terraform Version
- AWS Region
- Common Variables
Nếu chỉ dùng Terraform, bạn sẽ viết lại:
terraform {
required_version = ">= 1.10"
}Ở project thứ hai.
Lại viết tiếp.
Project thứ ba.
Tiếp tục.
Provider cũng vậy.
provider "aws" {
region = "ap-southeast-1"
default_tags {
tags = {
ManagedBy = "Terraform"
}
}
}Backend cũng vậy.
Version cũng vậy.
Sau vài tháng, repository đầy những đoạn cấu hình giống hệt nhau.
Nếu muốn đổi region?
Bạn phải sửa hàng chục file.
Đó là điều Terragrunt muốn giải quyết.
include — Kế thừa cấu hình
Đây là tính năng được sử dụng nhiều nhất.
Hãy tưởng tượng cấu trúc:
live/
terragrunt.hcl
production/
terragrunt.hcl
eks/
terragrunt.hcl
File root:
live/
terragrunt.hcl
Định nghĩa:
- backend
- provider
- common locals
- remote state
- tags
Các project con chỉ cần:
include "root" {
path = find_in_parent_folders()
}Terragrunt sẽ tự động kế thừa toàn bộ cấu hình.
Điều này rất giống với inheritance trong lập trình hướng đối tượng.
include hoạt động như thế nào?
Giả sử:
Root
↓
Production
↓
EKS
Root chứa:
Backend
Provider
Tags
Production chứa:
Environment
AWS Account
EKS chứa:
Cluster Version
Node Size
Sau khi Terragrunt resolve toàn bộ include, EKS sẽ nhìn thấy tất cả:
Backend
+
Provider
+
Tags
+
Environment
+
Cluster Version
Mỗi tầng chỉ cần khai báo phần cấu hình thực sự khác biệt.
include không phải copy file
Điều quan trọng cần nhớ là:
Terragrunt không copy file.
Nó xây dựng một cây cấu hình.
Ví dụ:
Root
↓
Production
↓
VPC
Khi chạy:
terragrunt applyTerragrunt sẽ:
- đọc Root
- đọc Production
- đọc VPC
- merge tất cả lại
Sau đó mới sinh Terraform configuration.
Điều này giúp repository luôn có một nguồn cấu hình duy nhất.
generate — Sinh Terraform Configuration
Một trong những tính năng thú vị nhất của Terragrunt là generate.
Giả sử mọi project đều cần:
provider "aws" {
...
}Bạn hoàn toàn có thể để Terragrunt sinh file này.
Ví dụ:
provider.tf
được generate tự động.
Điều đó có nghĩa:
Trong repository, bạn sẽ không còn nhìn thấy provider nữa.
Terragrunt sẽ tạo nó trước khi Terraform chạy.
Điều này thường được dùng cho:
- provider
- backend
- versions.tf
- provider aliases
- common Terraform settings
Vì sao generate hữu ích?
Giả sử công ty quyết định:
Chuyển toàn bộ provider sang AWS Provider phiên bản mới.
Nếu dùng Terraform thuần:
Bạn sửa hàng trăm file.
Nếu dùng Terragrunt:
Bạn sửa đúng một nơi.
Lần chạy tiếp theo, tất cả project đều nhận được provider mới.
Đây chính là sức mạnh của việc sinh cấu hình.
read_terragrunt_config
Đây là tính năng ít được nhắc tới hơn.
Nhưng cực kỳ hữu ích.
Giả sử bạn có:
account.hcl
Chứa:
- Account ID
- Region
- Common Tags
Một project khác có thể đọc:
account.hcl
và sử dụng toàn bộ giá trị bên trong.
Điều này giúp chia sẻ configuration giữa nhiều project mà không cần duplication.
Cấu hình theo từng tầng
Một repository Terragrunt thường được chia thành nhiều tầng.
Ví dụ:
Root
Chứa:
- backend
- provider
- common locals
Production
Chứa:
- account
- region
- environment
Application
Chứa:
- application name
- common variables
EKS
Chứa:
- cluster version
- instance types
Nhờ đó, mỗi tầng chỉ chịu trách nhiệm cho đúng phạm vi của mình.
Đây là một nguyên tắc rất quan trọng khi thiết kế repository.
Một project chỉ còn vài dòng
Sau khi tận dụng ba tính năng trên, một project Terragrunt có thể chỉ còn:
include "root" {
path = find_in_parent_folders()
}
terraform {
source = "../../../modules/eks"
}
inputs = {
cluster_name = "production"
}Đó gần như là toàn bộ cấu hình.
Không còn:
- backend
- provider
- version
- remote state
- default tags
Mọi thứ đều được kế thừa hoặc generate tự động.
Đây mới là DRY thực sự
Nhiều người nghĩ DRY chỉ là dùng Terraform Module.
Thực tế:
Terraform Module chỉ giúp tái sử dụng resource.
Terragrunt giúp tái sử dụng:
- configuration
- repository structure
- backend
- provider
- variables
- environment settings
Hai công cụ giải quyết hai bài toán hoàn toàn khác nhau.
Có nên lạm dụng include?
Không.
Một repository có thể có:
Root
↓
Organization
↓
Production
↓
Application
↓
Service
Nếu kế thừa quá nhiều tầng, việc tìm nguồn gốc của một biến sẽ trở nên khó khăn.
Nguyên tắc chung là:
- Chia tầng hợp lý.
- Không tạo hierarchy quá sâu.
- Mỗi tầng chỉ chứa đúng trách nhiệm của nó.
Repository sẽ dễ hiểu hơn rất nhiều.
Tổng kết
Ba tính năng:
includegenerateread_terragrunt_config
chính là "linh hồn" của Terragrunt.
Chúng giúp:
- Loại bỏ phần lớn cấu hình lặp lại.
- Chuẩn hóa repository.
- Quản lý hàng trăm Terraform project.
- Chia sẻ configuration giữa nhiều environment.
- Dễ bảo trì và mở rộng trong thời gian dài.
Nếu chỉ học một phần của Terragrunt, thì đây chính là phần quan trọng nhất.
Bài tiếp theo
Ở bài tiếp theo, chúng ta sẽ tìm hiểu Dependency Management trong Terragrunt, cách Terragrunt tự động xây dựng dependency graph giữa các Terraform project và giúp chúng được triển khai theo đúng thứ tự mà không cần ghi nhớ hay thực hiện thủ công.
