Версионирование, публикация модулей и Terragrunt

Рейтинг: 70.1% · 9 голосов
Подробный курс по Terraform и OpenTofu на примерах Yandex Cloud: ресурсы и провайдеры, состояние и бэкенды, модули, циклы и условия HCL, секреты и Lockbox, тестирование, CI/CD и командная работа. С актуализацией на 2026 (Terraform 1.3-1.10, OpenTofu).
Ответить
Аватара пользователя
Vlad_DevOps
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Версионирование, публикация модулей и Terragrunt

Сообщение Vlad_DevOps »

Оглавление курса (47)
  1. Что такое инфраструктура как код и зачем она нужна
  2. Категории инструментов IaC: чем отличаются Ansible, Packer, Kubernetes и Terraform
  3. Чем Terraform отличается от Ansible, Pulumi и CloudFormation
  4. Как работает Terraform под капотом и кто такой OpenTofu
  5. Установка Terraform и OpenTofu, подготовка Yandex Cloud
  6. Первый ресурс: разворачиваем виртуальную машину в Yandex Cloud
  7. Веб-сервер на ВМ: метаданные, user-data и группы безопасности
  8. Переменные ввода и выходные значения в Terraform
  9. Кластер ВМ: группа экземпляров Yandex Compute Instance Group
  10. Балансировщик нагрузки: Application Load Balancer в Yandex Cloud
  11. Состояние Terraform: что такое tfstate и чем опасен локальный файл
  12. Удалённое хранилище состояния: бэкенд на Yandex Object Storage
  13. Изоляция состояния: рабочие области против раскладки по папкам
  14. Источники данных и terraform_remote_state: связываем компоненты
  15. Модули Terraform: переиспользуем инфраструктуру
  16. Входные, локальные и выходные переменные модуля
  17. Версионирование модулей и подводные камни
  18. Циклы в Terraform: параметр count
  19. Циклы в Terraform: for_each по множествам и картам
  20. Выражения for и строковые директивы
  21. Условная логика: count-трюк, for_each и директива if
  22. Встроенные функции, типы и выражения HCL
  23. Развёртывание без простоя: create_before_destroy
  24. Подводные камни Terraform и рефакторинг с блоком moved
  25. Управление секретами: основы и типы
  26. Инструменты управления секретами: Yandex Lockbox, Vault, SOPS
  27. Аутентификация провайдера и секреты: сервисные аккаунты и OIDC
  28. Секреты в ресурсах и состоянии, ephemeral-значения
  29. Провайдеры Terraform: установка, версии, required_providers
  30. Несколько копий одного провайдера: alias и мультизона
  31. Несколько провайдеров и аккаунтов: configuration_aliases
  32. Terraform, Docker и Kubernetes: Managed Kubernetes в Yandex Cloud
  33. Код Terraform промышленного уровня: почему это долго
  34. Мелкие компонуемые модули вместо монолита
  35. Тестируемые модули и проверки: validation, precondition, postcondition
  36. Версионирование, публикация модулей и Terragrunt (вы здесь)
  37. Ручное тестирование Terraform и очистка ресурсов
  38. Автоматические тесты на Terratest: модульные тесты
  39. Интеграционные и сквозные тесты, пирамида тестирования
  40. Нативный terraform test и статический анализ
  41. Внедрение Terraform в команде: процессы и культура
  42. Конвейер развёртывания прикладного кода
  43. Конвейер инфраструктурного кода и золотое правило Terraform
  44. Стиль, ревью и CI/CD-инструменты: Atlantis, TACOS, policy as code
  45. Что нового в Terraform 1.3-1.10: моды, импорт, провайдер-функции, Stacks
  46. OpenTofu в 2026: чем живёт форк и как мигрировать
  47. Сертификация HashiCorp Terraform Associate и карьера
Представь ситуацию: ты написал отличный модуль для сети в Yandex Cloud, расшарил его пяти командам, а через месяц поправил один ресурс - и у троих коллег внезапно упал apply в проде. Почему? Потому что все ссылались на ветку main, а ты в main и закоммитил ломающее изменение. Никто не виноват, но прод лежит. Эта боль лечится ровно одной дисциплиной - версионированием. Сегодня разберём, как версионировать модули семвером и git-тегами, как публиковать их в реестр, как держать версии terraform, провайдеров и модулей под контролем в команде, и заглянем за границы голого terraform - туда, где живёт Terragrunt и новые декларативные блоки import и removed.

