Работа с Terraform: эмпирические правила

—

от автора

Примерно с 2018 года я занимаюсь программированием инфраструктуры если не каждый день, то несколько раз в неделю. Я не утверждаю, что это позволяет мне претендовать на какой-то авторитет. Но за это время я определённо успел сформировать конкретные мнения по некоторым вопросам. В этой статье поделюсь некоторыми этими наблюдениями в качестве эмпирических правил, которым пытаюсь следовать при разработке для Terraform, но с тем же успехом вы можете применять их и в других языках для программирования инфраструктуры.

1. Не жертвуйте локальной разработкой

Это настоящий флагман, так что, если бы мне потребовалось оставить всего один пункт, то я оставил бы этот!

Как подсказывает мой опыт, такое жертвование выражается в двух формах. Во-первых, допускаются жёсткие зависимости от среды, в которой выполняются конвейеры. Например, вместо нормальных переменных Terraform используются инструменты замены токенов, либо общие модули копируются в каталог для развёртывания прямо во время выполнения. Это случается, если Terraform развёртывают с единственной целью — использовать его в конвейере.

На самом деле, конвейеры нужны для развёртывания изменений в реальных окружениях. Но для этого вы не должны возводить такую систему, в которой инженер не смог бы прямо у себя на компьютере тестировать и отлаживать создаваемую систему — полностью или частично. Если система запускается локально, то она может запускаться и в конвейере, но обратное верно не всегда. Не допускайте, чтобы git commit && git push && *вечно дожидаться, пока конвейер сообщит вам об опечатке* остался единственным способом протестировать изменение.

Другой вариант полного отказа от локальной разработки — злоупотребление предусмотренными в terraform обобщёнными словарями (maps) применяя их в качестве модульных переменных. Если модуль рассчитан на то, что всё его содержимое будет передаваться в map(string) —  а мне такое попадалось достаточно часто — то все, кто имеет дело с такими переменными, как разработчики, так и пользователи, работают вслепую, без всяких подсказок типов на уровне IDE или валидации. Такой подход грубо уродует старую добрую привычную разработку на локальной машине.

Иногда без динамических словарей никак не обойтись — например, при работе с метками ресурсов. Но, если вы знаете, какие параметры понадобятся вашему модулю — определите их как отдельные переменные или как объект с известными атрибутами.

### Не делайте так  variable "subnet_config" { type = map(string) }  resource "subnet" "mySubnet" { name = var.subnet_config["name"] ... }  ### А делайте так  variable "subnet_config" { type = object({ name = string cidr = string

В первом примере мы летим вслепую. Во втором примере нам в качестве второго пилота помогает terraform-подобная система (нет, это не разновидность copilot). Если вам это кажется настолько очевидным, что даже в пояснении не нуждается, поверьте мне — нуждается.

2. Учитывайте, кто ваша аудитория

При разработке модуля terraform держите в уме, для кого он делается. Иногда целевая аудитория – это инженеры платформ или инфраструктурные инженеры, а эти люди зачастую ценят возможность видеть систему в мельчайших деталях и контролировать их конфигурацию. Если скрывать от пользователей эти детали, им это может не понравиться.

Программисты, в свою очередь, ценят возможность максимально быстро поднять и запустить инфраструктуру, так, чтобы при этом приходилось принимать как можно меньше решений. Ваш модуль будет пользоваться повышенной популярностью, если вы сможете минимизировать время, в течение которого программисту придётся отвлекаться от повседневной работы. Для таких людей старайтесь, насколько это возможно, ограничить обязательные параметры, а для всех остальных параметров предусмотрите разумные умолчания.

3. Старайтесь при помощи модулей снижать сложность распространённых паттернов

«Трамбовка сложности» (compressing complexity) — это термин, подсмотренный в сообществе Ruby on Rails. Сам приём — это вариант создания сбалансированных абстракций, которые не скрывают сложность, но всё равно не слишком забивают голову человеку, которому приходится ими управлять. Применительно к модулям terraform такая практика позволяет минимизировать обязательные параметры, но в то же время в достаточном количестве предоставить опциональные параметры с разумными и осознанно выбранными умолчаниями. Если в модуле хорошо утрамбована сложность, то разработчику легко приступить к работе с ним, поскольку не приходится досконально разбираться в модуле. Тем не менее, сохраняется возможность переопределить умолчания, если понадобится более полный контроль над модулем. .

Умение подходить к работе с модулями именно таким образом пригодится, когда вы приступите к компоновке ресурсов и других модулей в более высокоуровневые пакеты. В таких составных модулях могут содержаться полезные абстракции, описывающие паттерны платформ, имеющие смысл именно в той предметной области, в которой вы работаете. Грегор Хоп  отлично объяснил эту концепцию на следующем примере из финансового сектора. Пользователь определяет базу данных ledgered-database, в которой компонуется и конфигурируется ряд AWS-сервисов, которые, в свою очередь, хорошо поддаются повторному использованию. Пользователям такого модуля ничего не требуется знать о конкретной базе данных или технологиях маршрутизации событий — и, тем не менее, они могут быстро внедрить такой функционал в своё приложение. В качестве других примеров можно привести

  • feature-store для управления признаками при машинномобучении и для предоставления их. Это могут быть, в том числе, сервисы для хранения данных, обеспечения работы конвейеров, версионирования и хостинга API.

  • workflow-orchestrator для определения потоков бизнес-задач и управления ими. В том числе, это могут быть сервисы для управления очередями, срабатываниями, состоянием инаблюдаемостью.

  • policy-enforced-api для хостинга защищённых сервисов API, в том числе, шлюза API,аутентификации, соответствия политикам(например, политикам ограничения частотыпередачи), мониторинга, шифрования и соответствия стандартам.

Модули такого рода очень ценны, поэтому не упускайте возможностей их создавать. Но учитывайте, что они полезны лишь в случае, если активно используются — так что вот вам ещё одна причина тщательно продумать, как их будет воспринимать пользователь. Вы ведь не хотите попасть в ловушку конкурирующих стандартов!

4. Структурируйте код terraform с применением модулей, стеков и единиц развёртывания

Здесь я перехожу к настоятельным предписаниям. По мере роста вашей базы кода на terraform приходится задумываться о том, как ее структурировать. Эта структура базируется на трёх каталогах верхнего уровня и, по моему опыту, по-настоящему хорошо работает. Опишу эти каталоги:

  • modules: содержит небольшие модули, описывающие отдельные ресурсы или комбинации отдельных ресурсов, обеспечивающих, например, передачу данных в частнойсети или управление учётными данными.

  • stacks: содержит относительно крупные модули, в которых из мелких модулей составляются решения (именно здесь вам может встретиться один из тех высокоуровневыхмодулей, о которых шла речь в предыдущем разделе). В стеках заключена сложность отношений между различными ресурсами, а также часто содержится код, упрощающий взаимодействие между этими ресурсами. Например, это может быть код для создания секретов, присваивания ролей, т.п. Также в стеках контролируется, какие вариациидоступны в разных окружениях —предоставляется ровно столько переменных, сколько нужно.

  • deployments: потребляет стеки и представляет отдельные инстансы или окружения конкретного стека. Именно здесь вы найдете конфигурации backend и provider дляterraform, а также любые детали, которые могут отличаться от окружения к окружению (например, имена или размеры параметров).

В итоге у вас получится примерно такая структура:

terraform/ ├── modules/ │   ├── app_service/ │   ├── sql_database/ │   ├── storage_account/ │   ├── virtual_network/ │   └── key_vault/ ├── stacks/ │   ├── app1/ │   └── app2/ │   └── shared_service/ ├── deployments/ │   ├── app1/ │   │   ├── dev/ │   │   ├── test/ │   │   └── prod/ │   └── app2/ │       ├── dev/ │       ├── test/ │       └── prod/ │   └── shared_service/ │       ├── dev/ │       ├── test/ │       └── prod/

А вот наглядное представление:

Обратите внимание: недавно компания Hashicorp а режиме бета-тестирования выкатила для своего облачного варианта Terraform новую фичу под названием “stacks”. Она не имеет никакого отношения к «стекам», о которых я пишу в этом разделе, хотя, концептуально первые и вторые «стеки» очень похожи.

Взяв на вооружение такую структуру, вы сможете поддерживать чистую, удобную для масштабирования базу инфраструктурного кода, понятную разработчикам. Она упростит вам управление сложными инфраструктурами и поможет многократно использовать один и тот же код в разных проектах.

На этом пока всё. Просто помните: инфраструктурный код может и должен быть удобен для разработчика!

Новости, обзоры продуктов и конкурсы от команды Timeweb.Cloud — в нашем Telegram-канале ↩

Перейти ↩

📚 Читайте также:


ссылка на оригинал статьи https://habr.com/ru/articles/877972/