Перейти к содержанию
Innovialabs

Модули Terraform: как структурировать переиспользуемую инфраструктуру

Практика3 мин

Коротко

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

Коротко

Модуль в Terraform — это просто папка с файлами .tf. Любая конфигурация уже корневой модуль, а переиспользуемые модули — это папки, которые вызываются блоком module.

Хороший модуль:

  • описывает одну цельную вещь: сеть, базу данных с бэкапами, сервис с балансировщиком;
  • принимает несколько типизированных входов с разумными значениями по умолчанию;
  • возвращает outputs, которые реально нужны другим частям стека;
  • версионируется, чтобы окружения обновлялись осознанно, а не случайно.

Окружения (dev, staging, prod) — это тонкие корневые конфигурации, которые вызывают одни и те же модули с разными значениями и хранят раздельный state.

Входы, выходы и контракт модуля

# modules/postgres/variables.tf
variable "name" {
  type        = string
  description = "Instance name prefix"
}

variable "instance_size" {
  type    = string
  default = "small"
}

variable "backup_retention_days" {
  type    = number
  default = 7
  validation {
    condition     = var.backup_retention_days >= 1
    error_message = "Retention must be at least one day."
  }
}

# modules/postgres/outputs.tf
output "endpoint" {
  value = aws_db_instance.this.address
}

output "password_secret_arn" {
  value     = aws_secretsmanager_secret.db.arn
  sensitive = true
}

Относитесь к переменным и выходам как к публичному API:

  • Указывайте type и description; добавляйте validation для значений, в которых легко ошибиться.
  • Объединяйте поля в одну object-переменную, только если они действительно связаны.
  • Помечайте секреты sensitive = true и помните, что они всё равно попадают в state, поэтому защищайте бэкенд state.
  • Не объявляйте блоки provider внутри переиспользуемых модулей — пусть провайдеры передаёт вызывающая сторона.

Версионирование

При вызове модуля из Git-репозитория или реестра версию можно закрепить:

module "db" {
  source = "git::https://github.com/acme/terraform-modules.git//postgres?ref=v1.3.0"

  name                  = "shop-prod"
  instance_size         = "large"
  backup_retention_days = 14
}
  • Помечайте релизы семантическими версиями: ломающие изменения входов или адресов ресурсов — новая мажорная версия.
  • Обновляйте сначала dev, потом staging, потом prod, каждый раз читая terraform plan.
  • При переименовании ресурсов внутри модуля добавляйте блоки moved, чтобы Terraform обновил адреса, а не пересоздал ресурсы.
  • Закрепляйте версии провайдеров в required_providers и коммитьте lock-файл.

Разумная структура репозитория

infra/
  modules/
    network/
    postgres/
    app-service/
  envs/
    dev/
      main.tf
      backend.tf
      terraform.tfvars
    staging/
    prod/
  • Каждая папка в envs/ — отдельный корень со своим state и настройками бэкенда. Ошибка в dev не заденет state продакшена.
  • Различия между окружениями живут в переменных, а не в скопированных ресурсах.
  • Большие окружения делите на несколько стеков (сеть, данные, приложения), когда план становится медленным или рискованным. Значения между ними передавайте через outputs и data sources.
  • Workspaces подходят для почти одинаковых окружений, но отдельные папки нагляднее показывают различия и упрощают разграничение доступа.

Как безопасно использовать модули из реестра

Публичные модули экономят время, но вы запускаете чужой код со своими облачными доступами.

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

Антипаттерны

  • Переабстрагированные модули: «универсальный» модуль с десятками флагов, который умеет создать что угодно. Его сложнее читать, чем обычные ресурсы, а любое изменение рискует задеть всех пользователей.
  • Модули-обёртки, которые лишь переименовывают аргументы одного ресурса и ничего не добавляют.
  • Глубокая вложенность: модули, вызывающие модули на несколько уровней вниз, делают план непонятным.
  • Скопированные окружения, которые со временем расходятся.
  • Захардкоженные значения вроде региона, ID аккаунтов или CIDR внутри переиспользуемого модуля.
  • Один гигантский state на всё, так что каждый apply затрагивает всю компанию.

Хорошее правило: пишите модуль, когда у него есть минимум два реальных применения или когда он закрепляет стандарт, который нужно соблюдать везде одинаково.

Частые вопросы

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

Поможем с этим

Если хотите не разбираться самим, а получить результат — этим мы занимаемся каждый день.

Обсудить проект

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

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.