К основному содержимому

Terraform: инфраструктура как код для VPS и облака

Облако и DevOps · 19.04.2026 · 4 мин чтения
Иллюстрация к статье «Terraform: инфраструктура как код для VPS и облака»

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.
Remote State: Для командной работы храните 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

КритерийTerraformAnsible
МодельДекларативная, с состоянием (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 по расписанию, чтобы такой дрейф не накапливался незаметно.

Статья помогла?
← Назад в базу знаний Задать вопрос поддержке