Ansible czy Terraform: co wybrać do infrastruktury?
Terraform tworzy zasoby, a Ansible konfiguruje oprogramowanie. Poznaj różnice w obsłudze stanu, dowiedz się dlaczego łączenie obu narzędzi bywa ryzykowne i kiedy wystarczy sam Ansible.
Ansible a Terraform w jednym zdaniu
Ansible i Terraform nie są narzędziami wykonującymi to samo zadanie. Terraform deklaruje stan infrastruktury: serwery, dyski, sieci, rekordy DNS. Ansible deklaruje stan wnętrza istniejącej maszyny: pakiety, użytkowników, pliki konfiguracyjne, uruchomione usługi. Terraform tworzy VPS. Ansible przekształca ten VPS w serwer WWW.
Oba narzędzia są deklaratywne i oba określa się mianem Infrastructure as Code (IaC). Różnica polega na sposobie przechowywania informacji o stanie. Terraform zapisuje plik stanu, który mapuje każdy zasób w kodzie na rzeczywisty obiekt utworzony przez API, dzięki czemu system wie, że usunięcie pięciu linii kodu oznacza konieczność zniszczenia serwera. Ansible nie przechowuje żadnych informacji między uruchomieniami. Łączy się przez SSH, sprawdza stan maszyny i zmienia tylko te elementy, które nie są zgodne z playbookiem.
Ta jedna różnica wyjaśnia resztę niniejszego przewodnika, w tym powody, dla których łączenie obu zadań w jednym narzędziu prowadzi do błędów.
Jak faktycznie działa Terraform
Terraform komunikuje się z API za pośrednictwem wtyczki dostawcy (provider plugin). Strona rejestru dostawcy definiuje typy zasobów, które można zdefiniować w kodzie, dlatego serwer u jednego dostawcy i serwer u innego mają różne nazwy zasobów oraz wymagają innych argumentów.
resource "cloud_server" "web" {
name = "web1"
image = "ubuntu-24.04"
type = "small"
}
output "web_ip" {
value = cloud_server.web.ipv4_address
}Zastąp cloud_server typem zasobu opisanym w dokumentacji dostawcy. Blok output jest kluczowy dla tego przewodnika, ponieważ określa sposób, w jaki adres opuszcza Terraform.
terraform init
terraform fmt -check
terraform validate
terraform plan -out=tfplan
terraform apply tfplanterraform init pobiera dostawcę i tworzy plik blokady (lock file). terraform plan wyświetla różnicę między kodem a plikiem stanu, kończąc się wierszem typu Plan: 1 to add, 0 to change, 0 to destroy.. Należy każdorazowo analizować ten wiersz. Niektóre argumenty nie mogą zostać zmienione w miejscu, co plan sygnalizuje za pomocą # forces replacement obok atrybutu, a następnie 1 to add, 0 to change, 1 to destroy. Zastosowanie takiego planu spowoduje usunięcie serwera i utworzenie nowego, pustego, co prowadzi do utraty danych, które użytkownik uznawał za bezpieczne.
Zapisanie planu do pliku i jego późniejsze zastosowanie, zamiast uruchamiania polecenia terraform apply bezpośrednio, gwarantuje, że wykonane zostanie dokładnie to, co zostało wcześniej zweryfikowane. W czasie między tymi dwoma operacjami inna osoba mogła wprowadzić zmiany w infrastrukturze.
terraform.tfstate stanowi pamięć narzędzia. Utrata tego pliku sprawia, że Terraform przestaje rozpoznawać istniejące serwery jako zarządzane przez siebie, co przy kolejnym zastosowaniu planu prowadzi do prób tworzenia duplikatów. Gdy z poleceń korzysta więcej niż jedna osoba, należy przechowywać ten plik w zdalnym backendzie, ponieważ jednoczesne uruchomienie operacji przez dwie osoby skutkuje następującym błędem:
Error: Error acquiring the state lockOpenTofu to fork Terraform, który obsługuje te same polecenia i format plików. Według stanu na lipiec 2026, wszystkie instrukcje w tym przewodniku działają po wpisaniu tofu zamiast terraform.
Czym w rzeczywistości zajmuje się Ansible
Ansible nie wymaga agenta ani API. Nawiązuje połączenie SSH, kopiuje niewielki moduł Python na serwer docelowy, uruchamia go, a następnie usuwa. Wszystko, do czego można uzyskać dostęp przez SSH i hasło sudo, może zostać skonfigurowane przez 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: trueansible -i inventory.ini web -m ansible.builtin.ping
ansible-playbook -i inventory.ini site.yml --check --diff
ansible-playbook -i inventory.ini site.ymlModuł ping pozwala zweryfikować działanie SSH, Python oraz sudo przed rozpoczęciem debugowania playbooka. Prawidłowy wynik to web1 | SUCCESS => {"ping": "pong"}. Uruchomienie --check --diff jest najbliższym odpowiednikiem planu w Ansible: raportuje zmiany, które zostałyby wprowadzone bez ich faktycznego wykonania. Należy jednak pamiętać, że zadania zależne od wcześniejszych kroków mogą w trybie sprawdzania raportować błędne dane, ponieważ wcześniejsza zmiana w rzeczywistości nie nastąpiła.
Każde uruchomienie kończy się podsumowaniem, takim jak ok=6 changed=2 unreachable=0 failed=0. Uruchom ten sam playbook dwukrotnie. Drugie uruchomienie powinno zwrócić wynik changed=0. Zadanie, które przy każdym uruchomieniu raportuje zmianę (changed), nie jest idempotentne. Zazwyczaj jest to zadanie command lub shell, które powinno zostać zrealizowane za pomocą dedykowanego modułu. Jeśli to Twoje pierwsze kroki, zacznij od pierwszego playbooka Ansible na pojedynczym VPS i rozwijaj go w miarę potrzeb.
Gdzie narzędzia te nakładają się na siebie i gdzie wchodzą w konflikt
Terraform może wykonywać polecenia na nowym serwerze za pomocą provisioner remote-exec. Dokumentacja HashiCorp określa provisionery jako ostateczność. Istnieją ku temu istotne powody.
Provisioner uruchamia się tylko w momencie tworzenia zasobu. Edycja skryptu nie powoduje żadnych zmian na istniejącym serwerze, ponieważ z punktu widzenia Terraform zasób jest już zgodny z kodem. Kroki provisionera nigdy nie pojawiają się w terraform plan, więc przegląd zmian nie wykazuje ich obecności. Jeśli skrypt zawiedzie, Terraform oznacza zasób jako tainted, a kolejne polecenie apply niszczy i tworzy od nowa serwer, który prawdopodobnie działał poprawnie.
Awaria następuje również w nieodpowiednim momencie. Dostawca zgłasza utworzenie serwera w chwili, gdy API potwierdza sukces, podczas gdy system operacyjny wciąż się uruchamia, a sshd jeszcze nie nasłuchuje.
Error: remote-exec provisioner error
timeout - last error: dial tcp 203.0.113.10:22: connect: connection refusedAnsible wiąże się z odwrotną pokusą. Moduły chmurowe potrafią tworzyć serwery i w przypadku kilku maszyn takie rozwiązanie działa. Ceną jest jednak utrata grafu zależności oraz pliku stanu. Ansible bez problemu utworzy zasób, ale usunięcie zadania z playbooka sprawi, że zasób pozostanie uruchomiony i będzie generował koszty, ponieważ nic nie odnotowało faktu, że należał on do zarządzanej infrastruktury.
Zasada wynikająca z powyższego: niech Terraform zarządza obiektami, które są tworzone i usuwane przez API, a Ansible niech zarządza wszystkim wewnątrz uruchomionego systemu operacyjnego.
Przekazanie, wykonane
Przekazanie jest granicą, a nie integracją. Terraform kończy działanie, publikuje adres i zatrzymuje się. Ansible rozpoczyna pracę od tego adresu.
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.ymlterraform output -raw wypisuje jedną wartość bez cudzysłowów i bez otoczki JSON, co jest pożądane wewnątrz podstawienia powłoki. Dla wielu serwerów należy użyć terraform output -json i na tej podstawie zbudować inwentarz, ponieważ -raw obsługuje tylko pojedynczy ciąg znaków, liczbę lub wartość logiczną.
Krok ping pomiędzy tymi dwoma narzędziami jest wart zachowania. Pozwala on odróżnić sytuację, w której „Terraform podał błędny adres” od sytuacji, w której „playbook zawiera błąd”. Oba te problemy wyglądają identycznie, gdy playbook jest pierwszym elementem, który nawiązuje połączenie z nową maszyną.
Odczytywanie stanu Terraform jako inwentarza Ansible
Jeśli wolisz w ogóle nie tworzyć pliku inwentarza, kolekcja cloud.terraform odczytuje stan bezpośrednio.
ansible-galaxy collection install cloud.terraformUtwórz terraform.yml obok swojego playbooka:
plugin: cloud.terraform.terraform_provider
project_path: /home/deploy/infraansible-inventory -i terraform.yml --graph
ansible-playbook -i terraform.yml site.ymlDwie kwestie wymagają uwagi, zanim zaczniesz na tym polegać. Wtyczka uruchamia terraform show względem project_path, więc ten katalog musi być już zainicjowany, w przeciwnym razie wtyczka zgłosi błąd. Nie tworzy ona również hostów automatycznie na podstawie zasobów serwerowych: odczytuje zasoby ansible_host oraz ansible_group, które deklarujesz w kodzie Terraform przy użyciu dostawcy Ansible. Nic nie pojawi się w ansible-inventory --graph, dopóki ich nie dodasz.
Zwykły wygenerowany plik inwentarza jest łatwiejszy w debugowaniu i współpracuje z każdym dostawcą. Wtyczka staje się opłacalna, gdy inwentarz przekroczy kilka maszyn, a ręczna edycja zacznie prowadzić do literówek – to ten sam moment, w którym zarządzanie kilkoma serwerami Linux z jednej maszyny sterującej staje się rzeczywistym procesem roboczym, a nie tylko nawykiem.
Czy faktycznie potrzebujesz Terraform?
Większość osób czytających ten tekst nie potrzebuje go, przynajmniej na razie. Terraform zwraca poniesione koszty, gdy tworzenie i usuwanie infrastruktury jest powtarzalnym zadaniem. Jeśli zamówiłeś jeden VPS przez panel sterowania i zamierzasz utrzymywać go przez dwa lata, Terraform opisuje czynność, która zdarza się raz, a dodatkowo tworzy plik stanu, którego nie wolno zgubić.
Sięgnij po Terraform, gdy często przebudowujesz środowiska, gdy środowisko staging musi być identyczne z produkcyjnym, gdy kilka osób zmienia infrastrukturę i wymagasz planu do weryfikacji przed usunięciem czegokolwiek lub gdy zarządzane zasoby wykraczają poza serwery i obejmują rekordy DNS, load balancery oraz reguły firewalla dostępne przez API dostawcy.
Pozostań przy samym Ansible, gdy serwery są długowieczne i jest ich niewiele, a codzienne pytanie brzmi „czy ten serwer jest poprawnie skonfigurowany”, a nie „czy ten serwer istnieje”. Pojedynczy playbook, który zabezpiecza świeży serwer, realizuje te same cele co pierwsze dziesięć minut na nowym VPS, z tą zaletą, że działa w ten sam sposób na kolejnym serwerze.
Kolejność nauki wynika z powyższego. Ansible zwraca się przy pierwszym posiadanym serwerze. Terraform zwraca się przy trzecim przebudowywanym środowisku.
Co psuje się podczas przekazania
Serwer nie jest gotowy. Terraform kończy działanie powodzeniem, Ansible natychmiast zgłasza błąd.
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 zwróciło adres, zanim sshd zaczął nasłuchiwać. Należy poczekać na port zamiast dodawać sztywne opóźnienie. Ansible posiada ansible.builtin.wait_for_connection dokładnie do tego celu; należy uruchomić to jako pierwsze zadanie w play. Gdy ten sam playbook dotyczy grupy hostów, a nie pojedynczej nowej maszyny, należy z góry ustalić co powinno się stać, gdy jeden host pozostaje nieosiągalny, ponieważ Ansible usuwa taki host z dalszego przebiegu, a informacja o tym pojawia się wyłącznie w podsumowaniu na końcu.
Klucz hosta uległ zmianie. Serwer został zniszczony i utworzony ponownie, a nowy odpowiada pod tym samym adresem z nowym kluczem.
Host key verification failed.Należy usunąć nieaktualny wpis za pomocą ssh-keygen -R 203.0.113.10. Sytuacja ta występuje stale, gdy Terraform zajmuje się przebudową infrastruktury, co stanowi dobry powód, aby ograniczyć częstotliwość przebudowy maszyn przechowujących dane.
Sudo kończy się błędem. fatal: [web1]: FAILED! => {"msg": "Missing sudo password"} oznacza, że become: true wymaga hasła na danym hoście. Należy skonfigurować sudo bez hasła dla użytkownika wdrażającego lub przekazać flagę --ask-become-pass.
Terraform chce zniszczyć coś, czego nie modyfikowano. Plan wykazuje zmiany, które nie zostały zapisane w kodzie. Oznacza to, że rzeczywista infrastruktura odbiega od kodu, zazwyczaj z powodu ręcznej zmiany ustawień w panelu WWW dostawcy. Należy uruchomić terraform plan -refresh-only, aby samodzielnie sprawdzić różnice, a następnie zdecydować, czy błąd leży po stronie kodu, czy zasobu na żywo. Nigdy nie należy zatwierdzać niszczącego planu, którego nie można wyjaśnić linijka po linijce.
Ansible zgłasza zmiany przy każdym uruchomieniu. Zadanie shell bez zabezpieczenia creates lub when uruchamia się bezwarunkowo. Nie jest to problem kosmetyczny, ponieważ oznacza, że nie można już używać changed=0 jako sygnału, że serwer znajduje się w pożądanym stanie.
FAQ
Czy Terraform może zastąpić Ansible?
Nie w zakresie konfiguracji wewnątrz serwera. Terraform może wywoływać skrypty za pomocą provisioner remote-exec, ale są one uruchamiane tylko podczas tworzenia zasobu, nigdy nie pojawiają się w terraform plan i oznaczają zasób jako uszkodzony (tainted) w przypadku awarii, co skutkuje zaplanowaniem usunięcia i ponownego utworzenia przy następnym wywołaniu apply. Terraform nie posiada odpowiednika modułu, który sprawdza, czy nginx jest już zainstalowany i nie podejmuje żadnych działań, jeśli tak jest. Należy użyć Terraform do utworzenia maszyny, a następnie przekazać kontrolę.
Czy Ansible może zastąpić Terraform?
W przypadku niewielkiej liczby długo działających serwerów – tak. Ansible posiada moduły chmurowe, które tworzą serwery; jeśli zamawiasz dwie instancje VPS i utrzymujesz je, jest to wystarczające. Tracisz jednak plik stanu (state file) oraz graf zależności: usunięcie zadania z playbooka sprawia, że zasób nadal działa i generuje koszty, ponieważ Ansible nie odnotował faktu jego utworzenia. Terraform zaplanowałby operację destroy.
Czego powinienem nauczyć się najpierw?
Ansible, jeśli już posiadasz serwery. Zwraca się on już przy pierwszej maszynie, nie wymaga niczego poza SSH, a zdobyte umiejętności można zastosować do serwera zamówionego ręcznie. Terraform zwraca się później, gdy wielokrotnie przebudowujesz środowiska lub zarządzasz zasobami dostawcy wykraczającymi poza serwery, takimi jak rekordy DNS czy reguły firewalla.
Jak przekazać adres IP nowego serwera z Terraform do Ansible?
Zadeklaruj output w kodzie Terraform, a następnie odczytaj go po zakończeniu operacji apply. terraform output -raw web_ip wypisuje czystą wartość do podstawienia w powłoce, a terraform output -json zwraca wszystkie wyjścia jednocześnie, gdy hostów jest więcej. Zapisz to w pliku inwentarza lub zainstaluj kolekcję cloud.terraform i skieruj ansible-inventory -i terraform.yml --graph na katalog projektu.
Dlaczego mój playbook kończy się błędem zaraz po zakończeniu pracy Terraform?
Dostawca zgłasza utworzenie serwera, gdy tylko API potwierdzi taką informację, podczas gdy system operacyjny wciąż się uruchamia, więc połączenia SSH są odrzucane przez pierwsze sekundy. Błąd to UNREACHABLE! z Connection refused. Ustaw ansible.builtin.wait_for_connection jako pierwsze zadanie w play zamiast zgadywać czas oczekiwania, ponieważ czas rozruchu różni się w zależności od obrazu i planu.