Najczęstszy los automatyzacji w małym zespole: jedna osoba pisze playbook, playbook rośnie do dwóch tysięcy linii, osoba odchodzi, a następca boi się go dotknąć i zaczyna od zera. Problemem nie jest Ansible, lecz brak struktury, która pozwala zrozumieć kod bez jego autora.
Układ repozytorium
Struktura katalogów jest pierwszą dokumentacją. Sprawdzony układ:
inventories/
prod/hosts.yml maszyny i grupy środowiska produkcyjnego
prod/group_vars/ zmienne per grupa
test/...
roles/
<nazwa_roli>/ jedna rola = jedna odpowiedzialność
playbooks/
<zadanie>.yml cienkie playbooki składające role
requirements.yml kolekcje i role zewnętrzne z przypiętymi wersjami
README.md jak uruchomić, co wymaga, gdzie są sekrety
Playbook w katalogu playbooks/ nie zawiera zadań, tylko listę ról i grup docelowych. Logika jest w rolach.
Rola: jedna odpowiedzialność
Rola instaluje i konfiguruje jedną rzecz: serwer WWW, agenta monitoringu, konfigurację czasu. Rola „serwer aplikacyjny”, która robi dwadzieścia rzeczy, jest nie do przetestowania i nie do ponownego użycia.
Każda rola ma:
defaults/main.ymlz wszystkimi zmiennymi, jakich używa, z wartościami domyślnymi i komentarzem przy każdej,README.mdz jednym akapitem: co robi, czego wymaga, przykład użycia,meta/main.ymlz zależnościami od innych ról, jeśli są.
Zmienne roli mają prefiks nazwy roli: nginx_worker_processes, nie worker_processes. Bez prefiksu dwie role prędzej czy później użyją tej samej nazwy do różnych rzeczy.
Zmienne: gdzie co leży
Ansible ma kilkanaście poziomów pierwszeństwa zmiennych. W praktyce wystarczą trzy i zasada, by nie używać pozostałych:
defaults/w roli: wartości rozsądne dla większości przypadków.group_vars/w inventory: różnice między środowiskami i grupami serwerów.host_vars/: wyjątki dla pojedynczych maszyn, każdy z komentarzem, dlaczego.
Zmienne w treści playbooka, w vars: zadania lub przekazywane z linii poleceń są dopuszczalne wyłącznie tymczasowo. Zadanie ma wyglądać identycznie dla każdego środowiska; różnice opisuje inventory.
Sekrety
Hasła i klucze nigdy nie leżą w repozytorium w postaci jawnej. Dwie akceptowalne opcje:
- Ansible Vault z osobnym plikiem zaszyfrowanym per środowisko i hasłem vault poza repozytorium,
- zewnętrzny menedżer sekretów odpytywany w czasie wykonania przez wtyczkę lookup.
Druga opcja jest lepsza, gdy sekretów używa więcej niż jedno narzędzie. Niezależnie od wyboru, zadania operujące na sekretach mają no_log: true, żeby wartości nie trafiały do logów wykonania.
Idempotentność i tryb sprawdzania
Playbook uruchomiony dwa razy z rzędu ma za drugim razem zgłosić zero zmian. Jeśli zgłasza zmiany, coś jest napisane przez shell lub command bez warunku creates albo changed_when. Każda taka konstrukcja ma komentarz, dlaczego moduł nie wystarczył.
Tryb --check musi działać dla całego playbooka. Zadania, które nie potrafią go obsłużyć, dostają check_mode: false świadomie i z komentarzem, nie przez przypadek.
Testy i wersje
Minimum, które da się utrzymać bez dedykowanego narzędzia: ansible-lint i --syntax-check uruchamiane automatycznie przy każdej zmianie w repozytorium oraz cykliczne uruchomienie playbooka na czystej maszynie testowej. Kolekcje w requirements.yml mają przypięte wersje; aktualizacja wersji jest osobną, świadomą zmianą.
Podsumowanie
Playbook, który przeżyje rotację, nie jest sprytniejszy od innych, tylko przewidywalny: struktura katalogów mówi, gdzie czego szukać, role robią jedną rzecz, zmienne mają jedno miejsce, sekrety są poza kodem, a dwa uruchomienia dają ten sam wynik. Następny administrator nie musi rozumieć autora, wystarczy, że rozumie konwencję.