SSD Nodes Learn Hosting plans →
Посібники Matt ConnorВід Matt Connor · Оновлено 2026-08-28

Ansible чи Terraform: що потрібно саме вам

Terraform створює VPS, а Ansible налаштовує його через SSH. Розберімо різницю, проблеми provisioners, команди передачі та випадки, коли вистачить Ansible.

Ansible і Terraform в одному реченні

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

Обидва інструменти декларативні, і обидва належать до підходу infrastructure as code (IaC). Реальна відмінність полягає в тому, що саме вони запам’ятовують. Terraform записує state file, який зіставляє кожен ресурс у вашому коді з реальним об’єктом, створеним через API. Тому Terraform може визначити, що видалення п’яти рядків означає потребу знищити один сервер. Ansible нічого не запам’ятовує між запусками. Він підключається через SSH, перевіряє машину та змінює лише те, що ще не відповідає playbook.

Ця єдина відмінність пояснює решту цього посібника, зокрема те, чому об’єднання обох завдань в одному інструменті призводить до проблем.

Що насправді робить Terraform

Terraform взаємодіє з API через плагін provider. Сторінка реєстру вашого provider визначає типи ресурсів, які можна описувати. Тому сервер на одному хості та сервер на іншому мають різні назви ресурсів і різні аргументи.

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

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

Замініть cloud_server на тип ресурсу, описаний у документації вашого provider. Блок output є ключовою частиною цього посібника, оскільки саме через нього адреса виходить із Terraform.

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

terraform init завантажує provider і записує lock-файл. 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. Якщо її втратити, Terraform більше не знатиме, що ці сервери належать до його стану, тому під час наступного застосування спробує створити дублікати. Щойно команди починає виконувати більше однієї людини, зберігайте стан у remote backend. Одночасне застосування змін двома людьми призводить до такого результату:

Error: Error acquiring the state lock

OpenTofu — це fork Terraform із такими самими командами та форматом файлів. Станом на July 2026 усе в цьому посібнику працює, якщо вводити tofu замість terraform.

Що насправді робить Ansible

Ansible не потребує агента чи API. Він відкриває SSH-з’єднання, копіює невеликий Python-модуль на цільову систему, запускає його та видаляє. Усе, до чого можна підключитися через SSH із паролем sudo, можна налаштувати за допомогою Ansible.

- 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: він показує, що змінилося б, але не вносить змін. Однак завдання, які залежать від попередніх завдань, можуть неправильно звітувати в check mode, оскільки попередня зміна фактично не відбулася.

Кожен запуск завершується підсумком, наприклад ok=6 changed=2 unreachable=0 failed=0. Запустіть той самий playbook двічі. Під час другого запуску має бути повідомлено changed=0. Завдання, яке повідомляє про зміну під час кожного запуску, не є ідемпотентним. Зазвичай це завдання command або shell, яке слід було реалізувати за допомогою повноцінного модуля. Якщо ви лише починаєте працювати з Ansible, почніть із першого playbook Ansible на одному VPS і розширюйте його далі.

Де можливості двох інструментів перетинаються, а де вони конфліктують

Terraform може виконувати команди на новому сервері за допомогою provisioner remote-exec. У власній документації HashiCorp називає provisioner крайнім засобом. Для цього є вагомі причини.

Provisioner запускається лише під час створення ресурсу. Якщо змінити скрипт, на наявному сервері нічого не станеться, оскільки, з погляду Terraform, ресурс уже відповідає коду. Кроки provisioner ніколи не відображаються в terraform plan, тому під час перевірки змін їх не видно. Якщо скрипт завершується з помилкою, Terraform позначає ресурс як пошкоджений, а наступний 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. Саме це потрібно всередині підстановки командної оболонки. Для кількох серверів використовуйте terraform output -json і на його основі сформуйте inventory, оскільки -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 provider. У ansible-inventory --graph нічого не з’явиться, доки ви їх не додасте.

Звичайний згенерований файл інвентарю простіше налагоджувати, і він працює з будь-яким provider. Плагін стає корисним, коли інвентар розростається до кількох машин, а ручне редагування починає спричиняти помилки в написанні. Це також момент, коли керування кількома Linux-серверами з однієї керувальної машини перетворюється на повноцінний робочий процес, а не просто на звичку.

Чи справді вам потрібен Terraform?

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

Використовуйте Terraform, коли часто перебудовуєте середовища, коли staging має точно відповідати production, коли кілька людей змінюють інфраструктуру й перед видаленням будь-яких ресурсів потрібен план, придатний для перевірки, або коли ви керуєте не лише серверами, а й DNS-записами, load balancer і правилами firewall, які зберігаються в 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. Коли той самий playbook починає працювати з групою хостів, а не з одним новим сервером, заздалегідь вирішіть що робити, якщо один хост залишається недоступним, оскільки Ansible виключає цей хост із решти запуску, а рядок recap є єдиним місцем, де це зазначається.

Ключ хоста змінився. Ви знищили й повторно створили сервер, а новий сервер відповідає за тією самою адресою, але з новим ключем.

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 provisioner, але вони виконуються лише під час створення ресурсу, ніколи не з’являються у terraform plan і позначають ресурс як tainted у разі помилки. Це планує видалення та повторне створення ресурсу під час наступного apply. У Terraform немає еквівалента модуля, який перевіряє, чи вже встановлено nginx, і нічого не робить, якщо це так. Використовуйте Terraform для створення машини, а потім передайте її Ansible.

Чи може Ansible замінити Terraform?

Для невеликої кількості серверів із тривалим життєвим циклом — так. Ansible має cloud-модулі для створення серверів. Якщо ви замовили два VPS і плануєте їх зберігати, цього достатньо. Ви втрачаєте state file і граф залежностей: якщо видалити завдання з playbook, ресурс продовжить працювати й генерувати витрати, оскільки Ansible не записав, що саме він його створив. Terraform запланував би видалення.

Що мені вивчити спочатку?

Ansible, якщо ви вже керуєте серверами. Він окупається на першій машині, потребує лише SSH, а ці навички можна застосувати до сервера, який ви замовили вручну. Terraform окупається пізніше, коли ви регулярно відтворюєте середовища або керуєте ресурсами провайдера, не обмежуючись серверами, наприклад DNS-записами та правилами firewall.

Як передати IP-адресу нового сервера з Terraform в Ansible?

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

Чому мій playbook завершується помилкою одразу після завершення Terraform?

Провайдер повідомляє, що сервер створено, щойно його API повертає відповідний статус, хоча операційна система ще завантажується. Тому протягом перших секунд SSH відхиляє підключення. Помилка має вигляд UNREACHABLE! з Connection refused. Зробіть ansible.builtin.wait_for_connection першим завданням у play замість фіксованої затримки, оскільки час завантаження залежить від image і plan.