Правильная работа с Terraform — это не просто написать код, а выстроить процесс — от создания модулей до CI/CD. В этой статье разберем, как начать, ничего не сломать и потом это нормально масштабировать.
Что такое Terraform и зачем он нужен
Terraform — это инструмент для управления инфраструктурой проекта как кодом от HashiCorp. Он позволяет управлять облачными и локальными ресурсами через декларативные конфигурационные файлы. С этим инструментом можно описывать, версионировать и безопасно разворачивать инфраструктуру любого масштаба.
Принцип декларативного подхода: «что», а не «как»
В отличие от императивных инструментов, где вы пишете пошаговые инструкции, в Terraform вы описываете желаемое состояние:
# Мы говорим: "нужна виртуальная машина с такими параметрами" resource "yandex_compute_instance" "web" { name = "prod-web-01" platform_id = "standard-v3" resources { cores = 2 memory = 4 } # Terraform сам решит, как ее создать, где разместить и какие зависимости учесть }
Сравнение с другими инструментами
Terraform не заменяет Ansible. Часто их используют вместе: Terraform создает виртуальные машины, Ansible настраивает программное обеспечение внутри.
Terraform — идеальный выбор, если:
- вы используете нескольких облачных провайдеров в связке (AWS + Yandex Cloud + on-prem);
- команда хочет версионировать инфраструктуру как код;
- нужно быстро поднимать/уничтожать окружения (dev/stage/prod);
- нужно проводить аудит изменений — то есть смотреть, кто, когда и что поменял.
Быстрый старт: как установить Terraform и создать первый ресурс
Установить Terraform легко. Для этого используйте следующие команды.
Для macOS:
brew install terraform
Для Linux (Ubuntu/Debian):
sudo apt update && sudo apt install -y terraform
Для Windows:
choco install terraformс
Проверьте установку. Для этого введите в командной строке:
terraform version # В ответ увидите сообщение # Terraform v1.9.0
Настройка провайдера (на примере Yandex Cloud)
- Создайте сервисный аккаунт в консоли Yandex Cloud.
- Получите access_key и secret_key.
- Создайте файл provider.tf:
terraform { required_providers { yandex = { source = "yandex-cloud/yandex" version = "0.120.0" # Фиксируем версию! } } }
provider "yandex" { cloud_id = var.cloud_id folder_id = var.folder_id zone = "ru-central1-a" # Секреты — только через переменные окружения! service_account_key_file = var.sa_key_file }
Три команды Terraform, которые нужно запомнить
Пример: поднимаем виртуальную машину с переменными через Terraform
variables.tf:
variable "instance_name" { type = string description = "Имя виртуальной машины" default = "web-server" }
variable "instance_cores" { type = number description = "Количество CPU-ядер" default = 2 }
main.tf:
resource "yandex_compute_instance" "web" { name = var.instance_name platform_id = "standard-v3" resources { cores = var.instance_cores memory = var.instance_cores * 2 # Логика: памяти в 2 раза больше ядер } boot_disk { initialize_params { image_id = "fd8k7m2p5q3r8t9v0w1x" # Ubuntu 22.04 LTS size = 20 } } network_interface { subnet_id = var.subnet_id nat = true # Публичный IP } }
Запуск:
terraform init terraform plan -out=tfplan terraform apply tfplan # Или просто `apply`, но с файлом безопаснее
Совет: Всегда используйте -out=tfplan, чтобы применить ровно тот план, который вы проверили.
Язык HCL: синтаксис, который понятен человеку
HashiCorp Configuration Language (HCL) создан так, чтобы его читали и люди, и машины. Разберем основные команды в Terraform, которые необходимы для работы.
Базовые блоки: resource, variable, output
# resource — что создаем resource "yandex_compute_instance" "app" { ... }
# variable — что настраиваем извне variable "env" { type = string }
# output — что показываем после применения output "app_public_ip" { value = yandex_compute_instance.app.network_interface.0.ip_address }
Работа с переменными: var, locals, tfvars
Полезные функции HCL
# Условное создание ресурса resource "yandex_compute_instance" "debug" { count = var.enable_debug ? 1 : 0 # Создастся, только если флаг включен # ... }
# Динамическое создание нескольких ресурсов resource "yandex_compute_instance" "workers" { for_each = toset(var.worker_names) # Список имен из переменной name = "worker-${each.key}" # ... }
# Валидация входных данных variable "instance_type" { type = string validation { condition = contains(["small", "medium", "large"], var.instance_type) error_message = "Допустимые значения: small, medium, large." } }
Валидация и форматирование кода
terraform fmt -recursive # Приводит код к единому стилю terraform validate # Проверяет синтаксис и ссылки tflint # Сторонний линтер с доп. правилами
Не храните секреты в .tf-файлах. Используйте переменные окружения (TF_VAR_…) или HashiCorp Vault.
Модули Terraform: как писать код, который переиспользуется
Модуль Terraform — как LEGO-блок: собрал один раз, протестировал — и используешь везде. Без дублирования кода и риска забыть важный параметр.
Структура модуля Terraform: три файла, которые решают все
Дополнительно:
- README.md — документация для пользователей модуля;
- versions.tf — версии провайдеров и Terraform;
- examples/ — примеры использования.
Источники модулей: откуда брать и как подключать
# Локальный модуль (для внутренних библиотек) module "network" { source = "./modules/vpc" # ... }
# Git-репозиторий (внутренний или публичный) module "database" { source = "git@github.com:company/terraform-modules.git//postgres?ref=v2.1.0" # ... }
# Официальный Registry (HashiCorp или облачных провайдеров) module "k8s" { source = "terraform-aws-modules/eks/aws" version = "19.15.0" # Всегда фиксируйте версию! # ... }
Пример: модуль для создания VPC с подсетями
modules/vpc/main.tf:
resource "yandex_vpc_network" "this" { name = var.name description = var.description }
resource "yandex_vpc_subnet" "private" { count = length(var.private_cidrs) name = "${var.name}-private-${count.index + 1}" network_id = yandex_vpc_network.this.id zone = var.azs[count.index] v4_cidr_blocks = [var.private_cidrs[count.index]] private = true }
resource "yandex_vpc_subnet" "public" { count = length(var.public_cidrs) name = "${var.name}-public-${count.index + 1}" network_id = yandex_vpc_network.this.id zone = var.azs[count.index] v4_cidr_blocks = [var.public_cidrs[count.index]] }
Использование модуля:
module "prod_vpc" { source = "./modules/vpc" name = "prod-network" description = "VPC для продакшен-окружения" azs = ["ru-central1-a", "ru-central1-b"] private_cidrs = ["10.0.1.0/24", "10.0.2.0/24"] public_cidrs = ["10.0.101.0/24", "10.0.102.0/24"] tags = { Environment = "production" Team = "platform" } }
Всегда фиксируйте версию модуля (version = «x.y.z» или ref=v2.1.0). Использование latest — риск, что завтра обновление сломает ваш план.
Управление состоянием: почему terraform.tfstate так важен
State-файл — это «чертеж» вашей инфраструктуры. Terraform сравнивает желаемое (код) с фактическим (state), чтобы понять, что изменить.
Почему нельзя хранить state локально
Настройка remote backend (на примере Yandex Object Storage)
terraform { backend "s3" { endpoint = "storage.yandexcloud.net" bucket = "my-terraform-state" key = "prod/terraform.tfstate" region = "ru-central1" # Блокировка через Yandex Lock dynamodb_table = "terraform-locks" # Шифрование encrypt = true } }
Никогда не коммитьте terraform.tfstate в Git. Добавьте его в .gitignore вместе с *.tfvars, .terraform/
Workspaces vs папки: как разделять окружения
Вариант 1: Workspaces (проще, но менее изолированно)
terraform workspace new dev terraform workspace new prod terraform workspace select prod terraform apply # Применит к prod
Вариант 2: Отдельные папки (рекомендуется)
environments/ ├── dev/ │ ├── backend.tf │ ├── main.tf │ └── terraform.tfvars ├── stage/ └── prod/
Для сложных проектов используйте папки + модули. Workspaces удобны для личных тестов, но в команде лучше явное разделение
Лучшие практики Terraform для продакшена
Terraform, как и любой удобный инструмент, имеет свои уязвимости и сложности в использовании. Чтобы избежать потери важных данных или взлома, применяйте следующие правила.
Безопасность и контроль доступа
- Секреты — только через переменные окружения или Vault.
- IAM-роли с принципом наименьших привилегий.
- Шифрование state-файлов на стороне хранилища.
- Аудит: кто и когда запускал apply (через Cloud Logging).
Чек-лист перед terraform apply в продакшен
- Состояние хранится в remote backend с шифрованием.
- Используется terraform plan -out=tfplan и ревью перед применением.
- Секреты не закоммичены, используются через env/Vault.
- Модули версионированы, не используется latest.
- Запущено статическое сканирование кода (tfsec, Checkov).
- Есть откатный план, который описывает, как быстро отменить изменения.
- Уведомления о изменениях настроены (Slack, Telegram).
Частые ошибки и как их избежать
Как отменить опасные изменения
# 1. Если еще не применили — просто удалите tfplan rm tfplan # 2. Если применили, но нужно откатить: # а) Верните код к предыдущей версии в Git # б) Запустите `terraform apply` — Terraform приведет инфраструктуру к желаемому состоянию # 3. Если удалили критический ресурс по ошибке: terraform state list # Посмотреть, что «видит» Terraform terraform import <resource> <id> # Вернуть ресурс под управление
Terraform: коротко о главном
- Terraform — инструмент для декларативного описания инфраструктуры: вы пишете, «что нужно», а он решает, «как сделать».
- Старт прост: init → plan → apply. Но продакшен требует больше — модули, remote state, CI/CD.
- Модули — основа масштабируемости. Пишите их как библиотеки —: с переменными, выводами и документацией.
- Состояние (state) — критический артефакт. Храните удаленно, блокируйте при изменениях, делайте бэкапы.
- Безопасность: секреты — только через env/Vault, код — сканируйте (tfsec, Checkov), права — по принципу наименьших привилегий.
- Автоматизация: terraform plan в CI, manual approval перед apply, destroy — только с подтверждением.
- Главное правило: пишите код для людей. Комментируйте, документируйте, тестируйте изменения через plan.
