Ответ
Я придерживаюсь модульной и масштабируемой структуры, которая разделяет ответственность и упрощает управление состояниями для разных окружений.
Базовая структура проекта (стандартная):
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
Ключевые принципы:
- Разделение по окружениям: Каждое окружение (
dev,staging,prod) имеет свою директорию с собственным файлом состояния (.tfstate), что изолирует риски. - Модульность: Повторяющаяся логика (создание VPC, security groups, RDS) выносится в модули в директории
modules/. Это обеспечивает согласованность и упрощает тестирование. - Управление состоянием: Бэкенд (например, S3 + DynamoDB для блокировок) настраивается отдельно для каждого окружения в
backend.tf, чтобы состояния не хранились локально. - Четкое именование переменных:
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.