Przebieg playbooka kończy się na zielono, jednostka WireGuard jest aktywna, handshake działa, a nowy peer nadal nic nie osiąga. Patrzenie, jak playbook raportuje sukces, podczas gdy serwer działa na wczorajszej konfiguracji, jest szczególnie frustrujące, bo w danych wyjściowych nic nie jest błędne. Ansible zrobił dokładnie to, o co poproszono: upewnił się, że jednostka jest uruchomiona, a była już uruchomiona. Zastosowanie przepisanego pliku konfiguracji to inne zadanie i zdarza się tylko wtedy, gdy coś każe usłudze przeczytać go ponownie.

Zobacz, co naprawdę raportuje drugie uruchomienie

Uruchom playbook dwa razy ze zmienioną konfiguracją i porównaj podsumowanie. Drugi przebieg powinien pokazać zadanie jednostki jako ok, a nie changed, bo już działająca usługa jest z założenia zostawiana w spokoju. To idempotencja działająca poprawnie i jednocześnie powód, dla którego nowy plik nigdy nie trafia do jądra.

Dodaj przebieg kontrolny z wyjściem diff, aby zobaczyć, które zadania w ogóle czegokolwiek dotkną.

ansible-playbook -i inventory site.yml --check --diff
systemctl show wg-quick@wg0 -p ActiveEnterTimestamp -p NRestarts
stat -c '%y %n' /etc/wireguard/wg0.conf

Trzy znaczniki czasu opowiadają historię, zanim przeczytasz choćby jedną linię logu. Jeśli plik konfiguracji jest nowszy niż moment, w którym jednostka stała się aktywna, działający interfejs zbudowano ze starszego pliku, a każdy kolejny objaw z tego wynika.

To nie jest wada Ansible. To samo zachowanie pojawia się w każdym narzędziu do zarządzania konfiguracją, które oddziela „upewnij się, że działa” od „upewnij się, że działa to”. Różnica wygląda pedantycznie, dopóki nie kosztuje cię popołudnia.

Uruchomione to stan, a nie ponowne wczytanie

state: started stwierdza warunek dotyczący jednostki. Nie obiecuje, że usługa ponownie odczyta swoje dane wejściowe, i nie może wiedzieć, że plik, którego nigdy nie otworzyła, się zmienił. We wdrożeniu wg-quick dane wejściowe obejmują konfigurację interfejsu, adresy, trasy, wpisy DNS i wszelkie haki post-up.

Dlatego zaktualizowany plik może zawierać nowy peer, inny adres lub zmienioną dyrektywę routingu, podczas gdy działający interfejs nadal obsługuje stary zestaw. Handshake wciąż się udaje, bo wczytane klucze i punkty końcowe są nadal ważne. Właśnie to czyni awarię trudną do zauważenia i dlatego ludzie godzinę gonią DNS, zanim sprawdzą znacznik czasu.

Dwie inne opcje modułu są często brane za rozwiązanie. daemon_reload każe systemd ponownie odczytać pliki jednostek, co jest nieistotne dla pliku konfiguracji WireGuard. state: reloaded prosi jednostkę o wykonanie akcji przeładowania, a to, czy robi ona to, czego potrzebujesz, zależy wyłącznie od jednostki. Żadna z nich nie zastępuje wiedzy, które dane wejściowe faktycznie odczytał działający proces.

Zainstaluj plik, potem powiadom handler

Wzorzec, który zachowuje idempotencję i mimo to stosuje zmiany, jest zwyczajny: zadanie zapisujące plik powiadamia handler, a handler uruchamia się tylko wtedy, gdy plik naprawdę się zmienił. Drugi przebieg bez zmian nie robi nic i tego właśnie chcesz.

- name: write the interface configuration
  ansible.builtin.template:
    src: wg0.conf.j2
    dest: /etc/wireguard/wg0.conf
    owner: root
    mode: "0600"
  notify: apply wg0

- name: ensure the interface unit is enabled
  ansible.builtin.systemd_service:
    name: wg-quick@wg0
    enabled: true
    state: started

- name: apply wg0
  listen: apply wg0
  ansible.builtin.systemd_service:
    name: wg-quick@wg0
    state: restarted

Handlery uruchamiają się na końcu playa, po zadaniach, które je powiadomiły, więc późniejsze zadanie w tym samym playu może wciąż widzieć stary interfejs. Jeśli kolejność ma znaczenie, opróżnij handlery jawnie przez meta: flush_handlers przed zadaniem, które zależy od nowej konfiguracji.

Używaj restart zamiast reload, chyba że sprawdziłeś akcję przeładowania jednostki. Jednostka wg-quick z upstreamu obsługuje taką i synchronizuje konfigurację WireGuard, ale nie powtarza konfiguracji adresów, tras, DNS ani haków. Dlatego reload po dodaniu dyrektywy trasy wygląda na udany i niczego nie zmienia.

Porównaj działające urządzenie z plikiem

Kontrola, która zamyka tę klasę błędów, to porównanie tego, co trzyma jądro, z tym, co mówi plik. Dwie komendy dają porównywalne wyjście, a różnica to odchylenie.

sudo wg showconf wg0
sudo wg-quick strip wg0
sudo diff <(sudo wg showconf wg0) <(sudo wg-quick strip wg0)

wg showconf wypisuje działające urządzenie, łącznie z kluczem prywatnym, więc traktuj to wyjście jako sekret i nie wklejaj go do zgłoszenia. wg-quick strip wypisuje plik bez kluczy specyficznych dla wg-quick i dlatego te dwa wyjścia w ogóle można porównać.

Różnica na liście peerów oznacza, że plik nigdy nie został zastosowany. Różnica ograniczona do kluczy lub punktów końcowych, których zmiany oczekiwałeś, oznacza, że coś zastosowało go częściowo. Brak różnicy oznacza, że konfiguracja jest aktualna, a usterka jest gdzie indziej, co samo w sobie jest użytecznym wynikiem.

Zapłać koszt restartu świadomie

Restart interfejsu zrywa tunel, a jeśli tunel przenosi twoją sesję administracyjną, restart kończy sesję, która go zażądała. Umieść handler za oknem zmian albo uruchamiaj playbook ze ścieżki zarządzania, która nie zależy od restartu interfejsu.

Koszt jest powodem, dla którego ludzie sięgają po reload. To rozsądny odruch i staje się słuszny, gdy wiesz, które zmiany reload naprawdę stosuje. Dla zmian peerów i kluczy na jednostce wg-quick z upstreamu to wystarcza; dla adresacji, routingu i haków nie. Wyjaśnienie tego raz jest tańsze niż odkrycie tego w trakcie okna zmian.

Dokumentacja modułu systemd_service wyjaśnia różnicę między started, restarted i reloaded, i warto ją raz przeczytać zamiast zgadywać z zielonego podsumowania. Wszystko w tym przewodniku wynika z tej jednej różnicy.

Powiązane artykuły