Это тема про взрослую эксплуатацию инфраструктуры как кода. Когда ты один - можно жить на ветках и копипасте. Когда вас десять и пять окружений - без версий и без DRY-подхода всё превращается в болото. Поехали.

Семвер и git-теги: контракт между тобой и потребителями модуля

Модуль terraform - это публичный контракт. Кто-то на него подписался строкой source. Значит, у изменений должны быть правила, по которым потребитель понимает, чего ждать. Эти правила - семантическое версионирование, semver. Версия выглядит как MAJOR.MINOR.PATCH, например 2.4.1.
  • PATCH (2.4.0 -> 2.4.1) - багфиксы, ничего не сломалось в интерфейсе. Обновляешься смело.
  • MINOR (2.4.1 -> 2.5.0) - новая функциональность, обратная совместимость сохранена. Добавил опциональную переменную с дефолтом - это minor.
  • MAJOR (2.5.0 -> 3.0.0) - ломающее изменение. Переименовал переменную, убрал ресурс, поменял дефолт так, что у людей пересоздастся инстанс - это major. Тут потребитель обязан прочитать changelog, а не просто нажать apply.
В мире terraform версия модуля - это git-тег. Реестр модулей (и публичный registry.terraform.io, и приватный в Yandex Cloud или GitLab) ориентируется именно на теги вида v1.2.3 или 1.2.3. Жизненный цикл публикации до неприличия прост:

Код: Выделить всё

# поправили модуль, обновили CHANGELOG.md
git add .
git commit -m "feat: add optional dns_record variable"
git tag v1.3.0
git push origin main --tags
Всё. Тег запушен - версия 1.3.0 опубликована. Никаких отдельных команд publish для git-based реестра не нужно: реестр сам подтягивает теги.

Изображение

Как потребитель пристёгивается к версии

Самая частая ошибка новичка - ссылаться на модуль без версии или на ветку. Так делать нельзя в команде, это та самая мина из вступления. Правильно - фиксировать версию через constraint:

Код: Выделить всё

module "network" {
  source  = "registry.terraform.io/my-org/network/yandex"
  version = "~> 1.3"

  network_name = "prod-vpc"
  zones        = ["ru-central1-a", "ru-central1-b", "ru-central1-d"]
}
Оператор version понимает несколько форм, и их стоит выучить наизусть:
  • - ровно эта версия, ни шагом влево.
  • Код: Выделить всё

    >= 1.3.0
    - эта и любая новее. Опасно: подтянет и major 3.0.0.
  • - "pessimistic constraint". Это >= 1.3, но строго меньше 2.0. То есть берёт любые minor и patch внутри ветки 1.x, но не пустит ломающий major. Золотая середина для команд.
  • Код: Выделить всё

    ~> 1.3.0
    - тот же оператор, но зафиксировал minor: пустит только патчи 1.3.x.
Для модулей из git-репозитория версия указывается прямо в source через ref:

Код: Выделить всё

module "vpc" {
  source = "git::https://gitlab.example.ru/infra/tf-yc-network.git//modules/vpc?ref=v1.3.0"
}
Двойной слеш // отделяет корень репозитория от подпапки внутри - это понадобится, когда в одном репо лежит монорепа модулей.

Версии terraform и провайдеров: блок required и lock-файл

Версионируется не только модуль, но и сам инструмент с провайдерами. Если на ноутбуке у тебя terraform 1.9, у коллеги OpenTofu 1.11, а в CI вообще 1.6 - вы получите три разных результата и долгие споры в чате. Лекарство - блок terraform с ограничениями:

Код: Выделить всё

terraform {
  required_version = ">= 1.7"

  required_providers {
    yandex = {
      source  = "yandex-cloud/yandex"
      version = "~> 0.120"
    }
  }
}
Маленькая, но важная деталь 2026 года: OpenTofu (форк, который ушёл уже довольно далеко от исходного terraform - на момент написания это ветка 1.11/1.12) использует тот же синтаксис, но source провайдера резолвит через свой реестр registry.opentofu.org. Конструкции этого урока работают и там, и там - просто помни, что бинарь может быть tofu, а не terraform.

