SSD Nodes Learn 🎉 VPS от $5.50/мес
Руководства Matt ConnorАвтор: Matt Connor · Обновлено 2026-08-07

Ansible или Terraform: что выбрать для задач

Terraform управляет инфраструктурой, а Ansible настраивает ПО внутри серверов. Узнайте, как правильно разделить зоны ответственности и почему эти инструменты дополняют друг друга.

Ansible против Terraform в одном предложении

Ansible и Terraform — это не выбор между двумя инструментами, выполняющими одну и ту же задачу. Terraform описывает состояние инфраструктуры: серверы, диски, сети, DNS-записи. Ansible описывает состояние внутри уже существующей машины: пакеты, пользователей, конфигурационные файлы, запущенные службы. Terraform создает VPS. Ansible превращает этот VPS в веб-сервер.

Оба инструмента декларативны, и оба относятся к категории infrastructure as code (IaC). Реальное различие заключается в том, что они «помнят». Terraform записывает файл состояния (state file), который сопоставляет каждый ресурс в коде с реальным объектом, созданным через API. Благодаря этому он понимает, что удаление пяти строк кода означает необходимость уничтожения одного сервера. Ansible не сохраняет информацию между запусками. Он подключается по SSH, проверяет состояние машины и изменяет только то, что не соответствует playbook.

Это единственное различие объясняет всё остальное в данном руководстве, включая причины, по которым попытка объединить обе задачи в одном инструменте приводит к ошибкам.

Что на самом деле делает Terraform

Terraform взаимодействует с API через плагин провайдера. Страница реестра вашего провайдера определяет типы ресурсов, которые вы можете описывать, поэтому сервер на одном хосте и сервер на другом — это разные имена ресурсов с разными аргументами.

resource "cloud_server" "web" {
  name  = "web1"
  image = "ubuntu-24.04"
  type  = "small"
}

output "web_ip" {
  value = cloud_server.web.ipv4_address
}

Замените cloud_server на тип ресурса, указанный в документации вашего провайдера. Блок output — это важная часть данного руководства, поскольку именно так адрес передается из Terraform.

terraform init
terraform fmt -check
terraform validate
terraform plan -out=tfplan
terraform apply tfplan

Команда terraform init загружает провайдер и создает файл блокировки. Команда terraform plan выводит разницу между вашим кодом и файлом состояния, завершаясь строкой вида Plan: 1 to add, 0 to change, 0 to destroy.. Читайте эту строку каждый раз. Некоторые аргументы нельзя изменить на месте, и план указывает на это с помощью # forces replacement рядом с атрибутом, за которым следует 1 to add, 0 to change, 1 to destroy. Применение такого плана удаляет сервер и создает новый пустой, именно так люди теряют данные, которые считали защищенными.

Сохранение плана в файл и его последующее применение вместо запуска простого terraform apply гарантирует, что выполняется именно то, что вы проверили. В промежутке между двумя командами кто-то другой может изменить инфраструктуру.

terraform.tfstate — это память системы. Потеряете его, и Terraform перестанет понимать, что эти серверы принадлежат вам, поэтому следующий запуск apply попытается создать дубликаты. Храните его в удаленном бэкенде, как только команды начинают запускать более одного человека, так как одновременное применение настроек двумя пользователями приводит к следующему:

Error: Error acquiring the state lock

OpenTofu — это форк Terraform с теми же командами и форматом файлов. По состоянию на июль 2026 года все инструкции в этом руководстве работают, если вы введете tofu вместо terraform.

Что на самом деле делает Ansible

Ansible не требует агента или API. Он устанавливает SSH-соединение, копирует небольшой Python-модуль на целевой узел, выполняет его и удаляет. Ansible может настроить всё, к чему есть доступ по SSH с паролем sudo.

- name: Base web server
  hosts: web
  become: true
  tasks:
    - name: Install nginx
      ansible.builtin.apt:
        name: nginx
        state: present
        update_cache: true

    - name: Ensure nginx is running at boot
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: true
ansible -i inventory.ini web -m ansible.builtin.ping
ansible-playbook -i inventory.ini site.yml --check --diff
ansible-playbook -i inventory.ini site.yml

Модуль ping позволяет проверить работу SSH, Python и sudo перед началом отладки playbook. Успешный результат выглядит как web1 | SUCCESS => {"ping": "pong"}. Запуск --check --diff — это ближайший аналог плана в Ansible: он сообщает, какие изменения будут внесены, не применяя их на самом деле. Однако задачи, зависящие от предыдущих шагов, могут отображаться некорректно, так как предыдущие изменения не были фактически выполнены.

