Loading...
Усі статті
Infrastructure · 6 min read

Найкращі практики Terraform: структуруємо ваш IaC для масштабування

Дізнайтеся, як структурувати Terraform-проєкти для зручності супроводу, командної співпраці та продакшн-інфраструктури масштабу.

Чому структура Terraform має значення

Кожна інфраструктурна команда починає однаково: один файл main.tf, що провізіонить кілька ресурсів. Це чудово працює для proof of concept. Потім проєкт зростає, додається більше інженерів, і раптом цей моноліт має 2,000 рядків, ніхто не знає, що від чого залежить, і кожен terraform plan займає вісім хвилин.

Знайомо? Спосіб, у який ви структуруєте Terraform-код, безпосередньо впливає на швидкість поставки команди, на безпечність змін та легкість онбордингу нових інженерів. У цьому посібнику ми ділимося патернами, які використовуємо в DevOpsVibe у десятках продакшн-середовищ.

Архітектура на основі модулів

Найважливіше рішення, яке ви можете прийняти, — рано перейти на архітектуру на основі модулів. Модулі — це повторно використовувані, тестовані одиниці інфраструктури, що інкапсулюють логічне групування ресурсів.

Структура каталогів

Ось структура, яку ми рекомендуємо для середніх та великих проєктів:

infrastructure/
├── modules/
│   ├── networking/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   ├── outputs.tf
│   │   └── README.md
│   ├── compute/
│   ├── database/
│   └── monitoring/
├── environments/
│   ├── dev/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   ├── terraform.tfvars
│   │   └── backend.tf
│   ├── staging/
│   └── production/
├── global/
│   ├── iam/
│   └── dns/
└── terragrunt.hcl  # optional

Кожен каталог середовища компонує модулі разом зі специфічними для середовища змінними. Самі модулі не містять жодних hardcoded значень.

Написання повторно використовуваного модуля

Добре структурований модуль має чіткі входи, виходи та одну відповідальність:

# modules/networking/variables.tf
variable "vpc_cidr" {
  description = "CIDR block for the VPC"
  type        = string

  validation {
    condition     = can(cidrnetmask(var.vpc_cidr))
    error_message = "Must be a valid CIDR block."
  }
}

variable "environment" {
  description = "Environment name (dev, staging, production)"
  type        = string
}

variable "availability_zones" {
  description = "List of AZs to use"
  type        = list(string)
}

# modules/networking/main.tf
resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name        = "${var.environment}-vpc"
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

resource "aws_subnet" "private" {
  count             = length(var.availability_zones)
  vpc_id            = aws_vpc.main.id
  cidr_block        = cidrsubnet(var.vpc_cidr, 8, count.index)
  availability_zone = var.availability_zones[count.index]

  tags = {
    Name        = "${var.environment}-private-${var.availability_zones[count.index]}"
    Environment = var.environment
    Type        = "private"
  }
}

# modules/networking/outputs.tf
output "vpc_id" {
  description = "ID of the created VPC"
  value       = aws_vpc.main.id
}

output "private_subnet_ids" {
  description = "List of private subnet IDs"
  value       = aws_subnet.private[*].id
}

Стратегія управління state

Віддалений state — це не предмет переговорів для команд. Використовуйте S3 з DynamoDB locking (AWS) або GCS з locking (GCP):

