Blog JSystems - uwalniamy wiedzę!
Blog JSystems - uwalniamy wiedzę!

Wyobraź sobie, że masz postawić dziesięć identycznych serwerów WWW. Logujesz się przez SSH na pierwszy, instalujesz nginx, wgrywasz konfigurację, zakładasz konto, uruchamiasz usługę. Potem to samo na drugim. I na trzecim. Po piątym mylisz się w jednym poleceniu i nagle serwery przestają być identyczne. Właśnie ten problem rozwiązuje Ansible.
W tym artykule wyjaśnimy od zera, czym Ansible jest i jak działa. Nie na slajdach, tylko na działającym labie: postawiliśmy węzeł sterujący i cztery serwery, a wszystkie zrzuty poniżej to prawdziwe wyjścia poleceń z tego środowiska. Zobaczysz playbook w akcji, zrozumiesz idempotencję i dowiesz się, dlaczego model bez agenta zrobił z Ansible ulubione narzędzie administratorów.
Ansible to narzędzie do automatyzacji konfiguracji serwerów i wdrożeń. Zamiast ręcznie klikać i wpisywać polecenia na każdej maszynie z osobna, opisujesz stan docelowy w czytelnym pliku tekstowym, a Ansible sam doprowadza do niego wszystkie wskazane serwery. Powiesz mu: „na tych dziesięciu hostach ma być zainstalowany nginx, ma działać i ma mieć taką konfigurację”, a on wykona to na wszystkich naraz.
Ansible powstał w 2012 roku, dziś rozwija go Red Hat i jest jednym z najpopularniejszych narzędzi w świecie DevOps. Jego siła bierze się z prostoty: konfigurację zapisujesz w formacie YAML, który czyta się niemal jak zwykłą listę kroków po polsku, a do działania nie potrzebujesz niczego poza SSH i Pythonem.

Ręczna administracja nie skaluje się. Trzy serwery jeszcze ogarniesz, ale przy trzydziestu pojawiają się dwa problemy. Po pierwsze czas: ta sama czynność powtórzona trzydzieści razy to godziny pracy. Po drugie rozjazd konfiguracji: prędzej czy później któryś serwer dostanie inną wersję pakietu albo inny wpis w konfiguracji, i zaczyna zachowywać się inaczej niż reszta. Ansible zamyka opis serwera w pliku, który trzymasz w repozytorium razem z kodem - dzięki temu konfiguracja jest powtarzalna, wersjonowana i taka sama na każdej maszynie. Skoro konfiguracja żyje w repozytorium, warto znać podstawy kontroli wersji - jeśli dopiero zaczynasz, zajrzyj do przewodnika czym jest Git i jak z niego korzystać. Ansible często łączy się też z narzędziami do zarządzania infrastrukturą jako kodem, takimi jak Terraform, i z podejściem GitOps - pokazaliśmy taki tandem w artykule o agencie AI, który stawia maszyny Terraformem i pilnuje GitOps.
Najważniejsza cecha Ansible kryje się w słowie agentless (bez agenta). Na zarządzanych serwerach nie instalujesz żadnego oprogramowania Ansible - żadnej usługi, żadnego demona działającego w tle. Ansible siedzi tylko na jednej maszynie, tak zwanym węźle sterującym (może nim być Twój laptop), i łączy się z serwerami przez zwykłe SSH. Wykorzystuje to, co i tak jest na każdym Linuksie: połączenie SSH i interpreter Pythona.

To ogromne uproszczenie. W modelu z agentem (jak w starszych Puppet czy Chef) na każdym serwerze musisz zainstalować i utrzymywać program, który cyklicznie pyta serwer centralny o konfigurację. To kolejna rzecz do zainstalowania, zabezpieczenia i naprawiania, gdy się zepsuje. Ansible tego nie potrzebuje: model push oznacza, że to węzeł sterujący sam wypycha zmiany na serwery wtedy, kiedy Ty o to poprosisz. Start jest banalny - o czym za chwilę.
Instalacja sprowadza się do jednego polecenia. Ansible to paczka Pythona, więc instalujemy go menedżerem pakietów pip:
python3 -m pip install ansible
ansible --version

