Мы все были там: вы написали конфигурацию Terraform, которая настолько потрясающая, что вам приходится применять ее несколько раз… Или, по крайней мере, в dev
, test
и prod
. Какой бы замечательной ни была ваша конфигурация, очень редко бывает так, чтобы все детали были абсолютно одинаковыми в каждой среде. Обычно в итоге получается что-то вроде этого, и это совершенно нормально (для удобства чтения я немного сократил код, более полный пример смотрите в этом gist):
resource "aws_instance" "this" {
count = var.instance_count
ami = var.ami_id
instance_type = var.instance_type
tags = {
Name = "project-xyz-${terraform.workspace}-${count.index}"
}
}
Где это начинает вредить, так это во время применения, потому что у вас будет, например:
terraform workspace select prod
terraform apply -var='instance_count=5' -var='ami_id=ami-1a2b3c' -var='instance_type=t2.large'
Эта строка переменных становится непонятной очень быстро. Кроме того, запоминать все эти переменные или заставлять командную строку запоминать утверждение (для каждого окружения) довольно обременительно. Более того, чтобы заставить это работать в автоматизации (опять же, для каждого окружения…), скорее всего, потребуется некоторая магия переменных env
. Это отделяет определение вашей инфраструктуры от стандартных способов ее вызова, что плохо. (Кстати, если вы никогда не слышали о connascence, прекратите читать эту статью и прочитайте эту).
Terraform Cloud предлагает некоторое исправление этого, но и здесь вы создаете разделение между шаблонами и наборами переменных env
. Было бы гораздо лучше, если бы мы могли держать наборы переменных по умолчанию в контроле версий рядом с нашей конфигурацией. После некоторых попыток я нашел следующее решение:
variable "workspace_variables" {
type = map(object({
instance_count = number
ami_id = string
instance_type = string
}))
default = {}
}
locals {
default_vars = lookup(var.workspace_variables, terraform.workspace, {
instance_count = 2
ami_id = "ami-abc123"
instance_type = "t2.micro"
})
instance_count = var.instance_count != null ? var.instance_count : local.default_vars["instance_count"]
ami_id = var.ami_id != null ? var.ami_id : local.default_vars["ami_id"]
instance_type = var.instance_type != null ? var.instance_type : local.default_vars["instance_type"]
}
Этот фрагмент кода считывает одну необязательную переменную, чтобы править ими всеми: workspace_variables
. Если она не указана, то будет использоваться набор подпеременных по умолчанию. Кроме того, вы можете переопределить любой набор переменных с помощью Terraform cli. Далее в этом фрагменте вам нужно сделать значение по умолчанию для всех переменных null
и при их использовании заменить var.
на local.
. Затем вы можете создать в Terraform файл vars примерно следующего содержания:
workspace_variables = {
dev = {
instance_count = 2
ami_id = "ami-abc123"
instance_type = "t2.micro"
}
test = {
instance_count = 3
ami_id = "ami-a1b2c3"
instance_type = "t2.medium"
}
prod = {
instance_count = 5
ami_id = "ami-1a2b3c"
instance_type = "t2.large"
}
}
Намного понятнее и все хранится в одном доступном месте, которое можно поместить в систему контроля версий! И вы получите нужную вам конфигурацию с помощью простого потока Terraform — выбрать рабочее пространство и нажать кнопку apply. Кроме того, вы все еще можете переопределить переменные с помощью командной строки для горячих исправлений, тестов и т.д.:
terraform workspace select prod
terraform apply -var='ami_id=ami-x1337x'
Это может показаться слишком много шаблонов для простого ввода параметров, но я обнаружил, что при большом количестве переменных это весит больше, чем установка переменных «обычным» способом. Проверьте, работает ли это для вас!
Последнее замечание: для реализации правильной проверки переменных в Terraform и совместимости с файлом vars потребуется еще немного доработки, но это может быть исправлено, когда опциональные атрибуты переменных выйдут из публичной бета-версии.
(Приведенный выше код сокращен для удобства чтения, более полный пример см. в этом gist).