Прошло уже почти 5 лет с тех пор, как я избрал Terraform в качестве одного из основных наборов инструментов DevOps и облачной инженерии для управления сервисами моих проектов на AWS, Azure, OpenStack, Github и Datadog. Его очень удобно использовать благодаря простому и понятному человеку синтаксису, а также огромному количеству модулей в реестре HashiCorp. Кроме того, множество поддерживаемых сервисов стали очень популярными среди DevOps и Cloud инженеров, ведь у вас в итоге может быть всего один инструмент для выполнения всех ваших ежедневных IaC рутин.
В этой статье я поделюсь своим опытом в отношении соглашений об именовании при написании стандартных блоков кода в Terraform. Некоторые из них стремятся улучшить стандартный файл Terraform, а некоторые необходимо учитывать из-за скрытых ограничений в соглашениях об именовании в API поставщиков, таких как AWS и Azure.
Общие соглашения
Всегда используйте
_
(нижнее подчеркивание) вместо-
(тире)
resource "aws_db_instance" "dev_db" {
...
name = "backend_db_instance"
...
}
Используйте только буквы в нижнем регистре и цифры:
resource "aws_key_pair" "ops" {
key_name = "roozbeh_key_1"
public_key = "ssh-rsa ..."
}
Соглашения о ресурсах и источниках данных
Не дублируйте тип ресурса в имени ресурса (ни частично, ни полностью):
resource "aws_route_table" "public" {
vpc_id = aws_vpc.example.id
...
}
Имя ресурса должно быть проименовано
this
, если не удается подобрать более описательного и общего имени:
resource "aws_nat_gateway" "this" {
allocation_id = aws_eip.nat.id
subnet_id = aws_subnet.example.id
...
}
В качестве имен всегда используйте существительные в единственном числе:
resource "aws_eip" "loadbalancer" {
instance = aws_instance.web.id
vpc = true
}
Использование — внутри значений аргументов и в местах, где значение будет продемонстрировано человеку (например, внутри DNS-имени инстанса RDS или записи Route 53):
resource "aws_route53_record" "www" {
zone_id = aws_route53_zone.primary.zone_id
name = "www.example-domain.com"
type = "A"
ttl = "300"
records = [aws_eip.lb.public_ip]
}
Добавляйте аргумент
count
в самый верх блока ресурсов и отделяйте его от остального новой строкой для наглядности:
resource "aws_instance" "web" {
count = "5"
...
}
Добавляйте аргумент
tags
, если он поддерживается ресурсом, в качестве последнего реального аргумента, после которого следуютdepends_on
иlifecycle
при необходимости. Все они должны быть разделены одной пустой строкой:
resource "aws_nat_gateway" "this" {
count = "1"
...
tags = {
Name = "..."
}
depends_on = ["aws_internet_gateway.this"]
lifecycle {
create_before_destroy = true
}
}
При использовании условия в аргументе
count
по возможности используйте какое-либо логическое значение, в противном случае используйтеlength
или другую интерполяцию:
count = "${length(var.public_subnets) > 0 ? 1 : 0}"
Чтобы сделать инвертированные условия, не создавайте новую переменную, если в этом нет реальной необходимости, используйте вместо этого
1 - boolean value
:
count = "${1 - var.create_public_subnets}"
Соглашения о переменных
Не изобретайте колесо в ресурсных модулях — используйте те же имена переменных, описание и значение по умолчанию как определено в разделе «Argument Reference» для ресурса, с которым вы работаете.
Используйте объявление
type = "list"
, еслиdefault = []
:
variable "availability_zone_names" {
type = list(string)
default = [
"eu-central-1a"
"eu-central-1b"
"eu-central-1c"
]
}
Используйте объявление
type = "map"
, еслиdefault = {}
:
variable "images" {
type = "map"
default = {
eu-central-1 = "image-1234"
eu-west-1 = "image-4567"
}
}
Используйте множественное числа в именах переменных типа
list
иmap
:
variable "users" {
type = "list"
...
}
variable "images" {
type = "map"
...
}
При определении переменных порядок следующий:
description
,type
,default
. Всегда добавляйтеdescription
для всех переменных, даже если вы думаете, что это очевидно.
variable "key" {
description = "description"
type = "string"
default = "value"
}
Выводы (Outputs)
Важно именовать выводы таким образом, чтобы сделать их единообразными и понятными за пределами их области действия (когда пользователь использует модуль, должно быть очевидно какой тип и атрибут значения возвращаются).
Общая рекомендация для именования выводов состоит в том, что они должны быть достаточно описательными для значения, которое они содержат, и иметь более лаконичную форму, чем вы обычно хотели бы.
Хорошая структура для имен вывода выглядит так -
{имя}_{тип}_{атрибут}
, где:
1. {имя}
- имя ресурса или источника данных без префикса поставщика. {имя}
для aws_subnet
- это subnet
, а для aws_vpc
- vpc
.
2. {тип}
- это тип источника ресурса.
3. {атрибут}
является атрибутом возвращаемого вывода.
Если выход возвращает значение с интерполяционными функциями и множеством ресурсов,
{имя}
и{тип}
должны быть максимально стандартизованы (this
зачастую является наиболее общим и поэтому предпочтительным).
Если возвращаемое значение является списком, оно должно иметь имя во множественном числе.
Всегда включайте description для всех результатов, даже если вы думаете, что это очевидно.
Материал подготовлен в рамках курса «Infrastructure as a code». Если вам интересно узнать подробнее о формате обучения и программе, познакомиться с преподавателем курса — приглашаем на день открытых дверей онлайн. Регистрация здесь.
Комментарии (9)
aelaa
07.08.2021 10:52+1Имя ресурса должно быть проименовано
this
Ну давайте еще переменные в коде this называть. Oh, wait...
Если я не могу придумать назначение ресурсу, на основе которого составляется название - я задумываюсь а нужен ли он мне. Хороший способ не добавлять лишнего
Для nat_gateway веселье начнется, когда появится второй. Назовем его that?
ghostinushanka
07.08.2021 11:43Нет, вы переименуете первый и обоим дадите соответствующие названия по смыслу. Это как-раз таки нормальная практика, даже без «best practice» советов.
Мы до этого сами дошли ещё до того, как модули Антона или книгу Евгения прочитали в своё время. Только вместо «this» мы их «default» называем.aelaa
07.08.2021 11:51На меня упадет кирпич, и никто это не будет переименовывать.
Мы называем main, чтобы в случае появления второго можно было уточнить, и оба имени были понятными
ghostinushanka
То что вы перевели, наглая копипаста с гитбука Антона Бабенко — www.terraform-best-practices.com/naming, причём на хабре об уже была публикация, гораздо более подробная habr.com/en/post/517804.
Пиар он такой…
aelaa
Не читал ни Антона, ни оригинал, но правила как-то сами организовались примерно такие же. Может они достаточно самоочевидные?
ghostinushanka
Тут дело не в самоочевидности, а в том что «оригинал» имеет в себе целые секции, которые «выделили, скопировали и вставили» 1 в 1.
Ну и например называть ресурсы «this» это вам самоочевидно настолько, что вы до этого сами додумались в разрез в документацией Hashicorp где в примерах эта конструкция не используется вообще ни разу?
aelaa
про this я как раз упомянул ниже) Я только про то, что правила вполне выводятся сами, не потому что кто-то сказал
А с прямым переводом нисколько не спорю