Каждый запуск завершается сводкой, например ok=6 changed=2 unreachable=0 failed=0. Запустите один и тот же playbook дважды. Второй запуск должен показать changed=0. Задача, которая сообщает об изменениях при каждом запуске, не является идемпотентной; обычно это задача command или shell, которую следовало реализовать с помощью штатного модуля. Если вы новичок, начните с первого playbook Ansible на одном VPS и постепенно расширяйте его.

Где инструменты пересекаются и где они конфликтуют

Terraform может выполнять команды на новом сервере с помощью provisioner remote-exec. В документации HashiCorp provisioner называют крайним средством. На то есть веские причины.

Provisioner запускается только в момент создания ресурса. Если изменить скрипт, на существующем сервере ничего не произойдет, так как с точки зрения Terraform ресурс уже соответствует коду. Шаги provisioner никогда не отображаются в terraform plan, поэтому в процессе ревью вы их не увидите. Если скрипт завершится с ошибкой, Terraform пометит ресурс как tainted, и при следующем apply сервер, который, вероятно, был исправен, будет удален и создан заново.

Кроме того, сбой часто происходит в неподходящий момент. Провайдер сообщает о создании сервера сразу после ответа API, в то время как операционная система еще загружается, а sshd еще не слушает порты.

Error: remote-exec provisioner error
timeout - last error: dial tcp 203.0.113.10:22: connect: connection refused

У Ansible есть обратное искушение. Облачные модули могут создавать серверы, и для небольшого количества машин это работает. Но вы теряете граф зависимостей и файл состояния. Ansible без проблем создаст ресурс, но если удалить задачу из playbook, ресурс продолжит работать и за него будут списываться деньги, так как нигде не зафиксировано, что он принадлежит вам.

Правило, которое из этого следует: пусть Terraform управляет объектами, которые создаются и удаляются через API, а Ansible управляет всем, что находится внутри загруженной операционной системы.

Передача управления: успешно

Передача управления — это граница, а не интеграция. Terraform завершает работу, публикует адрес и останавливается. Ansible начинает работу с этого адреса.

terraform apply -auto-approve
terraform output -raw web_ip
printf '[web]\n%s ansible_user=root\n' "$(terraform output -raw web_ip)" > inventory.ini
ansible -i inventory.ini web -m ansible.builtin.ping
ansible-playbook -i inventory.ini site.yml

terraform output -raw выводит одно значение без кавычек и без JSON-обертки, что и требуется при подстановке в shell. Для нескольких серверов используйте terraform output -json и формируйте инвентарь на его основе, так как -raw работает только с одной строкой, числом или логическим значением.

Шаг ping между двумя инструментами стоит сохранить. Он позволяет отделить ситуацию «Terraform выдал неверный адрес» от ситуации «в моем playbook есть ошибка», так как эти две проблемы выглядят идентично, если playbook — это первое, что обращается к новому серверу.

Чтение состояния Terraform в качестве инвентаря Ansible

Если вы не хотите создавать файл инвентаря вручную, коллекция cloud.terraform позволяет считывать состояние напрямую.

ansible-galaxy collection install cloud.terraform

Создайте файл terraform.yml рядом с вашим playbook:

plugin: cloud.terraform.terraform_provider
project_path: /home/deploy/infra
ansible-inventory -i terraform.yml --graph
ansible-playbook -i terraform.yml site.yml

Перед использованием учтите два момента. Плагин запускает terraform show в директории project_path, поэтому она должна быть предварительно инициализирована, иначе плагин завершится с ошибкой. Кроме того, он не создает хосты автоматически на основе ресурсов сервера: он считывает ресурсы ansible_host и ansible_group, которые вы объявляете в коде Terraform с помощью провайдера Ansible. В ansible-inventory --graph ничего не появится, пока вы их не добавите.

Обычный сгенерированный файл инвентаря проще отлаживать, и он работает с любым провайдером. Использование плагина становится оправданным, когда инвентарь вырастает до нескольких десятков машин и ручное редактирование начинает приводить к опечаткам. Это тот же момент, когда управление несколькими серверами Linux с одной управляющей машины превращается из привычки в полноценный рабочий процесс.

Действительно ли вам нужен Terraform?

Большинству читателей этого текста он не нужен, по крайней мере пока. Terraform окупает себя, когда создание и удаление инфраструктуры становится повторяющейся задачей. Если вы заказали один VPS через панель управления и планируете использовать его два года, Terraform лишь описывает действие, которое происходит один раз, и создает файл состояния, который нельзя потерять.

Используйте Terraform, если вы часто пересобираете окружения, если staging должен в точности соответствовать production, если инфраструктуру меняют несколько человек и вам нужен план для проверки перед удалением ресурсов, или если вы управляете не только серверами, но и DNS-записями, балансировщиками нагрузки и правилами брандмауэра через API провайдера.