Точные версии провайдеров фиксирует файл .terraform.lock.hcl. Его обязательно коммитят в git. В нём прибиты не только номера версий, но и хеши артефактов под каждую платформу - чтобы в CI на Linux и у тебя на маке встал бит-в-бит один и тот же провайдер. Обновляешь осознанно:

Код: Выделить всё

terraform init -upgrade
# пересчитает lock под новые constraint, коммитишь обновлённый .terraform.lock.hcl
Terragrunt: убираем копипасту backend и оркеструем зависимости

Голый terraform отлично описывает один кусок инфраструктуры. Проблемы начинаются, когда окружений много. У тебя dev, staging, prod, в каждом по десять компонентов, и в каждом - почти одинаковый блок backend для хранения terraform state в Object Storage. Копипаста плодится, в ней заводятся опечатки. Это и решает Terragrunt - тонкая обёртка над terraform/OpenTofu. В 2026 он наконец дорос до стабильной ветки 1.0, так что на него можно опираться всерьёз.

Что даёт Terragrunt:
  • DRY backend. Описываешь конфигурацию state один раз в корневом terragrunt.hcl, а во всех дочерних компонентах просто наследуешь её. Никакой копипасты бакета и ключа.
  • Оркестрация зависимостей. Через блок dependency один компонент читает output другого. Terragrunt строит граф и применяет компоненты в правильном порядке: сначала сеть, потом инстансы, которые в этой сети живут.
  • before/after hooks. Хуки, которые выполняются до или после команды terraform - например, прогнать линтер перед plan или отправить уведомление после apply.
Корневой terragrunt.hcl с единым backend выглядит так:

Код: Выделить всё

remote_state {
  backend = "s3"
  config = {
    endpoint   = "https://storage.yandexcloud.net"
    bucket     = "tfstate-prod"
    key        = "${path_relative_to_include()}/terraform.tfstate"
    region     = "ru-central1"
    # Object Storage Yandex Cloud S3-совместим, поэтому отключаем AWS-специфику
    skip_region_validation      = true
    skip_credentials_validation = true
  }
}
А дочерний компонент, который зависит от сети, всего лишь подключает родителя и тянет чужой output:

Код: Выделить всё

include "root" {
  path = find_in_parent_folders()
}

dependency "network" {
  config_path = "../network"
}

inputs = {
  subnet_id = dependency.network.outputs.subnet_id
}
Запускаешь terragrunt run-all apply - и он сам разрулит порядок по графу зависимостей. Важно понимать: Terragrunt живёт за границами terraform, это отдельный инструмент сообщества (Gruntwork), а не фича HashiCorp. Тащить его в проект стоит только когда боль копипасты и оркестрации реально появилась, а не "на всякий случай".

2026: декларативный жизненный цикл через import и removed

Раньше управление жизненным циклом ресурса делалось императивными командами CLI: terraform import гонял по аргументам, а terraform state rm выкидывал ресурс из стейта руками. Проблема - это не отражается в коде, не проходит ревью, легко забыть и невозможно воспроизвести в CI. Современный подход - описывать это декларативно прямо в конфиге.

Блок import (появился в terraform 1.5) затягивает уже существующий ресурс под управление кодом. Ты добавляешь ресурс в конфиг и рядом пишешь:

Код: Выделить всё

import {
  to = yandex_compute_instance.web
  id = "fhm8abcd1234example"
}

resource "yandex_compute_instance" "web" {
  name = "web-1"
  # ... остальная конфигурация под существующий инстанс
}
На следующем terraform plan apply ресурс заезжает в state без единой ручной команды. В OpenTofu это поехало ещё дальше - там import умеет for_each, и можно затащить пачку ресурсов разом.

Блок removed (terraform 1.7) - зеркальный сценарий. Ты хочешь убрать ресурс из управления terraform, но НЕ удалять его в облаке. Удаляешь блок resource из кода и вместо него ставишь:

Код: Выделить всё

removed {
  from = yandex_compute_instance.legacy

  lifecycle {
    destroy = false
  }
}
destroy = false говорит: "забудь про этот ресурс, но в Yandex Cloud его не трогай". Это аккуратная замена terraform state rm, и она проходит через git и ревью, как и должна любая порядочная инфраструктура как код.

