Ansible сообщает, что сервис запущен, но WireGuard работает со старой конфигурацией
state: started не трогает уже работающий unit, поэтому перезаписанный wg0.conf так и не применяется. Добавьте обработчик изменения, а затем сравните работающее устройство с файлом.
Прогон 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, и её стоит один раз прочитать, вместо того чтобы гадать по зелёной сводке. Всё в этом руководстве следует из этого единственного различия.