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.yml z wszystkimi zmiennymi, jakich używa, z wartościami domyślnymi i komentarzem przy każdej,
  • README.md z jednym akapitem: co robi, czego wymaga, przykład użycia,
  • meta/main.yml z 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:

  1. defaults/ w roli: wartości rozsądne dla większości przypadków.
  2. group_vars/ w inventory: różnice między środowiskami i grupami serwerów.
  3. 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ę.