Оставайтесь на одном Ansible, если серверов мало и они живут долго, а ежедневный вопрос звучит как «правильно ли настроен этот сервер», а не «существует ли этот сервер». Один playbook для первичной настройки сервера выполняет те же задачи, что и первые десять минут работы на новом VPS, но с преимуществом: он точно так же отработает на следующем сервере.

Порядок обучения вытекает из этого. Ansible приносит пользу уже на первом вашем сервере. Terraform приносит пользу на третьем окружении, которое вы пересобираете.

Что ломается при передаче управления

Сервер не готов. Terraform завершается успешно, Ansible сразу выдает ошибку.

fatal: [web1]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 203.0.113.10 port 22: Connection refused", "unreachable": true}

API вернул адрес до того, как sshd начал прослушивание. Дождитесь открытия порта вместо использования фиксированной задержки. В Ansible для этого есть ansible.builtin.wait_for_connection, выполните его как первую задачу в play.

Изменился ключ хоста. Вы удалили и создали сервер заново, и новый сервер отвечает по тому же адресу с новым ключом.

Host key verification failed.

Удалите устаревшую запись с помощью ssh-keygen -R 203.0.113.10. Это постоянно происходит, когда Terraform выполняет пересборку, что является веской причиной избегать частых пересозданий на машинах, где хранятся данные.

Ошибка sudo. fatal: [web1]: FAILED! => {"msg": "Missing sudo password"} означает, что become: true требует пароль на этом хосте. Либо настройте sudo без пароля для пользователя, выполняющего развертывание, либо передайте --ask-become-pass.

Terraform хочет удалить то, что вы не меняли. План показывает изменения, которые вы не вносили. Это значит, что реальная инфраструктура разошлась с кодом, обычно из-за того, что кто-то изменил настройки в веб-панели провайдера. Запустите terraform plan -refresh-only, чтобы увидеть разницу, а затем решите, что неверно: код или текущий ресурс. Никогда не применяйте деструктивный план, если не можете объяснить каждую его строку.

Ansible сообщает об изменениях при каждом запуске. Задача shell без проверки creates или when выполняется безусловно. Это не косметическая проблема, так как это означает, что вы больше не можете использовать changed=0 как сигнал того, что сервер находится в требуемом состоянии.

FAQ

Может ли Terraform заменить Ansible?

Нет, если речь идет о настройке внутри сервера. Terraform может вызывать скрипты с помощью провизионера remote-exec, но они выполняются только при создании ресурса, не отображаются в terraform plan и помечают ресурс как «испорченный» (tainted) при сбое, что приводит к его удалению и пересозданию при следующем запуске. В Terraform нет аналога модуля, который проверяет, установлен ли уже nginx, и ничего не делает, если он есть. Используйте Terraform для создания машины, а затем передавайте управление Ansible.

Может ли Ansible заменить Terraform?

Для небольшого количества серверов с длительным сроком жизни — да. У Ansible есть облачные модули для создания серверов, и если вам нужно заказать два VPS и оставить их работать, этого достаточно. Вы теряете файл состояния (state file) и граф зависимостей: если удалить задачу из плейбука, ресурс продолжит работать и потреблять ресурсы, так как Ansible не фиксирует факт его создания. Terraform в такой ситуации запланировал бы удаление.

Что изучать в первую очередь?

Ansible, если у вас уже есть серверы. Он окупается на первой же машине, не требует ничего, кроме SSH, а навыки применимы к серверу, который вы настроили вручную. Terraform окупается позже, когда вы начинаете многократно пересобирать окружения или управлять ресурсами провайдера помимо серверов, например, DNS-записями и правилами брандмауэра.

Как передать IP нового сервера из Terraform в Ansible?

Объявите output в коде Terraform, а затем считайте его после применения конфигурации. terraform output -raw web_ip выводит чистое значение для подстановки в оболочке, а terraform output -json выдает все выходные данные сразу, если хостов несколько. Запишите их в файл инвентаризации или установите коллекцию cloud.terraform и укажите ansible-inventory -i terraform.yml --graph путь к каталогу проекта.

Почему мой плейбук завершается ошибкой сразу после завершения работы Terraform?

Провайдер сообщает о создании сервера, как только API подтверждает запрос, в то время как операционная система еще загружается, поэтому в первые секунды SSH отклоняет соединения. Ошибка выглядит как UNREACHABLE! с Connection refused. Сделайте ansible.builtin.wait_for_connection первой задачей в плей, вместо того чтобы угадывать время ожидания, так как время загрузки зависит от образа и тарифного плана.