Обработчики Ansible: handlers, notify и flush_handlers

Рейтинг: 72.2% · 10 голосов
Подробный курс по Ansible с прицелом на экзамен RHCE EX294: установка и инвентарь, плейбуки, переменные и факты, Vault, циклы и условия, шаблоны Jinja2, роли и коллекции, Execution Environments и ansible-navigator, управление системами (диски, LVM, cron, SELinux, firewalld). Примеры на RHEL/Fedora. Актуально на 2026.
Ответить
Аватара пользователя
Maksim_DevOps
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Обработчики Ansible: handlers, notify и flush_handlers

Сообщение Maksim_DevOps »

Оглавление курса (47)
  1. Что такое Ansible и зачем он нужен: автоматизация без агентов
  2. Сертификация RHCE и экзамен EX294: что внутри и как готовиться
  3. Архитектура Ansible: control node, узлы, модули и плагины
  4. Установка Ansible на control node: dnf, pip и версии ansible-core
  5. Подготовка управляемых узлов: SSH, пользователь и sudo
  6. Инвентарь Ansible: hosts, группы и переменные
  7. Настройка Ansible: файл ansible.cfg и приоритеты конфигурации
  8. Host patterns и инструменты командной строки Ansible
  9. Ad hoc команды Ansible: быстрые задачи без плейбука
  10. Ansible playbook: что это такое и как устроен
  11. Структура плейбука: play, task, модули и переменные
  12. Запуск плейбуков: проверка, теги, ограничения и отладка
  13. Параллелизм и порядок выполнения: forks, serial, strategy
  14. Факты Ansible: сбор информации об узлах
  15. Переменные Ansible: типы, объявление и приоритет
  16. Регистрация результатов и специальные переменные
  17. Ansible Vault: шифрование паролей и секретов
  18. Организация инвентаря: group_vars, host_vars и вложенные группы
  19. Динамический инвентарь и инвентарные плагины
  20. Циклы в Ansible: loop, списки и словари
  21. Повтор задачи до условия: until, retries и delay
  22. Условия в Ansible: директива when и логика
  23. Jinja2 в Ansible: выражения, фильтры и подстановки
  24. Обработчики Ansible: handlers, notify и flush_handlers (вы здесь)
  25. Обработка ошибок: failed_when, changed_when и ignore_errors
  26. Блоки в Ansible: block, rescue и always
  27. Управление файлами: модули file, copy, fetch и stat
  28. Архивы и сборка файлов: archive, unarchive, assemble
  29. Точечное редактирование файлов: lineinfile и blockinfile
  30. Шаблоны Jinja2: модуль template и динамические конфиги
  31. Jinja2 продвинуто: фильтры, циклы, макросы и lookup
  32. Модули Ansible: ansible-doc и поиск нужного модуля
  33. Разработка собственного модуля Ansible на Python
  34. Роли Ansible и Ansible Galaxy: переиспользование
  35. Разработка роли Ansible: создание с нуля
  36. Зависимости ролей и requirements.yml
  37. Коллекции Ansible: ansible.posix, community и своя коллекция
  38. Execution Environments: контейнеры для запуска Ansible
  39. Сборка Execution Environment с ansible-builder
  40. ansible-navigator: запуск плейбуков в Execution Environment
  41. Управление SSH-ключами через Ansible: authorized_key
  42. Управление дисками: filesystem, mount и parted
  43. LVM через Ansible: тома, группы и расширение
  44. Планировщик задач: cron и systemd timers через Ansible
  45. Безопасность: hardening SSH и репозитории dnf/yum
  46. SELinux через Ansible: булевы, контексты и порты
  47. Сквозной проект и подготовка к экзамену EX294
Представь типичную ситуацию. Ты раскатываешь nginx, кладёшь свежий конфиг, а потом в каждой роли честно дописываешь задачу "перезапустить сервис". Запускаешь плейбук второй раз - конфиг не менялся, а nginx всё равно ребутится. И так на каждом прогоне, на каждом хосте. Сервис дёргается на ровном месте, клиенты ловят разрыв соединения, а ты сидишь и думаешь, почему idempotent-инструмент ведёт себя как простыня bash-скриптов.

Решение - обработчики. Это та самая фишка, ради которой Ansible придумывал отдельный механизм: выполнять реакцию только тогда, когда реально что-то поменялось. Поменялся конфиг - перезапустили. Не поменялся - тишина. В этом уроке разберём, как работают ansible handlers, чем notify отличается от обычного вызова, зачем нужен listen и в каких случаях спасает flush_handlers.

Что такое обработчики ansible и как работает notify

Обработчик (handler) - это обычная задача, которая лежит в отдельном разделе handlers и по умолчанию не выполняется. Она ждёт уведомления. Уведомление шлёт другая задача через директиву notify, но - и это ключевой момент - только если сама задача вернула статус changed. Если задача отработала с результатом ok (ничего не изменилось), notify молчит, и обработчик не дёргается.