Типичные грабли
  • source без version. Классика. Ссылаешься на модуль, забыл version - и любой апдейт модуля прилетает тебе неконтролируемо. Всегда фиксируй constraint.
  • Ветка вместо тега. ?ref=main - это бомба замедленного действия. Только ?ref=vX.Y.Z.
  • .terraform.lock.hcl в .gitignore. Lock-файл обязан быть в репозитории, иначе CI и локалка разъедутся по версиям провайдеров.
  • Major-бамп без changelog. Сломал интерфейс - подними MAJOR и опиши, что именно сломалось. Иначе потребители узнают об этом в проде.
  • Terragrunt без нужды. Если у тебя один компонент и одно окружение - terragrunt только добавит слой абстракции и боли. Он окупается на масштабе.
  • Путаница import и moved. import - затащить существующий ресурс из облака. moved - переименовать адрес ресурса внутри стейта. Это разные блоки под разные задачи.
Мини-лаба
  • Возьми любой свой модуль, заведи в нём CHANGELOG.md, поставь git-тег v0.1.0 и запушь с --tags.
  • Подключи этот модуль из другого проекта через source + version = "~> 0.1" и убедись, что terraform init его тянет.
  • Внеси в модуль обратно-совместимую правку, тегни v0.2.0, и посмотри, как ~> 0.1 подхватит её, а ~> 0.1.0 - нет.
  • Возьми существующий ресурс в Yandex Cloud, опиши его в конфиге и затащи блоком import. Затем убери из управления через removed с destroy = false и проверь planом, что облако не тронуто.
Контрольные вопросы
  • Что означает оператор ~> 1.3 в constraint версии модуля и почему он удобен для команды?
  • Зачем коммитить .terraform.lock.hcl в git и что в нём хранится кроме номеров версий?
  • Чем блок removed с lifecycle { destroy = false } отличается от terraform destroy и от terraform state rm?
  • Какие три проблемы решает Terragrunt и почему его не стоит тащить в маленький проект?
Итог: что запомнить

Версия модуля - это git-тег по semver, а major-бамп - это обещание потребителю "читай changelog, тут сломалось". Потребитель всегда пристёгивается к версии через version constraint, а ~> закрывает большинство командных сценариев. Версии terraform/OpenTofu и провайдеров фиксируются блоком required и lock-файлом, который живёт в git. Когда окружений становится много - приходит Terragrunt со своим DRY-backend, графом зависимостей и хуками. А управление жизненным циклом ресурсов в 2026 году делается декларативно: import затаскивает существующее под код, removed аккуратно отпускает его, не трогая облако. Меньше CLI-магии руками - больше воспроизводимости через git.
👍 ❤️4 🔥1 😄 🤔
Аватара пользователя
andy_kate
Сообщения: 1
Зарегистрирован: 15 май 2026, 13:53

Re: Версионирование, публикация модулей и Terragrunt

Сообщение andy_kate »

Спасибо, наконец дошло зачем нужен ~> а не просто >=. У нас как раз прод падал когда модуль на main ссылался, теперь понятно почему.
👍1 ❤️1 🔥 😄 🤔2
Аватара пользователя
dfield
Сообщения: 1
Зарегистрирован: 07 июн 2026, 23:00

Re: Версионирование, публикация модулей и Terragrunt

Сообщение dfield »

А removed с destroy=false реально не удаляет ресурс в облаке? Звучит слишком хорошо, надо проверить на тестовом инстансе перед тем как такое в прод нести.
👍2 ❤️ 🔥 😄 🤔2
Ответить
← Предыдущая глава
Тестируемые модули и проверки: validation, precondition, postcondition
Следующая глава →
Ручное тестирование Terraform и очистка ресурсов

Все главы курса «Terraform: инфраструктура как код на Yandex Cloud»

Поделиться темой: ✈ Telegram VK
Похожие запросы: как вынести terraform в модуль и переиспользовать инфраструктурувходные и выходные переменные модуля terraform как передатькак создать роль ansible с нуля и структура каталогов

Вернуться в «Terraform: инфраструктура как код на Yandex Cloud»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость