Представьте, что вы настроили сервер в облаке: накликали мышкой нужные галочки в веб-консоли, создали базу данных, открыли пару портов. Всё работает. А через месяц нужно поднять точно такую же копию для тестов — и вы уже не помните, какие именно галочки нажимали. Знакомо? Именно эту боль решает Infrastructure as Code (инфраструктура как код, сокращённо IaC).
Идея простая: вместо того чтобы настраивать облако руками, вы описываете желаемую инфраструктуру в текстовых файлах — а специальная программа сама создаёт всё по этому описанию. Эти файлы можно положить в git, показать коллеге на ревью, откатить к прошлой версии. Никакого «а что я там нажимал» — есть код, и по нему видно, что должно быть.
Разница не в том, что получилось сейчас, а в том, что будет через месяц: настройку мышью нельзя повторить, откатить и показать коллеге, а описание в файлах можно применить снова и получить ту же инфраструктуру.
Что такое инфраструктура как код
Инфраструктура — это всё, на чём работает ваше приложение: виртуальные машины, базы данных, сети, хранилища, правила доступа. Раньше всё это создавали вручную через панель управления или серию команд. Подход IaC переносит это в файлы, которые читаются и человеком, и машиной.
Аналогия: рецепт блюда. Можно готовить «на глаз» и каждый раз получать чуть разный результат, а можно записать точный рецепт — граммы, минуты, температура. По рецепту блюдо повторит и другой человек, и вы через год. Файл инфраструктуры — это и есть такой рецепт для облака.
Что это даёт на практике:
- Воспроизводимость. Одним и тем же файлом разворачиваете идентичные окружения для разработки, тестов и продакшена.
- История изменений в git. Видно, кто, когда и зачем добавил базу данных — обычный
git log. - Ревью. Изменение инфраструктуры проходит через pull request, как любой код.
- Откат. Что-то сломалось — возвращаетесь к прошлой рабочей версии файла.
- Никакого «забыл, что нажимал»: источник правды один — файл.
Базовые сведения о самом облаке — в статье основы AWS.
Чего инфраструктура как код не делает
Границу стоит провести сразу, потому что новички ждут от инструмента больше, чем он умеет, и потом не понимают, где стыки.
Инфраструктурный инструмент создаёт ресурсы, а не настраивает их изнутри. Terraform поднимет виртуальную машину, сеть, диск и права. Что происходит внутри машины — установка пакетов, правка файлов настроек, сервисы — это другая задача, и называется она управлением конфигурацией: Ansible, Chef, Puppet, а чаще всего просто заранее собранный образ машины. Признак, что граница нарушена: в коде инфраструктуры появляется длинный скрипт первичной загрузки, который ставит половину системы.
И он не выкатывает приложение. Новая версия кода, откат, постепенная замена экземпляров — это конвейер доставки. Смешивать это с инфраструктурой вредно по двум причинам: приложение выкатывается десять раз в день, а инфраструктура меняется раз в месяц, и держать их в одном цикле означает гонять план по всей инфраструктуре ради смены тега образа. Признак нарушения: terraform apply на каждый коммит приложения.
Как разделяют на практике:
| Задача | Чем делают |
|---|---|
| Сеть, права, базы, кластеры, очереди | инфраструктурный инструмент (Terraform, CloudFormation, CDK) |
| Содержимое машины и её пакеты | заранее собранный образ (Packer) или управление конфигурацией |
| Содержимое контейнера | Dockerfile и сборка образа |
| Версия приложения в кластере | конвейер доставки, манифесты, оператор доставки |
| Схема базы данных | миграции в составе приложения или отдельной задачей |
Серая зона есть, и стоит знать, где она. Ресурсы уровня приложения (правило маршрутизации, тема очереди, параметр в хранилище настроек) можно описывать и инфраструктурным инструментом, и вместе с приложением — правило простое: кто чаще меняет, тот и владеет. Если тема очереди появляется вместе с новой функцией сервиса, она живёт в его репозитории; если это общая шина на всю компанию — в инфраструктурном.
Declarative против imperative
Это главная развилка в мире IaC, и понять её важно с самого начала. Есть два способа описать, чего вы хотите.
Imperative (императивный) — вы описываете КАК, по шагам: «создай сервер, потом подключи к нему диск, потом открой порт 80». Это последовательность команд. Похоже на навигатор, который диктует каждый поворот: «через 200 метров направо».
Declarative (декларативный) — вы описываете ЧТО хотите получить в итоге: «должен существовать сервер с диском и открытым портом 80». А уже инструмент сам решает, какие шаги нужны, чтобы привести реальность к этому состоянию. Это как сказать водителю адрес — а маршрут он построит сам.
Большинство популярных инструментов IaC — декларативные. Terraform и CloudFormation работают именно так. Например, в Terraform на языке HCL вы описываете конечное состояние — хранилище в облаке:
resource "aws_s3_bucket" "example" {
bucket = "my-unique-bucket-name"
tags = {
Name = "My bucket"
Environment = "Dev"
}
}
То же самое в CloudFormation выглядит как YAML-шаблон:
AWSTemplateFormatVersion: '2010-09-09'
Resources:
ExampleBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-unique-bucket-name
Tags:
- Key: Name
Value: My bucket
Заметьте: ни там, ни там нет слов «создай», «потом», «если уже есть — пропусти». Вы просто описали, что хранилище должно существовать с такими тегами. Решать, создавать его или менять существующее, будет сам инструмент.
Особняком стоит CDK (Cloud Development Kit). Его пишут как обычную программу — на TypeScript, Python, Java или других языках. Но это не делает его императивным: код CDK не создаёт ресурсы напрямую, а синтезирует declarative-шаблон CloudFormation, который потом и применяется. То есть CDK — это удобная обёртка, дающая привычный язык программирования поверх декларативного движка:
Команда cdk synth превратит такую программу в шаблон CloudFormation (в человекочитаемом виде его можно посмотреть как YAML), а cdk deploy его применит. Подробный разбор каждого инструмента — в статьях про Terraform, CloudFormation и CDK.
Инфраструктура уже есть: как завести её в код
Учебные примеры начинаются с чистого листа, а в жизни почти всегда есть накликанное руками: сеть, база, пара машин, права. Вопрос «как это завести в код» встаёт в первый же день, и у него есть штатный ответ — импорт.
Что делает импорт. Он не создаёт ресурс и не меняет его — он записывает существующий ресурс в состояние инструмента, после чего тот начинает считать его своим. Дальше вы описываете этот ресурс в коде так, чтобы план показывал «изменений нет»: это и есть критерий успеха.
В Terraform это можно сделать командой или, что удобнее, блоком в самом коде:
import {
to = aws_s3_bucket.reports
id = "acme-reports-prod"
}
resource "aws_s3_bucket" "reports" {
bucket = "acme-reports-prod"
}
terraform plan # покажет, что будет импортировано и чем описание отличается от реальности
terraform apply # выполнит импорт
У CloudFormation есть своя операция импорта ресурсов в стек, у CDK — она же под капотом.
Как это делают на практике, чтобы не утонуть. Импортировать всё сразу — верный способ застрять на неделю. Работающий порядок такой:
- Начинают с того, что редко меняется и мало от чего зависит: хранилища объектов, реестры, группы параметров.
- Идут снизу вверх по зависимостям: сначала сеть, потом то, что в ней живёт.
- По одному ресурсу за раз, каждый раз доводя план до «изменений нет». Импортированный ресурс почти всегда отличается от описания в десятке мелочей (теги, версии, значения по умолчанию) — их либо переносят в код, либо намеренно игнорируют.
- Ресурсы, которые нельзя трогать, сразу защищают от удаления (об этом ниже), потому что импортированная база — теперь объект, который инструмент может удалить по вашей же ошибке в коде.
- Что-то оставляют вне кода осознанно и записывают это решение: учётные записи людей, ресурсы чужих команд, эксперименты.
Инструменты, которые ускоряют. Есть генераторы, которые читают существующую инфраструктуру и выдают заготовки кода (terraformer, экспорт шаблона у CloudFormation, cdk migrate). Результат у них черновой — имена, структура и модули будут не те, — но как отправная точка на большом контуре это экономит дни. Правило одно: сгенерированное обязательно приводят к своему виду и доводят план до нуля изменений.
И честное ожидание по времени. Перевод существующего контура среднего размера в код — это недели, а не вечер, и делается он параллельно обычной работе. Отсюда практическая стратегия, которой пользуются чаще всего: всё новое сразу в коде, старое импортируют по мере того, как к нему приходится прикасаться. Через полгода в коде окажется то, что действительно живёт, а мёртвое так и останется в стороне — и это нормально.
Что такое state
Чтобы декларативный инструмент мог понять, что нужно изменить, ему нужна память. Эта память называется state (состояние) — это «слепок» того, что инструмент знает о реальной инфраструктуре: какие ресурсы он создал, с какими настройками и идентификаторами.
Аналогия: список покупок с галочками. Вы пришли в магазин со списком (это ваш код — чего вы хотите) и отмечаете, что уже лежит в корзине (это state — что уже есть). Сравнив одно с другим, вы точно знаете, что ещё докупить.
Когда вы запускаете terraform plan, Terraform делает ровно это — но с важной поправкой. Сначала он сходит в облако и освежит свои записи (это называется refresh), а сравнивать ваш код будет уже с тем, что там на самом деле, а не с тем, что записано в state. Разница в том и в другом случае выглядит одинаково — что добавит, что изменит, что удалит, — но благодаря освежению план заметит и то, что кто-то поменял мимо Terraform: если коллега удалил хранилище руками через консоль, план предложит создать его заново. Только после terraform apply изменения применяются по-настоящему.
terraform plan
terraform apply
У CloudFormation роль state играет сам стек (stack) на стороне AWS — облако само хранит, какие ресурсы относятся к этому шаблону. У Terraform state — это отдельный файл (terraform.tfstate), который в командной работе хранят не на ноутбуке, а в общем месте, например в объектном хранилище, чтобы у всех была одна версия правды.
В state лежат ваши пароли
Одно свойство состояния важнее всех остальных, и о нём лучше узнать до первого запуска, а не после.
Состояние хранит значения всех атрибутов созданных ресурсов — в открытом виде. Не только идентификаторы, а всё: пароль, который вы сгенерировали для базы, содержимое созданного секрета, ключ доступа, приватная часть сертификата, строка подключения. Инструмент обязан их помнить, иначе он не сможет сравнить желаемое с текущим.
Отсюда практические правила, которые нельзя обойти:
- Файл состояния — секрет. Его нельзя коммитить в репозиторий, нельзя выкладывать в общий доступ, нельзя отправлять в чат «посмотри, что там». Утечка состояния равносильна утечке паролей всей описанной инфраструктуры.
- Удалённое хранилище состояния — с шифрованием и ограниченным доступом. Права на чтение этого хранилища равны правам на все пароли; список тех, у кого они есть, должен быть коротким.
- Ничего чувствительного не создают через инфраструктурный код без нужды. Пароль базы лучше сгенерировать в хранилище секретов и передавать ссылку, чем генерировать в коде инфраструктуры и хранить значение в состоянии.
- Пометка
sensitiveне помогает. Она лишь скрывает значение в выводе плана; в файле состояния оно лежит как есть. Это частое заблуждение. - Резервные копии состояния — тоже секрет, с теми же требованиями.
Подробный разбор того, где держать состояние, как его блокировать и что делать с секретами в конвейере, — в статье про состояние и доставку.
Повторный запуск ничего не ломает: idempotency
Страшное слово idempotency (идемпотентность) на деле означает очень понятную вещь: повторный запуск без изменений в коде ничего не ломает и ничего не дублирует.
Аналогия: выключатель света со словами «выключено» вместо «переключить». Сколько раз ни нажми «выключено» — свет останется выключенным. А если бы кнопка была «переключить», каждое нажатие меняло бы состояние, и результат зависел бы от числа нажатий.
Именно так ведут себя декларативные инструменты. Вы описали «нужно одно хранилище». Запустили — хранилище создалось. Запустили ещё раз тот же код, не меняя его — инструмент сравнит код со state, увидит, что всё уже на месте, и ничего не сделает. Не появится второе хранилище, ничего не сломается.
terraform apply
terraform apply
Второй apply на неизменённом коде завершится сообщением вроде «0 added, 0 changed, 0 destroyed». Это и есть идемпотентность в действии — и именно благодаря ей запускать IaC безопасно хоть каждый день: лишнего он не натворит.
Где идемпотентность ломается
Обещание «второй запуск не даёт изменений» верно в большинстве случаев и не во всех. Полезно знать, где именно оно не работает, потому что иначе вы будете подозревать себя.
Ресурсы, которые провайдер не умеет сравнивать. У части ресурсов провайдер не читает обратно все свойства (это называется отсутствием поддержки чтения), и тогда он не знает, изменилось ли что-то. Проявляется по-разному: план каждый раз показывает изменение, которого нет, или наоборот — реальное изменение не видно вовсе.
Генерируемые значения. Ресурс, у которого имя или пароль создаются случайно при каждом планировании, будет бесконечно пересоздаваться. Лечится тем, что случайность выносят в отдельный ресурс со своим состоянием (random_password и его аналоги) — тогда значение генерируется один раз и запоминается.
Внешние изменения. Ресурс, который меняет кто-то другой, — самый частый случай. Классика: число копий в группе масштабирования, которое меняет автомасштабирование, или тег, который навешивает корпоративная политика. Инструмент видит расхождение и старательно возвращает своё значение — то есть каждый запуск гасит работу другого механизма.
Побочные эффекты провайдера. Часть ресурсов при применении выполняет действия, которые нельзя отменить и повторить безболезненно (пересоздание базы вместо изменения параметра, замена машины при смене типа). Идемпотентность тут сохраняется формально — итог одинаковый, — а вот цена второго запуска совсем другая, и план об этом честно предупреждает словом «replace».
Два инструмента, которые закрывают эти случаи и заодно отвечают на вопрос из следующего раздела про дрейф:
ignore_changes — «это свойство меняет не я, не трогай его». Обязательно для числа копий под автомасштабированием, для тегов от внешних систем, для версий образов, которые обновляет конвейер:
resource "aws_autoscaling_group" "app" {
desired_capacity = 2
lifecycle {
ignore_changes = [desired_capacity, tag]
}
}
prevent_destroy — «этот ресурс нельзя удалять, даже если из кода следует обратное». Ставится на всё, чья потеря необратима: база, хранилище с данными, ключ шифрования:
resource "aws_db_instance" "orders" {
lifecycle {
prevent_destroy = true
}
}
Это единственная защита от ошибки, которая случается со всеми: переименовали ресурс в коде, и инструмент честно решил, что старый надо удалить, а новый создать. У баз и хранилищ такое движение означает потерю данных; с этой пометкой применение просто остановится с ошибкой, и вы успеете подумать.
Когда реальность разошлась с кодом: drift
А теперь представьте: кто-то из коллег зашёл в веб-консоль и поменял настройку руками — например, переключил тип сервера. В коде по-прежнему написано одно, а в реальности уже другое. Вот это расхождение и называется drift (дрейф): реальность разошлась с кодом.
Drift опасен тем, что незаметен. Файлы выглядят как раньше, но облако работает иначе. А при следующем apply инструмент может «откатить» ручную правку обратно к тому, что записано в коде, — и сломать то, что коллега чинил вручную.
Поэтому в инструментах есть способ обнаружить дрейф. В Terraform это запуск, который только сверяет реальность с записями, ничего не меняя ни в инфраструктуре, ни в самих записях:
terraform plan -refresh-only
Terraform сходит в облако, посмотрит фактическое состояние ресурсов и сообщит: «объекты изменились вне Terraform». Обратите внимание на plan: у той же операции есть форма terraform apply -refresh-only, и она инфраструктуру тоже не трогает, но записывает увиденное в state — то есть молча принимает ручную правку за новое желаемое состояние. Для «просто посмотреть» нужен именно plan.
В CloudFormation для того же есть отдельная команда обнаружения дрейфа. Работает она в два приёма: первая команда только запускает проверку и возвращает её идентификатор, результат забирают второй:
aws cloudformation detect-stack-drift --stack-name my-stack
aws cloudformation describe-stack-resource-drifts --stack-name my-stack
Лечение дрейфа простое по идее: либо вернуть реальность к коду (применить шаблон заново), либо внести ручную правку в код, чтобы он отражал новое желаемое состояние. Главное правило, которое экономит нервы: меняем инфраструктуру через код, а не руками в консоли — тогда дрейфу просто неоткуда взяться.
Цена подхода и когда его не брать
Список преимуществ выглядит убедительно, и честности ради стоит назвать цену — она реальная и особенно заметна на старте.
Медленнее кликов, пока не станет быстрее. Создать одну машину в консоли — две минуты. Описать её кодом, настроить хранилище состояния, разобраться с провайдером и правами — полдня в первый раз. Выигрыш появляется со второй средой и с третьим изменением, а не сразу.
Появляется ревью и очередь. Изменение инфраструктуры теперь проходит через запрос на изменение, и «быстро поправить руками» становится нарушением. Это правильно и это замедляет срочные правки — к чему надо быть готовым, а не обнаруживать в инциденте.
Версии ломаются. Провайдер обновился и поменял поведение, инструмент обновился и требует другой синтаксис, модуль из реестра выпустил несовместимую версию. Без фиксации версий и файла блокировки один и тот же код на двух машинах даёт разный результат. Это отдельная дисциплина, которой в кликах не было.
Состояние — новая сущность, которую можно потерять. Файл состояния надо где-то хранить, блокировать, беречь как секрет и уметь чинить, когда он разошёлся с реальностью. Это не сложно, но это ещё одна вещь, за которой следят.
Первый час в новом проекте уходит не на дело. Инициализация, бэкенд, права, версии — и только потом первый ресурс.
Когда не брать вовсе. Одноразовый эксперимент, который завтра удалят. Разовая правка в чужом контуре, который вы не ведёте. Ресурсы, у которых нет провайдера или он слабый (тогда код превратится в набор вызовов командной строки, и лучше честно оставить это скриптом). И учебная работа, где цель — понять сервис, а не воспроизвести его: кликнуть в консоли и посмотреть, что получилось, для этого быстрее.
Когда точно брать. Больше одной среды. Больше одного человека. Инфраструктура, которую придётся воспроизвести (новый регион, авария, новый заказчик). Требование аудита «кто и когда изменил». И простой критерий, который работает лучше рассуждений: если вы второй раз повторяете одну и ту же последовательность кликов — пора.
Где это применяется
Infrastructure as Code — это фундамент, на котором стоит почти вся современная эксплуатация облака. Несколько типичных мест, где вы с этим столкнётесь:
- Автоматический деплой. IaC — естественная часть пайплайнов: код инфраструктуры применяется так же автоматически, как и код приложения. См. принципы пайплайна и стратегии релизов.
- Одинаковые окружения. Разработка, тесты и продакшен поднимаются из одних файлов — отличаются только параметрами.
- Kubernetes. Манифесты Kubernetes — это тоже декларативное описание желаемого состояния, та же философия «опиши ЧТО, а не КАК». Начать стоит с основ Kubernetes и деплоя и конфигурации.
Типичные ошибки начинающих, которых легко избежать, если помнить материал выше:
- Правят инфраструктуру руками «по-быстрому» — и получают drift, который потом всплывает в самый неподходящий момент. Любое изменение — через код.
- Теряют или не делят state. В команде state должен лежать в общем месте, иначе двое разработчиков случайно затрут изменения друг друга.
- Боятся повторно запускать apply. Из-за идемпотентности повторный запуск на неизменённом коде безопасен — бояться нечего.
- Путают imperative и declarative и пытаются писать «по шагам» там, где инструмент ждёт описания конечного состояния.
Что учить дальше: возьмите один инструмент и пройдите его целиком — например, Terraform как самый универсальный, CloudFormation как родной для AWS или CDK, если ближе обычный язык программирования. Затем посмотрите, как state и доставка устроены в командной работе, в статье про state и доставку, и как всё это связывается с автоматическими релизами в разделе про ветвление и релизы.
Глубже: завести существующее под управление: importрасширенное
Все примеры выше начинаются с чистого листа, а первая настоящая задача другая: облако уже есть, его накликали руками за два года, и теперь это нужно описать кодом, не пересоздавая. Для этого у инструментов есть импорт: связать существующий ресурс с описанием в коде и записать его в state, ничего не меняя в облаке.
В Terraform импорт делают блоком в коде с версии 1.5: import { to = aws_s3_bucket.assets, id = "shop-assets" }, а terraform plan -generate-config-out=generated.tf сам напишет черновик описания ресурса по его реальному состоянию. Дальше черновик правят до читаемого вида и добиваются пустого плана: plan не предлагает ни одного изменения, значит код совпал с реальностью, и только после этого блок импорта убирают. Старая команда terraform import адрес id делает то же самое, но описание приходится писать руками целиком. В CloudFormation существующий ресурс заводят в стек через набор изменений типа импорт с обязательным DeletionPolicy: Retain, чтобы ошибка не удалила то, что импортировали; в CDK та же операция называется cdk import.
Что здесь важно. Импорт идёт по одному ресурсу, у каждого свой формат идентификатора, и он написан в документации провайдера у каждого типа. Зависимости (группа безопасности, на которую ссылается машина) импортируют раньше зависимых. Массовый импорт сотен ресурсов делают инструментами, которые генерируют код по аккаунту целиком, и результат всё равно читают глазами: он описывает всё, включая мусор. Разумный порядок: сначала то, что собираетесь менять, потом остальное по мере надобности, а список «пока не под управлением» ведут явно, иначе через год никто не знает, что кодом, а что нет. И сразу после импорта настраивают проверку дрейфа, о которой раздел выше, потому что руками править привыкли, и привычка переживёт импорт.
Глубже: проверки инфраструктурного кодарасширенное
Конвейер из plan и apply пропускает половину ошибок, которые ловятся до плана, и все, которые план не показывает. Проверок четыре уровня, и внедряют их в этом порядке.
Форма. terraform fmt -check и terraform validate ловят синтаксис и опечатки в именах атрибутов за секунды, без доступа к облаку; cfn-lint делает то же для шаблонов CloudFormation, tflint находит устаревшие аргументы и несуществующие типы машин. Это первый шаг конвейера, и он бесплатный.
Политика и безопасность. Сканеры читают код и находят то, что план покажет как обычное изменение: бакет с публичным доступом, группу безопасности с 0.0.0.0/0 на порт базы, диск без шифрования, роль с *. Для Terraform это checkov и trivy config (бывший tfsec), для CloudFormation cfn_nag и тот же checkov. Свои правила компании («тег команды обязателен», «только эти регионы») пишут на OPA и проверяют conftest по JSON плана; у коммерческих платформ для этого свои языки политик. Правила начинают с десятка критичных, иначе первый прогон даёт пятьсот предупреждений и сканер выключают.
Стоимость. Изменение, которое поднимает счёт на тысячу долларов в месяц, в плане выглядит как одна строка с другим типом машины. Оценщик стоимости (infracost) читает план и пишет в pull request разницу в деньгах до и после, и это единственная проверка, которую читают все.
Тесты. Модуль, который используют десять команд, тестируют как код: terraform test (с версии 1.6) поднимает ресурсы по сценарию и проверяет утверждения, Terratest делает то же на Go с реальным облаком, у CDK есть модуль утверждений, который проверяет синтезированный шаблон без развёртывания. Тесты с реальными ресурсами дороги и медленны, поэтому их гоняют для модулей, а не для каждого окружения.
Порядок в конвейере: форма, линтер, сканер политик, план, стоимость, и тесты отдельно по модулям. Каждый шаг падает и останавливает слияние; предупреждения, которые не останавливают, через месяц никто не читает.
Глубже: какой инструмент брать: Terraform, OpenTofu, CloudFormation, CDK и Pulumiрасширенное
Сравнение инструментов рассыпано по концовкам статей раздела в виде «а ещё посмотрите». Соберём в одном месте с критериями, потому что выбор живёт годами.
Terraform и OpenTofu. Terraform в 2023 году сменил лицензию с открытой на BUSL: пользоваться можно, а строить на нём конкурирующий продукт нельзя. В ответ сообщество под крылом Linux Foundation сделало OpenTofu, форк с открытой лицензией, совместимый по языку, провайдерам и state; расходятся они на новых возможностях (у OpenTofu раньше появилось шифрование state), и модуль, написанный сегодня, работает у обоих. Для компании выбор между ними это вопрос лицензионной политики и того, какой из них поддерживает выбранная платформа запуска, а не языка.
CloudFormation. Родной для AWS, state держит сам AWS, откат при ошибке делает автоматически. Только AWS, новые сервисы появляются с задержкой, шаблоны многословны, а типовые операции (циклы, условия) выражаются хуже, чем в HCL.
CDK. Тот же CloudFormation, но пишется на TypeScript, Python или Java с конструктами высокого уровня, где одна строка разворачивается в десяток ресурсов с разумными умолчаниями. Хорош для команд, которые живут в AWS и пишут на этих языках; цена та же, что у CloudFormation, плюс слой абстракции, через который иногда приходится смотреть на синтезированный шаблон.
Pulumi. Настоящие языки программирования, как в CDK, но для любого провайдера, и со своим state, который либо в их облаке, либо в вашем бакете. Берут, когда команда хочет тесты, типы и переиспользование как в обычном коде и работает не только с AWS.
Критерии по порядку важности. Провайдеров больше одного или это возможно через три года: Terraform или OpenTofu, либо Pulumi. Команда из разработчиков, а не из инженеров инфраструктуры, и код на одном языке: CDK в AWS, Pulumi вне его. Нужен state без своей эксплуатации и автоматический откат: CloudFormation. Найм и готовые модули: у Terraform самое большое сообщество и реестр модулей, и это аргумент, пока остальное равно. Смешивать можно: Terraform для сети и учётных записей, CDK для сервисов одной команды, но каждый ресурс принадлежит одному инструменту, иначе они спорят за него через drift.
Коротко
- Инфраструктура как код: описание желаемого состояния в файлах, история и ревью в git, одинаковые окружения из одних файлов.
- Декларативные инструменты (Terraform, CloudFormation) описывают что, а не как; CDK пишется как программа, но синтезируется в тот же декларативный шаблон.
- State это память инструмента о том, что он создал; у Terraform это файл, который делят и блокируют, у CloudFormation его держит AWS. В файле состояния лежат все значения атрибутов, включая пароли: это секрет, пометка
sensitiveего не шифрует, а права на хранилище состояния равны правам на пароли. - Повторный
applyбез изменений кода ничего не делает; ручные правки дают дрейф, который ловятplan -refresh-onlyи проверкой дрейфа стека. Идемпотентность ломают несравниваемые свойства, генерируемые значения и внешние изменения: лечитсяignore_changes, а необратимое защищаютprevent_destroy. - Существующее заводят под управление импортом: блок
importс генерацией кода, пустой план как критерий, зависимости раньше зависимых, список неуправляемого явно. Существующую инфраструктуру заводят импортом: по одному ресурсу, доводя план до «изменений нет», начиная с простого и защищая важное от удаления; всё новое сразу в коде, старое — по мере касания. - Конвейер проверяет форму, линтер, сканер политик, план, стоимость изменений и тесты модулей; каждый шаг останавливает слияние.
- Выбор инструмента: Terraform или OpenTofu и Pulumi для нескольких провайдеров, CDK для команд разработчиков в AWS, CloudFormation за state и откат у AWS; каждый ресурс принадлежит одному инструменту.
- Инструмент создаёт ресурсы, но не настраивает машину изнутри и не выкатывает приложение: эти задачи закрывают образ машины, сборка контейнера и конвейер доставки.
- Цена подхода: полдня на первый ресурс, ревью вместо быстрых правок, дисциплина версий и состояние как новая сущность; для одноразового эксперимента это не нужно.
Что почитать дальше
- Terraform — провайдеры, ресурсы, переменные и модули на примерах AWS.
- IaC на практике: state, секреты, доставка — удалённый state с блокировкой, секреты вне кода, конвейер с планом на ревью.
- CloudFormation и CDK — родные инструменты AWS и когда их берут вместо Terraform.