Второй важный момент: обработчики не запускаются сразу. Ansible собирает все уведомления в течение play и выполняет обработчики один раз в самом конце, после того как отработали все обычные задачи. Даже если десять задач уведомили один и тот же handler, он выполнится ровно один раз. Это и есть идемпотентность, которую мы хотим: лишний перезапуск сервиса не случится.

Классический пример - копируем конфиг и перезапускаем сервис только при изменении:

Код: Выделить всё

- name: Настройка nginx
  hosts: web
  become: true

  tasks:
    - name: Установить nginx
      ansible.builtin.dnf:
        name: nginx
        state: present

    - name: Выложить конфиг nginx
      ansible.builtin.copy:
        src: files/nginx.conf
        dest: /etc/nginx/nginx.conf
        owner: root
        group: root
        mode: "0644"
      notify: Restart nginx

  handlers:
    - name: Restart nginx
      ansible.builtin.systemd:
        name: nginx
        state: restarted
        enabled: true
Логика простая. Если файл /etc/nginx/nginx.conf реально изменился, задача copy вернёт changed, сработает notify, и в конце play обработчик "Restart nginx" перезапустит сервис. Прогонишь плейбук повторно без правок в конфиге - copy вернёт ok, уведомления не будет, nginx останется в покое. Именно это просят показать на экзамене RHCE (EX294): ansible handler restart сервиса при изменении файла - одна из самых частых задач в билете, и решается она именно так, а не через бездумный state: restarted в tasks.

На RHEL-семействе (RHEL, Fedora, Rocky, AlmaLinux, RED OS, Astra) под капотом systemd, поэтому ansible.builtin.systemd - правильный выбор. Если работаешь с Ubuntu/Debian, всё то же самое, только имя пакета будет nginx и менеджер пакетов ansible.builtin.apt вместо dnf, а сам механизм обработчиков идентичен.

Изображение

listen: один notify на пачку обработчиков

Часто после смены конфига надо не просто перезапустить один сервис, а сделать несколько действий: перезагрузить демон, прогреть кеш, пнуть мониторинг. Можно перечислить в notify список имён, но есть способ изящнее - директива listen. Она задаёт обработчику "тему" (topic), на которую тот подписывается. Уведомив тему, ты запустишь сразу все обработчики, которые её слушают.

Код: Выделить всё

  tasks:
    - name: Развернуть конфиг приложения
      ansible.builtin.template:
        src: app.conf.j2
        dest: /etc/myapp/app.conf
        mode: "0640"
      notify: "config changed"

  handlers:
    - name: Перезапустить приложение
      ansible.builtin.systemd:
        name: myapp
        state: restarted
      listen: "config changed"

    - name: Сбросить кеш
      ansible.builtin.command: /usr/local/bin/myapp-cache-flush
      listen: "config changed"

    - name: Перечитать firewalld
      ansible.posix.firewalld:
        service: http
        permanent: true
        state: enabled
        immediate: true
      listen: "config changed"
Одно событие "config changed" - три обработчика отработали. Это удобно ещё и потому, что роли становятся переносимыми: разные роли могут подписываться на общую тему, не зная имён конкретных задач друг друга. Имя обработчика в notify и имя/тема в listen должны совпадать символ в символ, включая регистр и пробелы. Тут многие и спотыкаются - об этом ниже.

flush_handlers: запустить обработчики прямо сейчас

По умолчанию обработчики ждут конца play. Но иногда так нельзя. Допустим, ты сменил конфиг сервиса, и следующая же задача должна обращаться к этому сервису по сети - проверить, что он поднялся с новыми настройками. Если обработчик с рестартом сработает только в конце, проверка пойдёт по старой конфигурации. Нужно перезапустить сервис посреди play, до остальных задач.

Для этого есть мета-задача ansible.builtin.meta: flush_handlers. Она принудительно выполняет все обработчики, накопившие уведомления к этому моменту, прямо там, где ты её поставил.

Код: Выделить всё

  tasks:
    - name: Поправить конфиг демона
      ansible.builtin.template:
        src: sshd_config.j2
        dest: /etc/ssh/sshd_config.d/99-custom.conf
        validate: "sshd -t -f %s"
        mode: "0600"
      notify: Restart sshd

    - name: Принудительно прогнать обработчики сейчас
      ansible.builtin.meta: flush_handlers

    - name: Проверить, что sshd слушает порт
      ansible.builtin.wait_for:
        port: 22
        timeout: 30
Здесь flush_handlers гарантирует: sshd перезапустится до проверки порта, а не в конце. Обрати внимание на validate в template - конфиг проверяется до записи, чтобы не уронить демон битым файлом. Это отдельная хорошая привычка, особенно для sshd, где ошибка стоит дорого.

Типичные грабли