Ansible musi wiedzieć, czym ma zarządzać. Tę listę nazywamy inventory. To zwykły plik tekstowy, w którym grupujemy serwery - na przykład osobno serwery WWW, osobno bazy danych. Grupy pozwalają potem powiedzieć „zrób to tylko na serwerach WWW”:
# inventory.ini - lista zarzadzanych hostow
[web]
web1
web2
web3
[db]
db1
[serwery:children]
web
db

Zanim napiszemy pełny scenariusz, możemy odpalać pojedyncze zadania jednym poleceniem. Nazywamy je ad-hoc (doraźnymi). Klasyczny pierwszy kontakt to ping - sprawdzenie, czy Ansible dogada się ze wszystkimi hostami:
ansible serwery -m ping

Tak samo uruchomimy dowolne polecenie systemowe na całej grupie naraz. Tu pytamy serwery WWW o czas działania, a przy okazji sprawdzamy, że nginx jeszcze nie jest zainstalowany - zaraz to zmienimy playbookiem:
ansible web -m command -a "uptime"

Polecenia ad-hoc i inventory to dopiero rozgrzewka. Jeśli chcesz nauczyć się pisać kompletne playbooki, składać z nich role wielokrotnego użytku i wdrażać całe środowiska od zera, pokażemy Ci to na praktycznych warsztatach. Szkolenie Ansible: automatyzacja zadań w praktyce ma terminy gwarantowane.
Polecenia ad-hoc są wygodne do drobiazgów, ale prawdziwa moc Ansible to playbook: plik YAML, w którym opisujemy cały scenariusz konfiguracji. Playbook to lista zadań (tasks), wykonywanych po kolei na wskazanych hostach. Każde zadanie używa jakiegoś modułu - gotowego klocka, który wie, jak coś zrobić. Modułowi mówisz, jaki ma być efekt („nginx ma być zainstalowany”), a on martwi się, jak to osiągnąć na danym systemie.
Oto kompletny playbook, który stawia serwer WWW na wszystkich hostach z grupy web - instaluje nginx, zakłada konto, wgrywa stronę z szablonu, poprawia wpis w konfiguracji, uruchamia usługę i sprawdza, czy odpowiada:
---
# site.yml - postaw serwer WWW nginx na wszystkich hostach z grupy [web]
- name: Konfiguracja serwerow WWW
hosts: web
gather_facts: true
vars:
powitanie: "Ta strona zostala postawiona przez Ansible"
admin_user: deploy
tasks:
- name: Zainstaluj serwer nginx
ansible.builtin.apt:
name: nginx
state: present
update_cache: true
- name: Zaloz konto administracyjne
ansible.builtin.user:
name: "{{ admin_user }}"
groups: sudo
shell: /bin/bash
state: present
- name: Wgraj strone startowa z szablonu Jinja2
ansible.builtin.template:
src: templates/index.html.j2
dest: /var/www/html/index.html
owner: www-data
mode: "0644"
- name: Ustaw liczbe procesow roboczych nginx
ansible.builtin.lineinfile:
path: /etc/nginx/nginx.conf
regexp: "^worker_processes"
line: "worker_processes auto;"
- name: Uruchom serwer nginx
ansible.builtin.command: nginx
args:
creates: /run/nginx.pid
- name: Sprawdz odpowiedz serwera WWW
ansible.builtin.uri:
url: http://localhost
return_content: false
register: odpowiedz
- name: Pokaz kod odpowiedzi HTTP
ansible.builtin.debug:
msg: "Serwer {{ inventory_hostname }} odpowiada kodem HTTP {{ odpowiedz.status }}"
Zwróć uwagę, jak czyta się ten plik: to niemal lista kroków po ludzku. hosts: web mówi „rób to na serwerach WWW”, a każdy - name: to jeden opisany krok. Uruchamiamy całość jednym poleceniem:
ansible-playbook site.yml

W kolorach kryje się cała logika Ansible. Żółte „changed” to zadania, które faktycznie coś zmieniły na serwerze. Zielone „ok” oznacza, że stan już się zgadzał i nie trzeba było nic robić. Podsumowanie PLAY RECAP na dole zbiera statystyki z każdego hosta - ile zadań się wykonało i ile z nich coś zmieniło.
Efekt jest namacalny. Zadanie z modułem template wygenerowało stronę startową, podstawiając do szablonu prawdziwe dane każdego serwera (nazwę hosta, wersję systemu, adres IP). Podejrzyjmy gotowy plik prosto na serwerze web1 poleceniem cat:

Widziałeś, jak jeden plik konfiguruje całą flotę serwerów naraz - to codzienność administratora pracującego z Ansible. Chcesz opanować playbooki, zmienne i szablony na tyle, by automatyzować własną infrastrukturę bez zaglądania do dokumentacji co chwilę? Szkolenie Ansible: automatyzacja infrastruktury dla administratorów ma terminy gwarantowane.
Dochodzimy do pojęcia, które odróżnia Ansible od zwykłego skryptu. Idempotencja oznacza, że wielokrotne uruchomienie tego samego playbooka daje ten sam efekt co jedno uruchomienie. Ansible najpierw sprawdza stan serwera i zmienia tylko to, co odbiega od opisanego celu. Jeśli wszystko już się zgadza - nie robi nic.

Zobaczmy to na żywo. Uruchamiamy dokładnie to samo polecenie drugi raz, bez żadnych zmian:
ansible-playbook site.yml # to samo, drugi raz

To potężna właściwość w praktyce. Playbook możesz odpalać bez obaw ile razy chcesz - jako sposób na sprawdzenie, czy serwery nadal są w prawidłowym stanie, albo po dodaniu jednego nowego zadania. Ansible dołoży tylko brakującą różnicę i nie zepsuje tego, co już działa. Zwykły skrypt shellowy tak nie potrafi - próba drugiej instalacji czy dopisania tej samej linijki zwykle kończy się błędem albo duplikatem.

Powyższe wystarczy, żeby zrozumieć, czym jest Ansible. Warto jednak poznać jeszcze cztery pojęcia, które spotkasz od razu przy realnej pracy.
Zanim wykona zadania, Ansible zbiera fakty o każdym serwerze: system operacyjny, adresy IP, ilość pamięci, zamontowane dyski i setki innych informacji. Możesz się do nich odwoływać w playbookach - na przykład zainstalować inny pakiet na Debianie, a inny na systemie z rodziny Red Hat. Podejrzyjmy fakty o dystrybucji jednego z serwerów:
ansible web1 -m setup -a "filter=ansible_distribution*"

Widziałeś już moduł template w playbooku. Korzysta on z Jinja2 - systemu szablonów, w którym w plik konfiguracyjny wstawiasz miejsca do wypełnienia w podwójnych nawiasach klamrowych. Ansible podstawia tam wartości zmiennych i faktów osobno dla każdego serwera. Dzięki temu jeden szablon obsługuje całą flotę, a każdy host dostaje swoją, właściwą wersję pliku - jak strona z poprzedniego przykładu, gdzie nazwa hosta i adres IP były podstawione automatycznie.
Tak dokładnie wygląda szablon index.html.j2, który wgrywaliśmy w playbooku. Wszystko, co stoi w podwójnych nawiasach klamrowych (podświetlone), to miejsce do wypełnienia - Ansible wstawi tam realną wartość osobno dla każdego serwera:
<!DOCTYPE html>
<html lang="pl">
<head><meta charset="utf-8"><title>{{ inventory_hostname }}</title></head>
<body>
<h1>{{ powitanie }}</h1>
<p>Host: {{ inventory_hostname }}</p>
<p>System: {{ ansible_distribution }} {{ ansible_distribution_version }}</p>
<p>Adres IP: {{ ansible_default_ipv4.address | default('n/d') }}</p>
</body>
</html>
Widać to od razu, gdy zestawimy szablon z wynikiem z poprzedniej sekcji: {{ inventory_hostname }} zamieniło się w web1, a {{ ansible_default_ipv4.address }} w konkretny adres IP tego serwera. Najlepszy dowód, że dzieje się to osobno dla każdego hosta, to pobrać gotową stronę wprost z innego serwera. Odpytajmy przez HTTP maszynę web2:

Gdy playbooków i zadań przybywa, jeden wielki plik YAML robi się nieczytelny. Wtedy dzielimy konfigurację na role. Rola to samodzielny, gotowy do wielokrotnego użytku kawałek automatyzacji - na przykład „postaw serwer WWW” albo „skonfiguruj bazę danych” - w którym zadania, szablony, pliki i zmienne leżą w z góry ustalonych katalogach. Dzięki tej stałej strukturze Ansible sam wie, gdzie czego szukać, a Ty raz napisaną rolę wpinasz potem do dowolnego playbooka jednym wpisem.
Roli nie musisz pisać od zera. Służy do tego osobne narzędzie ansible-galaxy, a stoi za nim Ansible Galaxy - publiczny katalog ról i kolekcji współdzielonych przez społeczność. Tym samym poleceniem pobierzesz gotową rolę z internetu (ansible-galaxy role install autor.nazwa_roli) albo wygenerujesz pusty szkielet własnej:
ansible-galaxy role init webserver
To polecenie niczego nie pobiera z sieci - tworzy lokalnie kompletny szkielet katalogów nowej roli o nazwie webserver, z pustymi plikami main.yml w każdym miejscu, którego Ansible się spodziewa. Zamiast ręcznie zakładać kilkanaście folderów, dostajesz je od razu gotowe do wypełnienia:

Każdy katalog w tym szkielecie ma z góry przypisane zadanie:
tasks/main.yml - główna lista zadań roli, ta sama, którą wcześniej wpisywaliśmy wprost w playbooku (tu trafiłyby zadania instalujące i konfigurujące nginx).handlers/main.yml - procedury (handlers) uruchamiane tylko wtedy, gdy coś faktycznie się zmieniło, na przykład „przeładuj nginx po zmianie konfiguracji”.templates/ - szablony Jinja2 (pliki .j2), dokładnie takie jak nasz index.html.j2.files/ - gotowe pliki statyczne kopiowane na serwery bez żadnych zmian.defaults/main.yml oraz vars/main.yml - zmienne roli. W defaults trzymasz wartości domyślne, łatwe do nadpisania, a w vars te ważniejsze, o wyższym priorytecie.meta/main.yml - metadane roli i lista innych ról, od których ona zależy.tests/ oraz README.md - przykładowy playbook testowy z własnym inventory i miejsce na dokumentację roli.Gotową rolę wpinasz do playbooka jednym wpisem roles:, a Ansible sam sięgnie po jej tasks/main.yml:
---
# site.yml - ten sam efekt co wczesniej, ale zadania siedza w roli
- name: Konfiguracja serwerow WWW
hosts: web
roles:
- webserver
Odwrotny scenariusz jest jeszcze wygodniejszy: zamiast pisać rolę samodzielnie, pobierasz gotową i sprawdzoną z Ansible Galaxy. Weźmy popularną rolę geerlingguy.nginx, która potrafi postawić i skonfigurować nginx za nas. Pobieramy ją jednym poleceniem:
ansible-galaxy role install geerlingguy.nginx

Teraz wystarczy krótki playbook, który stosuje pobraną rolę - bez pisania własnych zadań instalujących nginx:
---
# nginx-galaxy.yml - uzyj gotowej roli pobranej z Ansible Galaxy
- name: Postaw serwer WWW rola z Galaxy
hosts: web2
become: true
roles:
- geerlingguy.nginx
Uruchamiamy go dokładnie tak samo jak każdy inny playbook:
ansible-playbook nginx-galaxy.yml

Playbooki i pliki ze zmiennymi trzymasz w repozytorium Git razem z resztą kodu - i słusznie, bo masz wtedy pełną historię zmian. Kłopot w tym, że część danych to sekrety: hasła do baz, klucze API, tokeny. Gdyby leżały w repozytorium otwartym tekstem, zobaczyłby je każdy, kto ma do niego dostęp, a po wycieku repozytorium - praktycznie każdy. Ansible Vault rozwiązuje to, szyfrując sekrety algorytmem AES256 hasłem, które znasz tylko Ty (i Twój serwer automatyzacji). Zaszyfrowane dane spokojnie commitujesz, a Ansible odszyfruje je w pamięci dopiero w chwili uruchomienia playbooka. Przejdźmy przez to krok po kroku.
Najczęstszy przypadek to jeden sekret, na przykład hasło do bazy. Zamieniasz go w zaszyfrowany blok poleceniem encrypt_string:
ansible-vault encrypt_string --stdin-name db_password
Co oznaczają poszczególne części polecenia:
encrypt_string - tryb szyfrowania pojedynczej wartości, a nie całego pliku.--stdin-name db_password - nazwa zmiennej, pod jaką zapiszesz sekret. Samą wartość sekretu Ansible czyta ze standardowego wejścia - wpisujesz ją w terminalu i kończysz skrótem Ctrl+D.Po uruchomieniu Ansible poprosi najpierw o hasło Vaulta (dwa razy, drugi raz dla potwierdzenia), potem o samą wartość sekretu, a na końcu wypisze gotowy blok:

