Terraform: инфраструктура как код
Terraform (HashiCorp) позволяет описывать серверную инфраструктуру в виде конфигурационных файлов .tf и управлять ею через CLI. Вместо ручного создания серверов через веб-консоль — весь стек в репозитории Git.
Установка Terraform
wget https://releases.hashicorp.com/terraform/1.7.0/terraform_1.7.0_linux_amd64.zip
unzip terraform_1.7.0_linux_amd64.zip
mv terraform /usr/local/bin/
terraform --versionПример: Hetzner Cloud
# main.tf
terraform {
required_providers {
hcloud = {
source = "hetznercloud/hcloud"
version = "~> 1.44"
}
}
}
provider "hcloud" {
token = var.hcloud_token
}
# SSH ключ
resource "hcloud_ssh_key" "default" {
name = "my-key"
public_key = file("~/.ssh/id_rsa.pub")
}
# Сервер
resource "hcloud_server" "web" {
name = "web-01"
image = "ubuntu-24.04"
server_type = "cx22"
location = "nbg1"
ssh_keys = [hcloud_ssh_key.default.id]
user_data = <<-EOF
#!/bin/bash
apt update && apt install -y nginx
systemctl enable nginx
EOF
}
# Firewall
resource "hcloud_firewall" "web_fw" {
name = "web-firewall"
rule {
direction = "in"
protocol = "tcp"
port = "80"
source_ips = ["0.0.0.0/0", "::/0"]
}
rule {
direction = "in"
protocol = "tcp"
port = "443"
source_ips = ["0.0.0.0/0", "::/0"]
}
}
resource "hcloud_firewall_attachment" "web" {
firewall_id = hcloud_firewall.web_fw.id
server_ids = [hcloud_server.web.id]
}
output "server_ip" {
value = hcloud_server.web.ipv4_address
}# variables.tf
variable "hcloud_token" {
sensitive = true
}# terraform.tfvars (в .gitignore!)
hcloud_token = "your-hetzner-api-token"Основные команды
# Инициализация (скачать провайдеры)
terraform init
# Просмотр плана изменений
terraform plan
# Применить изменения
terraform apply
# Посмотреть состояние
terraform show
terraform state list
# Уничтожить инфраструктуру
terraform destroyНесколько серверов с count
resource "hcloud_server" "app" {
count = 3
name = "app-${count.index + 1}"
image = "ubuntu-24.04"
server_type = "cx22"
location = "nbg1"
ssh_keys = [hcloud_ssh_key.default.id]
}
output "app_ips" {
value = hcloud_server.app[*].ipv4_address
}terraform.tfvars и *.tfstate в репозиторий — они содержат токены и секреты. Добавьте в .gitignore.terraform.tfstate в Terraform Cloud, S3 или Hetzner Object Storage — это позволяет нескольким людям управлять одной инфраструктурой.Разбор реальных ошибок Terraform
Две ошибки почти гарантированно встретятся при командной работе с Terraform, и обе связаны не с синтаксисом HCL, а с состоянием.
Error acquiring the state lock
Error: Error acquiring the state lock
Lock Info:
ID: 7a3f1c2e-...
Path: terraform.tfstate
Operation: OperationTypeApply
Who: ci-runner@build-42Кто-то уже выполняет apply, либо предыдущий процесс упал и не снял блокировку. Прежде чем разблокировать, убедитесь, что параллельного запуска действительно нет — иначе рискуете повредить state. Только после этого: terraform force-unlock 7a3f1c2e-....
Inconsistent dependency lock file
Error: Inconsistent dependency lock file
The following dependency selections recorded in the lock file are inconsistent with the current configuration.
To update the locked dependency selections to match a change in the configuration, run: terraform init -upgradeВерсия провайдера в .terraform.lock.hcl не совпадает с той, что указана в блоке required_providers — обычно после обновления версии вручную в коде без пересборки лока. Решение прямо в тексте ошибки: terraform init -upgrade, затем закоммитить обновлённый lock-файл.
Terraform vs Ansible
| Критерий | Terraform | Ansible |
|---|---|---|
| Модель | Декларативная, с состоянием (state) | Процедурная, без состояния по умолчанию |
| Основная задача | Создание инфраструктуры (серверы, сети, DNS) | Настройка ОС внутри уже существующего сервера |
| Идемпотентность | Гарантирована через сравнение с state | Зависит от качества написанных модулей и задач |
Когда Terraform не подходит
- Разовое изменение одного сервера — полноценный цикл plan/apply избыточен, быстрее и понятнее прямой вызов API или CLI провайдера.
- Настройка ПО внутри сервера — установка пакетов, шаблоны конфигов, systemd-юниты. Для этого нужен Ansible, Terraform лишь создаёт железо и запускает cloud-init.
- Нужна сложная императивная логика с циклами и условной обработкой ошибок — декларативный HCL для этого плохо приспособлен.
- Один сервер, одна команда из двух человек, инфраструктура почти никогда не меняется — оверхед на remote state и HCL не окупается.
- Нужен мгновенный откат инфраструктуры без пересчёта плана —
terraform applyиdestroyне транзакционны и требуют времени на выполнение каждого ресурса.
Проверка результата
# План должен быть пустым после успешного apply — признак идемпотентности
terraform plan -detailed-exitcode
# exit code 0 = изменений нет, 2 = есть расхождения, 1 = ошибка
# Синтаксис и форматирование в порядке
terraform validate
terraform fmt -check -recursive
# Сверить состояние с реальностью у провайдера
terraform state list
hcloud server listЕсли terraform plan -detailed-exitcode возвращает 2 сразу после apply — где-то есть дрейф: либо ресурс поменяли руками через веб-консоль, либо провайдер сам изменил атрибут (например, назначил другой IP). Регулярно прогоняйте terraform plan по расписанию, чтобы такой дрейф не накапливался незаметно.