core code helper

Terraform: state, модулі та безпечний workflow

Що таке state і чому його не можна тримати локально, як структурувати код модулями, як рефакторити без перестворення ресурсів і як не знищити продакшен одним apply.

0 імпортували Імпортувати собі

Terraform описує інфраструктуру кодом: мережі, бази, кластери, DNS-записи. Синтаксис HCL засвоюється за день. Складнощі починаються пізніше — зі state, структурою коду і роботою в команді.

Примітка

Усе нижче так само стосується OpenTofu — відкритого форку Terraform під егідою Linux Foundation. Команди відрізняються лише назвою бінарника: tofu замість terraform.

State: пам'ять Terraform

Terraform зберігає у файлі state відповідність між ресурсами в коді і реальними об'єктами в хмарі (aws_db_instance.main → db-7f3k2). Без нього він не знає, що вже створено.

Правила роботи зі state:

  1. Тільки віддалений backend з блокуванням — інакше двоє людей одночасно зіпсують state.
  2. Шифрування і обмежений доступ: у state у відкритому вигляді лежать паролі баз, ключі та інші секрети.
  3. Ніколи не редагуйте руками — лише через terraform state команди або блоки moved / import / removed.
terraform {
  required_version = ">= 1.9"
  backend "s3" {
    bucket       = "acme-terraform-state"
    key          = "production/network.tfstate"
    region       = "eu-central-1"
    encrypt      = true
    use_lockfile = true   # блокування через S3 без окремої таблиці DynamoDB
  }
  required_providers {
    aws = { source = "hashicorp/aws", version = "~> 6.0" }
  }
}

Розділяйте state

Один гігантський state на все — повільний plan і величезний радіус ураження. Ділять зазвичай за середовищем і шаром:

live/
  production/
    network/      # VPC, підмережі — змінюються рідко
    database/
    eks/
    apps/
  staging/
    ...
modules/
  vpc/
  rds-postgres/
  eks-cluster/

Модулі

Модуль — це папка з .tf-файлами, у якої є вхідні змінні й вихідні значення.

# modules/rds-postgres/variables.tf
variable "name"           { type = string }
variable "instance_class" {
  type    = string
  default = "db.t4g.medium"
}
variable "subnet_ids"     { type = list(string) }

# live/production/database/main.tf
module "orders_db" {
  source         = "../../../modules/rds-postgres"
  name           = "orders"
  instance_class = "db.r7g.large"
  subnet_ids     = data.terraform_remote_state.network.outputs.private_subnet_ids
}
Порада

Не робіть модуль на кожен ресурс. Хороший модуль описує логічну одиницю («база з параметрами, бекапами і алармами»), а не обгортає один aws_s3_bucket з тими самими аргументами.

Безпечний workflow

terraform fmt -check
terraform validate
terraform plan -out=tfplan        # зберегти план
terraform show tfplan             # уважно прочитати
terraform apply tfplan            # застосувати рівно те, що бачили

Читайте план, звертаючи увагу на символи:

Символ Значення
+ створення
~ зміна на місці
-/+ знищення і створення заново
- знищення
Обережно

-/+ біля бази даних означає, що Terraform видалить її і створить порожню. Причиною може бути зміна аргументу, що вимагає перестворення (наприклад, ідентифікатор чи зона доступності). Для критичних ресурсів додавайте lifecycle { prevent_destroy = true }.

Рефакторинг без перестворення

Перейменували ресурс або перенесли в модуль — Terraform вирішить, що старий треба знищити, а новий створити. Блок moved пояснює, що це той самий об'єкт:

moved {
  from = aws_db_instance.main
  to   = module.orders_db.aws_db_instance.this
}

А ресурси, створені вручну колись давно, можна взяти під керування блоком import:

import {
  to = aws_s3_bucket.logs
  id = "acme-legacy-logs"
}

Terraform у CI

  • на пул-реквест — fmt, validate, plan і коментар з планом у PR;
  • apply — лише після мерджу, з тим самим збереженим планом;
  • доступ до хмари — через OIDC, без довгоживучих ключів у секретах CI;
  • сканери конфігурацій (tflint, Checkov, Trivy) ловлять відкриті security groups і бакети без шифрування ще до apply.

Drift

Хтось змінив налаштування в консолі хмари — код і реальність розійшлися. Регулярний terraform plan за розкладом (наприклад, щоночі) показує такий drift. Виправляють його або поверненням до коду, або оновленням коду під реальність — але не ігноруванням.

Ви перенесли ресурс у модуль, і plan показує знищення та створення. Як уникнути перестворення?

Додати блок moved зі старою і новою адресою ресурсу (або виконати terraform state mv). Тоді Terraform зрозуміє, що це той самий об'єкт, і план не міститиме знищення.

Збережіть статтю собі

Увійдіть, щоб імпортувати її у власні нотатки, відмічати вивчене, ставити лайки й питати спільноту. Реєстрація безкоштовна: вхід через Google займає кілька секунд.

Увійти через Google Інші способи входу