Gdy sekretów jest więcej, wygodniej trzymać je w jednym pliku i zaszyfrować go w całości. Tworzysz zwykły plik YAML (na przykład sekrety.yml z kluczami db_password i api_key), a potem uruchamiasz:
ansible-vault encrypt sekrety.yml
Polecenie encrypt <plik> szyfruje plik w miejscu - po podaniu hasła cała czytelna treść zamienia się w nagłówek $ANSIBLE_VAULT;1.1;AES256 i blok szesnastkowy. Żeby zajrzeć do środka bez odszyfrowywania pliku na dysk, używasz ansible-vault view:

Na zaszyfrowanym pliku pracujesz kilkoma poleceniami, bez ręcznego odszyfrowywania go na dysk:
ansible-vault view sekrety.yml - pokazuje odszyfrowaną treść na ekranie (po podaniu hasła), nigdzie jej nie zapisując.ansible-vault edit sekrety.yml - otwiera plik w edytorze już odszyfrowany, a po zapisaniu automatycznie szyfruje go z powrotem.ansible-vault rekey sekrety.yml - zmienia hasło Vaulta, przydaje się przy rotacji haseł w zespole.ansible-vault decrypt sekrety.yml - trwale odszyfrowuje plik. Używaj wyjątkowo, bo zdejmuje ochronę.To najważniejszy krok, bo sam zaszyfrowany plik nic nie robi, dopóki nie sięgniesz po niego w playbooku. Sekret podpinasz jak każdą inną zmienną - na przykład wpisem vars_files: [sekrety.yml] - i odwołujesz się do niego jako {{ db_password }}. Jedyna różnica jest przy uruchamianiu: musisz podać hasło Vaulta. Interaktywnie robisz to tak:
ansible-playbook use-secret.yml --ask-vault-pass
A w automatyzacji (CI/CD) podajesz hasło z pliku, który trzymasz poza repozytorium:
ansible-playbook use-secret.yml --vault-password-file ~/.vault_pass
Znaczenie parametrów:
--ask-vault-pass - Ansible zapyta o hasło interaktywnie, wygodne przy pracy z konsoli.--vault-password-file <plik> - hasło czytane z pliku, na potrzeby potoków CI/CD. Ten plik NIGDY nie trafia do repozytorium (trzymasz go w sekretach pipeline'u albo lokalnie, poza projektem).Jeśli uruchomisz playbook bez hasła, Ansible w ogóle nie wystartuje i zgłosi błąd. Dopiero z hasłem sekret zostaje odszyfrowany w pamięci na czas działania i nigdzie nie jest zapisywany:

Tak domyka się bezpieczny obieg sekretu: zaszyfrowany leży w repozytorium, a odszyfrowuje się wyłącznie w pamięci i wyłącznie w chwili, gdy jest naprawdę potrzebny.
Ansible to narzędzie, które zamienia ręczną, powtarzalną administrację w jeden opisany plik. Zapamiętaj kilka rzeczy:
Dobry sposób na naukę to zrobić dokładnie to, co my w tym artykule: postawić kilka maszyn (choćby w kontenerach) i napisać własny, pierwszy playbook. Ansible odwdzięcza się szybko - już pierwszy scenariusz, który konfiguruje kilka serwerów naraz, pokazuje, ile czasu oszczędza.
Jeśli chcesz przejść od pierwszego playbooka do biegłej automatyzacji całej infrastruktury - ról, Vault, dynamicznego inventory i wdrożeń - nasze szkolenie Automatyzacja zadań z Ansible prowadzi przez to krok po kroku, na praktycznych warsztatach. Kurs ma termin gwarantowany.
To szkolenie może być dofinansowane dla Ciebie z KFS lub BUR.
★★★★★Średnia ocena naszych szkoleń w Google: 5/5
Komentarze (0)
Brak komentarzy...