Прогін playbook завершується зеленим, unit WireGuard активний, рукостискання проходить, а новий peer усе одно нікуди не потрапляє. Бачити звіт про успіх, коли сервер працює з учорашньою конфігурацією, особливо прикро, бо у виводі немає нічого неправильного. Ansible зробив рівно те, про що його просили: переконався, що unit запущено, а він уже був запущений. Застосування перезаписаного файлу конфігурації — окреме завдання, і воно відбувається лише якщо щось скаже службі перечитати файл.

Подивіться, що насправді повідомляє другий прогін

Запустіть playbook двічі зі зміненою конфігурацією і порівняйте підсумок. Другий прогін має показати завдання unit як ok, а не changed, бо вже працююча служба за задумом не чіпається. Це коректно працююча ідемпотентність, і це ж причина, чому новий файл ніколи не доходить до ядра.

Додайте перевірний прогін із виводом diff, щоб побачити, які завдання взагалі чогось торкнуться.

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

Три позначки часу розповідають історію ще до читання одного рядка логу. Якщо файл конфігурації новіший за момент, коли unit став активним, робочий інтерфейс побудований зі старішого файлу, і всі подальші симптоми випливають із цього.

Це не дефект Ansible. Така сама поведінка виявляється в будь-якому інструменті керування конфігурацією, який розділяє «переконайся, що працює» і «переконайся, що працює саме це». Різниця здається педантичною, доки не коштує вам півдня.

Запущено — це стан, а не перезавантаження

state: started стверджує умову про unit. Він не обіцяє, що служба перечитає свої вхідні дані, і не може знати, що файл, який вона ніколи не відкривала, змінився. Під час розгортання через wg-quick вхідні дані включають конфігурацію інтерфейсу, адреси, маршрути, записи DNS і будь-які хуки post-up.

Тому оновлений файл може містити новий peer, іншу адресу або змінену директиву маршрутизації, поки робочий інтерфейс продовжує обслуговувати старий набір. Рукостискання все ще проходить, бо завантажені ключі та кінцеві точки ще дійсні. Саме це робить збій важко помітним і пояснює, чому люди годину ганяються за DNS, перш ніж перевірити позначку часу.

Ще дві опції модуля часто вважають рішенням. daemon_reload змушує systemd перечитати файли unit, що не має відношення до файлу конфігурації WireGuard. state: reloaded просить unit виконати свою дію перезавантаження, і чи робить воно те, що вам потрібно, повністю залежить від unit. Жодна не замінює знання того, які вхідні дані робочий процес справді прочитав.

Встановіть файл, потім повідомте обробник

Схема, яка зберігає ідемпотентність і все ж застосовує зміни, звичайна: завдання, що записує файл, повідомляє обробник, а обробник запускається лише коли файл справді змінився. Другий прогін без правок не робить нічого, і це саме те, що потрібно.

- 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

Обробники запускаються в кінці play, після завдань, які їх повідомили, тому пізніше завдання в тому ж play може ще бачити старий інтерфейс. Якщо порядок важливий, явно очистіть обробники через meta: flush_handlers перед завданням, яке залежить від нової конфігурації.

Використовуйте restart, а не reload, якщо ви не перевірили дію перезавантаження unit. Верхній unit wg-quick підтримує її і синхронізує конфігурацію WireGuard, але не повторює налаштування адрес, маршрутів, DNS чи хуків. Тому reload після додавання директиви маршруту виглядає успішним і нічого не змінює.

Порівняйте робочий пристрій із файлом

Перевірка, що закриває цей клас помилок, — це порівняння того, що тримає ядро, з тим, що каже файл. Дві команди дають порівнюваний вивід, а різниця — це відхилення.

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

wg showconf друкує робочий пристрій, включно з приватним ключем, тому ставтеся до цього виводу як до секрету і не вставляйте його в заявку. wg-quick strip друкує файл без ключів, специфічних для wg-quick, і саме тому ці два виводи взагалі можна порівнювати.

Різниця в списку peer означає, що файл ніколи не застосовувався. Різниця лише в ключах або кінцевих точках, які ви очікували змінити, означає, що щось застосувало його частково. Відсутність різниці означає, що конфігурація актуальна, а несправність в іншому місці, що саме по собі корисний результат.

Сплатіть ціну перезапуску свідомо

Перезапуск інтерфейсу обриває тунель, і якщо цей тунель несе вашу адміністративну сесію, перезапуск завершує сесію, яка його попросила. Помістіть обробник за вікно змін або запускайте playbook із шляху керування, який не залежить від перезапуску інтерфейсу.

Ціна — причина, чому люди хапаються за reload. Це розумний інстинкт, і він стає правильним, коли ви знаєте, які зміни reload справді застосовує. Для змін peer і ключів на верхньому unit wg-quick цього достатньо; для адресації, маршрутизації та хуків — ні. Розібратися один раз дешевше, ніж виявити це під час вікна змін.

Документація модуля systemd_service пояснює різницю між started, restarted і reloaded, і її варто один раз прочитати, замість того щоб гадати за зеленою зведенням. Усе в цьому посібнику випливає з цієї єдиної різниці.

Схожі статті