Какую структуру файлов и директорий вы используете в проекте Terraform?

«Какую структуру файлов и директорий вы используете в проекте Terraform?» — вопрос из категории Terraform, который задают на 23% собеседований Devops Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Базовая структура проекта (стандартная):

terraform-project/
├── modules/           # Повторно используемые модули
│   ├── network/       # Модуль VPC, подсетей, шлюзов
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   ├── outputs.tf
│   │   └── README.md
│   └── compute/       # Модуль EC2/инстансов
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
├── environments/      # Конфигурации для разных окружений (рабочих пространств)
│   ├── dev/
│   │   ├── main.tf          # Вызов модулей для dev
│   │   ├── variables.tf     # Переменные для dev
│   │   ├── terraform.tfvars # Значения переменных dev
│   │   └── backend.tf       # Конфигурация бэкенда S3 для состояния dev
│   ├── staging/
│   └── prod/
├── globals.tf         # Глобальные переменные (регион, теги по умолчанию)
└── versions.tf        # Ограничения версий провайдеров и Terraform

Ключевые принципы:

  1. Разделение по окружениям: Каждое окружение (dev, staging, prod) имеет свою директорию с собственным файлом состояния (.tfstate), что изолирует риски.
  2. Модульность: Повторяющаяся логика (создание VPC, security groups, RDS) выносится в модули в директории modules/. Это обеспечивает согласованность и упрощает тестирование.
  3. Управление состоянием: Бэкенд (например, S3 + DynamoDB для блокировок) настраивается отдельно для каждого окружения в backend.tf, чтобы состояния не хранились локально.
  4. Четкое именование переменных: variables.tf содержит объявления, а terraform.tfvars или *.auto.tfvars — конкретные значения для окружения.

Пример вызова модуля из environments/dev/main.tf:

module "network" {
  source = "../../modules/network"

  vpc_cidr         = "10.0.0.0/16"
  public_subnets   = ["10.0.1.0/24", "10.0.2.0/24"]
  environment_name = "dev"
}

Такая структура позволяет легко масштабировать проект, применять изменения точечно к окружениям и соблюдать принципы Infrastructure as Code.