Комментарии 5
Спасибо за добротный разбор.
Я видел еще пару интересных концепций:
Использование дефолтного объекта. Например, параметры ami, instance_type, create_before_destroy и tag можно собрать в одну переменную default_instance. Теперь мы все эти параметры можем как переопределять, так и вовсе не указывать (хотя для читаемости параметр instance_type можно и оставить). Однако это может нарушать принципы, изложенные в п.1.11 статьи.
Hidden text
variable "instances" {
description = "Instances with parameters"
type = map(object({
# instance_type = string
instance_name = string
idx = number
}))
}
variable "default_instance" {
description = "Default Instance parameters"
type = object({
instance_type = string
ami = string
create_before_destroy = bool
tags = map(string)
})
}
# default_instance = {
# instance_type = "t2.micro"
# ami = "ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"
# create_before_destroy = true
# tags = { lb = "nlb" }
# }
# instances = {
# instance1 = { instance_name = "instance-1", idx = 0 }
# instance2 = { instance_name = "instance-2", idx = 1 }
# }
locals {
instances = { for k, v in var.instances: k => merge(var.default_instance, v) }
}
resource "aws_instance" "instance" {
for_each = local.instances
ami = data.aws_ami.ubuntu.id
instance_type = each.value.instance_type
subnet_id = data.aws_subnets.subnets.ids[each.value.idx]
iam_instance_profile = aws_iam_instance_profile.instance_profile.name
lifecycle {
create_before_destroy = each.value.create_before_destroy
}
tags = merge(each.value.tags, { Name = each.value.instance_name })
}
Использование yaml файлов для задания переменных. Если требуется использовать один конфиг для нескольких инструментов (например, для terraform и ansible или helm), то вместо *.auto.tfvars может подойти более универсальный формат. Код terraform при этом не сильно усложняется
Hidden text
default_instance:
instance_type: t2.micro
ami: "ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"
create_before_destroy: true
tags:
lb: nlb
instances:
instance1:
instance_name: instance-1
idx: 0
instance2:
instance_name: instance-2
idx: 1
# variable "config_path" {
# description = "Path to yaml config"
# path = string
# }
# locals {
# config = yamldecode(file(${var.config_path}"))
# instances = local.config.instances
# }
1.3. Использование нижнего подчеркивания "_" вместо тире "-" в наименовании module, resource, data source, variable, output, etc.
К сожалению, этот пункт никак не прокомментирован. Зачем всё же у объекта и управляющего им ресурса делать разные имена, ещё и сильно похожие? :)
Спасибо за бестррактис. Неплохо бы дополнить это репом с пуликом на рефакторинг для наглядности
Все вышеперечисленные аргументы не относятся только к преимуществам Terraform, скорее больше в целом к преимуществам IaC. Да и у Terraform существуют аналоги, например, Pulumi, CloudFormation для AWS. Поэтому, если вы используете любой другой аналог Terraform, который поддерживает концепцию IaC, это уже хорошо, и однозначно лучше, чем "натыкивать" инфраструктуру руками.
Между Terraform и Pulumi есть кардинальное концептуальное отличие. Terraform - это декларативная портянка, в которой придется на еще одном декларативном синтаксисе описывать километровые конфигурационные файлы. Это тоже самое, что CloudFormation Template в AWS.
Pulumi (и родной AWS CDK) - это модель описания инфраструктуры реальным кодом (обычно с поддержкой нескольких ЯП), где портянка из п.1 авто-генерируется на основе написанного кода.
Любой, кто работал с CFN Templates на реальной инфраструктуре, больше к декларативному подходу возвращаться не захочет.
Мы в компании давно выбрали CDK и ни разу не пожалели. Рекомендую всем изучить этот вопрос, и четко ответить на вопрос - нужен ли вам Тераформ.
Кстати HashiCorp осознали фатальный недостаток декларативных портянок и тоже делают https://github.com/hashicorp/terraform-cdk. Можно его тоже рассмотреть.
Terraform: поиск оптимального написания кода