# environments/production/backend.tf
terraform {
  backend "s3" {
    bucket         = "mycompany-terraform-state"
    key            = "production/networking/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}

Ключові правила управління state:

  • Один файл state на компонент на середовище. Ніколи не кладіть всю інфраструктуру в один state-файл. Якщо state вашої VPC буде пошкоджено, ви не хочете, щоб він забрав і базу даних.
  • Увімкніть шифрування at rest. State-файли містять чутливі дані, включно з паролями та приватними ключами.
  • Використовуйте state locking. Без нього два інженери, які одночасно запускають terraform apply, можуть пошкодити ваш state.
  • Ніколи не комітьте state-файли в систему контролю версій. Додайте *.tfstate і *.tfstate.backup до вашого .gitignore.

Управління змінними

Уникайте hardcoding значень. Використовуйте шаровий підхід до змінних:

# environments/production/terraform.tfvars
environment        = "production"
vpc_cidr           = "10.0.0.0/16"
availability_zones = ["us-east-1a", "us-east-1b", "us-east-1c"]
instance_type      = "m6i.xlarge"
min_capacity       = 3
max_capacity       = 10

Для секретів ніколи не зберігайте їх у файлах .tfvars. Натомість посилайтеся на них з менеджера секретів:

data "aws_secretsmanager_secret_version" "db_password" {
  secret_id = "production/database/master-password"
}

resource "aws_db_instance" "main" {
  password = data.aws_secretsmanager_secret_version.db_password.secret_string
  # ...
}

Стратегія тегування

Послідовне тегування є необхідним для розподілу витрат, аудиту безпеки та управління ресурсами. Визначте спільний модуль тегування:

# modules/tags/main.tf
variable "environment" { type = string }
variable "project" { type = string }
variable "team" { type = string }

locals {
  common_tags = {
    Environment = var.environment
    Project     = var.project
    Team        = var.team
    ManagedBy   = "terraform"
    Repository  = "github.com/mycompany/infrastructure"
  }
}

output "tags" {
  value = local.common_tags
}

Потім використовуйте його скрізь:

module "tags" {
  source      = "../../modules/tags"
  environment = "production"
  project     = "platform"
  team        = "infrastructure"
}

resource "aws_instance" "app" {
  # ...
  tags = merge(module.tags.tags, {
    Name = "app-server"
    Role = "application"
  })
}

CI/CD інтеграція

Terraform ніколи не повинен запускатися з ноутбука розробника в продакшні. Налаштуйте конвеєр:

  1. Pull request відкривається -- terraform fmt -check і terraform validate запускаються автоматично
  2. PR схвалено -- terraform plan запускається, а вивід публікується як коментар до PR
  3. PR злито в main -- terraform apply -auto-approve виконується в конвеєрі

Використовуйте такі інструменти, як Atlantis або Spacelift, для цього робочого процесу або побудуйте власний з GitHub Actions:

# .github/workflows/terraform.yml
name: Terraform
on:
  pull_request:
    paths: ['infrastructure/**']

jobs:
  plan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - run: terraform init
        working-directory: infrastructure/environments/production
      - run: terraform plan -no-color -out=tfplan
        working-directory: infrastructure/environments/production
      - uses: actions/github-script@v7
        with:
          script: |
            const output = require('fs').readFileSync('tfplan.txt', 'utf8');
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `## Terraform Plan\n\`\`\`\n${output}\n\`\`\``
            });

Тестування вашої інфраструктури

Використовуйте Terratest або terraform test (вбудований з Terraform 1.6) для валідації ваших модулів:

# modules/networking/tests/vpc.tftest.hcl
run "creates_vpc_with_correct_cidr" {
  command = plan

  variables {
    vpc_cidr           = "10.0.0.0/16"
    environment        = "test"
    availability_zones = ["us-east-1a"]
  }

  assert {
    condition     = aws_vpc.main.cidr_block == "10.0.0.0/16"
    error_message = "VPC CIDR block did not match expected value"
  }
}

Поширені анти-патерни, яких слід уникати

  • Монолітні state-файли. Розділяйте за компонентом і середовищем.
  • Використання count, коли for_each доречніший. for_each з мапами дає вам стабільні адреси ресурсів, які не зміщуються при додаванні чи видаленні елементів.
  • Ігнорування дрифту. Запускайте terraform plan за розкладом, щоб виявляти ручні зміни.
  • Пропуск terraform fmt. Забезпечуйте форматування в CI. Непослідовне форматування створює шумні diff'и.
  • Hardcoding версій провайдерів. Закріплюйте їх явно, але регулярно переглядайте оновлення.

Висновок

Добре структурування Terraform з самого початку економить експоненційні зусилля пізніше. Описані вище патерни -- архітектура на основі модулів, ізольований state, шарові змінні, CI/CD автоматизація та тестування -- становлять фундамент кожного масштабованого інфраструктурного проєкту, який ми постачаємо.

У DevOpsVibe ми допомагаємо командам проєктувати та впроваджувати Terraform-архітектури, що масштабуються від кількох ресурсів до тисяч. Чи ви починаєте з нуля, чи розплутуєте існуючу кодову базу, наші інженери можуть поставити вашу інфраструктуру на тверду основу. Зв'яжіться з нами, щоб дізнатися більше.

у категорії
terraformterraformiacinfrastructureawsawsdevopsautomation
працювати з нами

Хочете, щоб наша команда допомогла з вашою інфраструктурою?

talk to an engineerFree 30-min discovery callBook
close