زیرساخت به عنوان کد (IaC) اغلب شامل مدیریت منابعی است که پیکربندی آنها در زمان کامپایل ثابت نیست. چه در حال تأمین گروههای امنیتی AWS باشید، چه پیکربندی استقرار Kubernetes یا تنظیم رابطهای شبکه Azure، تعداد بلوکهای تو در تو یا آیتمهای لیست اغلب متفاوت است. کدنویسی سختافزاری (Hardcoding) این پیکربندیها منجر به کدهای تکراری و کابوسهای نگهداری میشود. اینجاست که بلوکهای دینامیک ترافورم و متا-آرگومان for_each درخشش خود را نشان میدهند.
برای توسعهدهندگان متوسط تا پیشرفته، تسلط بر این دو ویژگی برای نوشتن کد HCL (زبان پیکربندی HashiCorp) تمیز، مقیاسپذیر و قابل نگهداری ضروری است. این پست به بررسی الگوهای پیشرفته برای ترکیب این ابزارها جهت مدیریت مجموعههای پیچیده با طول متغیر میپردازد.
درک بلوکهای سازنده
قبل از غوطهور شدن در الگوهای پیشرفته، بیایید تمایز آنها را به طور خلاصه روشن کنیم. for_each یک متا-آرگومان است که میتواند روی منابع یا ماژولها استفاده شود تا چندین نمونه از یک منبع را بر اساس یک نقشه یا مجموعهای از رشتهها ایجاد کند. این روش برای ایجاد منابع در سطح بالا ایدهآل است.
از طرف دیگر، بلوکهای دینامیک به شما امکان میدهند بلوکهای تو در تو (مانند ingress در یک گروه امنیتی) را به صورت پویا در داخل یک منبع تولید کنید. آنها روی یک لیست یا نقشه تکرار شده و پیکربندی بلوک متناظر را برای هر عنصر تزریق میکنند.
الگوی ۱: بلوک دینامیک تو در تو
رایجترین مورد استفاده برای بلوکهای دینامیک، تولید پیکربندیهای تو در تو است. یک aws_security_group AWS را در نظر بگیرید. میخواهید قوانین ingress را بر اساس یک متغیر تعریف کنید، اما تعداد قوانین نامشخص است. با استفاده از یک بلوک دینامیک، میتوانید روی یک لیست از نقشهها تکرار کنید.
variable "ingress_rules" {
type = list(object({
from_port = number
to_port = number
protocol = string
cidr_blocks = list(string)
}))
default = []
}
resource "aws_security_group" "example" {
name = "example-sg"
dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.from_port
to_port = ingress.value.to_port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
}
# Usage in main.tf
resource "aws_security_group" "web_sg" {
ingress_rules = [
{
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
},
{
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"]
}
]
}
این الگو تعاریف متغیر شما را تمیز نگه میدارد و به زیرساخت شما اجازه میدهد تا به محض اضافه شدن قوانین جدید به لیست ورودی، به سرعت سازگار شود.
الگوی ۲: ترکیب for_each و بلوکهای دینامیک
سناریوهای پیشرفته اغلب نیاز به ایجاد چندین منبع دارند که هر کدام مجموعه خاص خود را از بلوکهای دینامیک تو در تو دارند. برای مثال، ممکن است چندین سرویس برنامه را مستقر کنید که هر کدام متغیرهای محیطی یا پورتهای کانتینر خاصی دارند.
با ترکیب for_each روی منبع و بلوکهای dynamic در داخل منبع، به انعطافپذیری بالایی دست مییابید.
variable "services" {
type = map(object({
name = string
ports = list(object({
containerPort = number
protocol = string
}))
}))
}
resource "aws_lb_target_group" "this" {
for_each = var.services
name = each.key
port = 80
protocol = "HTTP"
# Note: Dynamic blocks inside target groups are limited,
# but this pattern applies to resources like aws_instance or aws_lb_listener
tags = {
Name = each.key
}
}
در حالی که مثال بالا سادهسازی شده است، یک پیادهسازی پیچیدهتر شامل استفاده از for_each برای راهاندازی ماژولها یا منابع متمایز است، جایی که هر نمونه یک نقشه پیکربندی خاص را دریافت میکند که بلوکهای دینامیک داخلی آن را هدایت میکند.
بهترین شیوهها و ملاحظات عملکرد
هنگام مدیریت مجموعههای با طول متغیر، نکات زیر را در نظر داشته باشید:
- ثبات کلیدها: هنگام استفاده از
for_each، مطمئن شوید که کلیدهای شما پایدار هستند. تغییر یک کلید در یک نقشه باعث میشود ترافورم به جای بهروزرسانی، منبع را از بین ببرد و دوباره ایجاد کند. - سادهسازی متغیرها: از انواع متغیر ساختاریافته (نقشههایی از اشیاء یا لیستهایی از اشیاء) استفاده کنید تا ورودیها را در مراحل اولیه اعتبارسنجی کنید. این کار از خطاهای زمان اجرا جلوگیری میکند زمانی که بلوکهای دینامیک سعی میکنند به ویژگیهای تعریف نشده دسترسی پیدا کنند.
- خوانایی: اگرچه قدرتمند هستند، اما تو در تو شدن بیش از حد بلوکهای دینامیک میتواند کد را به سختی خواند. اگر منطق بسیار پیچیده شد، پیکربندیهای پیچیده را به مقادیر محلی یا ماژولهای جداگانه استخراج کنید.
نتیجهگیری
بلوکهای دینامیک و for_each فقط ویژگیهای راحتی نیستند؛ آنها ابزارهای اساسی برای ساخت زیرساختهای قوی و بومی ابری با ترافورم هستند. با بهرهگیری از این الگوهای پیشرفته، میتوانید از تکرار کد جلوگیری کنید، خطر انحراف پیکربندی را کاهش دهید و زیرساختی ایجاد کنید که به همان اندازه که برنامههای شما مقیاسپذیر هستند، به زیبایی مقیاس میپذیرد. از امروز شروع به بازنگری بلوکهای کدنویسی شده سخت کنید تا پتانسیل کامل استراتژی زیرساخت به عنوان کد خود را آزاد کنید.