Имя не совпадает. Самая частая беда. notify: restart Nginx и handler с именем Restart nginx - это два разных имени. Ansible не ругается, просто молча не запускает обработчик. Внимательно следи за регистром и пробелами. Хорошая практика - копировать имя из handlers в notify, а не печатать заново.

Ошибка до flush. Если задача между notify и концом play (или до flush_handlers) упадёт, обработчики по умолчанию не выполнятся - они привязаны к финалу play, до которого исполнение просто не дойдёт. Хост уведомил обработчик о рестарте, но упал на следующей задаче - и остался со свежим конфигом, но старым работающим сервисом. На повторном прогоне copy вернёт ok (файл-то уже лежит), notify не сработает, и рассинхрон сохранится. Лечится либо ранним meta: flush_handlers сразу после критичной задачи, либо флагом

Код: Выделить всё

--force-handlers
/ параметром play

Код: Выделить всё

force_handlers: true
, который прогоняет накопленные обработчики даже после падения.

Порядок выполнения. Обработчики запускаются не в порядке уведомлений, а в порядке их объявления в разделе handlers (с учётом тем listen). Если тебе важно, чтобы "reload daemon" шёл строго перед "restart service", расставляй их в нужном порядке именно в блоке handlers, а не надейся на очередь notify.

Обработчик в роли. В роли обработчики кладут в roles/имя/handlers/main.yml, и notify из задач этой роли их видит автоматически. Но если уведомляешь обработчик другой роли - надёжнее завязываться на listen-тему, а не на имя.

Мини-лаба

Собери всё руками на тестовой RHEL/Fedora-машине:
  • Напиши плейбук, который ставит nginx через ansible.builtin.dnf и кладёт конфиг через ansible.builtin.copy с notify на обработчик Restart nginx.
  • Прогони дважды. Убедись, что во второй раз обработчик НЕ сработал (changed=0 на задаче copy, обработчик не в выводе).
  • Поменяй конфиг, прогони снова - теперь обработчик должен выполниться. Это и есть ansible notify в действии.
  • Добавь второй обработчик и переведи оба на общий listen-топик. Уведоми тему - проверь, что отработали оба.
  • Вставь meta: flush_handlers после задачи с конфигом и задачу wait_for после него. Убедись, что рестарт идёт до проверки.
  • Поломай имя в notify (смени регистр) и посмотри, как Ansible молча проглатывает уведомление - запомни этот симптом.
Контрольные вопросы
  • При каком статусе задачи notify реально уведомляет обработчик, а при каком нет?
  • Сколько раз выполнится один обработчик, если его уведомили пять разных задач за один play?
  • Зачем нужен ansible.builtin.meta: flush_handlers и чем плох вариант ждать конца play?
  • Что произойдёт с уведомлённым обработчиком, если задача между notify и концом play упадёт с ошибкой, и как это обойти?
Итог

Обработчики ansible - это механизм "реагировать только на изменения". notify шлёт уведомление при changed, обработчик копится и выполняется один раз в конце play, listen группирует несколько обработчиков под одной темой, а flush_handlers запускает их принудительно посреди play, когда дальнейшие задачи зависят от результата. Главные грабли - несовпадение имён и потерянные обработчики после ошибки. Запомни связку notify плюс handler restart сервиса при изменении конфига: на RHCE она встречается почти гарантированно, и решать её надо именно обработчиками, а не лобовым перезапуском в каждом прогоне.
👍2 ❤️3 🔥 😄 🤔
Аватара пользователя
addict_artem
Сообщения: 1
Зарегистрирован: 21 май 2026, 19:18

Re: Обработчики Ansible: handlers, notify и flush_handlers

Сообщение addict_artem »

Спасибо, наконец дошло зачем flush_handlers. У меня sshd проверка падала потому что рестарт уезжал в конец play, поставил meta: flush_handlers перед wait_for и заработало.
👍2 ❤️1 🔥 😄 🤔1
Аватара пользователя
bonzo80
Сообщения: 1
Зарегистрирован: 21 май 2026, 17:03

Re: Обработчики Ansible: handlers, notify и flush_handlers

Сообщение bonzo80 »

Полдня искал почему обработчик не дёргается, а это был регистр в имени - notify restart Nginx, а handler Restart nginx. Реально молча проглатывает, ни ошибки ни предупреждения. Теперь копирую имя из handlers.
👍1 ❤️1 🔥 😄 🤔1
Ответить
← Предыдущая глава
Jinja2 в Ansible: выражения, фильтры и подстановки
Следующая глава →
Обработка ошибок: failed_when, changed_when и ignore_errors

Все главы курса «Ansible: автоматизация и подготовка к RHCE (EX294)»

Поделиться темой: ✈ Telegram VK
Похожие запросы: что такое ansible простыми словамиansible для начинающих с чего начатьустановка ansible на linuxansible inventory и hosts файлчто такое playbook в ansibleструктура ansible playbook из чего состоит

Вернуться в «Ansible: автоматизация и подготовка к RHCE (EX294)»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость