Повтор задачи до условия: until, retries и delay

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

Повтор задачи до условия: until, retries и delay

Сообщение 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
Знакомая боль: ты перезапустил сервис, тут же бьёшь по нему health-check, а он ещё не поднялся. Или контейнер стартовал, но приложение внутри прогревает кеш секунд десять. Плейбук падает не потому, что ты ошибся, а потому, что Ansible сделал шаг раньше, чем система была готова. Лечится это не костылём со sleep, а штатными механизмами ожидания: повтором задачи до условия и явным ожиданием порта или файла. Разберём until, retries, delay, а заодно pause, wait_for и wait_for_connection - набор, без которого реальные плейбуки разваливаются на ровном месте.

Повтор задачи в Ansible: until, retries и delay

Ключевая идея простая. Обычная задача выполняется один раз. Если добавить к ней until, Ansible будет повторять её, пока условие не станет истинным или пока не кончатся попытки. Это и есть повтор задачи в Ansible, и реализован он тремя директивами уровня задачи:
  • until - условие выхода. Пишется как Jinja2-выражение, но БЕЗ фигурных скобок (оно шаблонизируется неявно). Пока выражение ложно - крутим дальше.
  • retries - сколько раз повторить, если условие не выполнилось. По умолчанию 3.
  • delay - пауза в секундах между попытками. По умолчанию 5.
Логика связки ansible until и ansible retries такая: задача выполняется, результат проверяется условием until. Если истина - идём дальше. Если ложь - ждём delay секунд и пробуем снова, и так до retries раз. Чтобы было что проверять, результат задачи надо сохранить через register. Условие почти всегда смотрит на поля этого результата: rc (код возврата команды), stdout, или признак удачи.

Вот канонический пример - ждём, пока веб-приложение начнёт отвечать кодом 200:

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

- name: Дождаться готовности приложения по HTTP
  ansible.builtin.uri:
    url: http://localhost:8080/health
    status_code: 200
  register: app_health
  until: app_health.status == 200
  retries: 30
  delay: 5
Здесь Ansible сделает до 30 попыток с паузой 5 секунд - то есть готов ждать до двух с половиной минут, прежде чем сдаться. Обрати внимание: если задача упала бы по ошибке (например, connection refused), сам модуль uri вернул бы fail, но until перехватывает это и повторяет. До исчерпания retries падения внутри попыток не считаются фатальными.

Часто проверяют не HTTP, а вывод команды. Тогда условие вешают на rc или на содержимое stdout:

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

- name: Ждать, пока сервис в systemd станет active
  ansible.builtin.command: systemctl is-active nginx
  register: svc_state
  until: svc_state.stdout == "active"
  retries: 12
  delay: 5
  changed_when: false
Мелочь, на которой спотыкаются: systemctl is-active возвращает ненулевой rc, пока сервис не активен. Без until такая задача просто упала бы. С until мы используем её как опрос. И обязательно ставим changed_when: false, иначе диагностическая команда будет вечно числиться как changed.

Изображение

Пауза в плейбуке: ansible pause, wait_for и wait_for_connection

until хорош, когда есть что проверять командой. Но иногда нужна именно пауза в плейбуке или ожидание конкретного ресурса - порта, файла, доступности хоста. Для этого есть три отдельных инструмента, и путать их не стоит.

ansible.builtin.pause - тупая пауза или остановка с вопросом к человеку. Используй ansible pause осторожно: жёсткий sleep на N секунд - это антипаттерн, потому что либо ждёшь слишком мало, либо слишком долго. А вот пауза с подтверждением оператора перед опасным шагом - вполне законно.

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

- name: Подождать оператора перед выкаткой на прод
  ansible.builtin.pause:
    prompt: "Проверь дашборд и нажми Enter для продолжения"

- name: Дать БД 10 секунд на прогрев (если очень надо)
  ansible.builtin.pause:
    seconds: 10
ansible.builtin.wait_for - умное ожидание. Это рабочая лошадка: ждёт, пока на хосте откроется TCP-порт, исчезнет порт, появится или удалится файл, либо в файле/логе появится нужная строка. Ansible wait_for запускается на удалённом хосте и не требует, чтобы порт смотрел наружу.

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

- name: Дождаться, пока приложение начнёт слушать порт 8080
  ansible.builtin.wait_for:
    port: 8080
    host: 127.0.0.1
    state: started
    delay: 3
    timeout: 60

- name: Дождаться появления PID-файла
  ansible.builtin.wait_for:
    path: /run/myapp/app.pid
    state: present
    timeout: 30
Параметры по делу: port и host - что слушать, state со значениями started, stopped, drained или present/absent, delay - сколько подождать перед первой проверкой, timeout - общий лимит (по умолчанию 300 секунд). Можно ждать строку в логе через параметр search_regex - удобно ловить "Server started" в выводе приложения.

ansible.builtin.wait_for_connection - особый случай. Он ждёт не порт, а саму возможность Ansible достучаться до хоста по SSH. Незаменим после перезагрузки. Связка классическая: перезагрузили - дождались, пока хост снова отвечает.

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

- name: Перезагрузить сервер
  ansible.builtin.reboot:
    reboot_timeout: 600

# reboot уже сам ждёт возврата, но если ребутишь руками:
- name: Послать reboot и не ждать
  ansible.builtin.shell: sleep 2 && systemctl reboot
  async: 1
  poll: 0

- name: Дождаться, пока система снова станет доступна
  ansible.builtin.wait_for_connection:
    delay: 15
    timeout: 300
    sleep: 5
Запомни разницу: wait_for проверяет ресурс (порт/файл) и крутится с управляющей машины или на самом хосте, а wait_for_connection проверяет именно транспорт до хоста и нужен после ребута, когда SSH временно отваливается. Модуль reboot обычно делает это за тебя, но знать про wait_for_connection надо.

Циклы, индексы и спецпеременные, плюс типичные грабли

until можно комбинировать с loop: тогда каждый элемент цикла прогоняется со своими retries. Результат по элементам собирается в registered-переменную в поле results - это список. Доступ к индексу текущей итерации даёт спецпеременная loop. У неё внутри цикла есть полезные поля: ansible_loop.index (нумерация с 1), ansible_loop.index0 (с 0), а через loop_control можно включить расширенную информацию.

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

- name: Ждать готовности нескольких портов с индексацией
  ansible.builtin.wait_for:
    port: "{{ item }}"
    timeout: 30
  loop:
    - 8080
    - 9090
    - 5432
  loop_control:
    extended: true
  register: ports_check
После выполнения ports_check.results - это список, где results[0] относится к порту 8080 и так далее. Индексацию удобно использовать, когда надо в условии или сообщении сослаться на конкретный шаг цикла.

Теперь грабли, на которых спотыкаются почти все:
  • Фигурные скобки в until. Условие until шаблонизируется неявно. Писать until: "{{ x.rc == 0 }}" не нужно и иногда вредно - достаточно until: x.rc == 0.
  • Условие, которое никогда не станет истинным. Если опечатался в имени поля (stout вместо stdout), задача честно отработает все retries и упадёт. Всегда сначала проверь реальную структуру результата через debug с var.
  • changed/failed внутри until. Промежуточные провалы попыток НЕ считаются ошибкой, пока есть retries. Но если retries кончились, а условие так и ложно - задача падает. Это ожидаемо.
  • sleep вместо ожидания. pause с фиксированными секундами вместо wait_for или until - хрупко. На медленной машине не хватит, на быстрой - потеряешь время.
  • wait_for порта на 0.0.0.0 vs 127.0.0.1. Если приложение слушает только localhost, указывай host: 127.0.0.1, иначе ожидание зависнет до таймаута.
Для готовящихся к экзамену: связка until/wait_for - типовой приём, который реально встречается в задачах RHCE (EX294). Сценарий "перезапусти сервис и убедись, что он поднялся" или "дождись, пока порт откроется" - именно то место, где экзаменуемые либо ставят костыльный sleep, либо забывают register и валят idempotency. Делай по уму - и проверка зачтётся с первого прохода.

Мини-лаба: дождаться готовности приложения после рестарта

Повтори руками на любой RHEL/Fedora-машине (на Ubuntu/Debian замени dnf на apt, остальное идентично; на RED OS и Astra тоже работает - там тот же systemd):
  • Поставь и запусти nginx: модулем ansible.builtin.dnf, затем ansible.builtin.service со state: restarted.
  • Сразу после рестарта добавь задачу wait_for на port: 80, state: started, timeout: 30 - убедись, что плейбук не идёт дальше, пока порт не открылся.
  • Сделай health-check через ansible.builtin.uri с until по status == 200, retries: 10, delay: 3.
  • Намеренно сломай: укажи в until несуществующее поле и посмотри, как задача отрабатывает все попытки и падает. Почини.
  • Добавь loop_control: extended: true к какому-нибудь циклу и выведи через debug значение ansible_loop.index.
Контрольные вопросы:
  • Чем until отличается от loop и какие две директивы всегда идут с ним в паре?
  • Почему в условии until обычно не нужны фигурные скобки {{ }}?
  • В каком случае берёшь wait_for, а в каком - wait_for_connection?
  • Что вернёт register, если until стоит на задаче с loop, и как достать результат конкретной итерации?
Итог: until + retries + delay превращают любую команду в опрос "пока не готово", wait_for ждёт порт или файл, wait_for_connection - доступность хоста после ребута, а pause оставь для подтверждений человеком, а не для угадывания таймаутов. Это переводит плейбуки из разряда "работает, когда повезёт" в разряд предсказуемых.
👍3 ❤️3 🔥2 😄 🤔
Аватара пользователя
lena88
Сообщения: 1
Зарегистрирован: 27 май 2026, 13:35

Re: Повтор задачи до условия: until, retries и delay

Сообщение lena88 »

Спасибо, наконец дошло чем wait_for отличается от wait_for_connection. Я раньше после ребута тупо ставил pause: seconds 60 и молился. Теперь ясно почему иногда не хватало.
👍1 ❤️2 🔥 😄 🤔
Аватара пользователя
Mender
Сообщения: 1
Зарегистрирован: 20 май 2026, 15:24

Re: Повтор задачи до условия: until, retries и delay

Сообщение Mender »

Поймал ровно те грабли с фигурными скобками - писал until: "{{ res.rc == 0 }}" и оно вело себя странно. Убрал скобки, заработало как надо. Плюс не забывайте changed_when: false на is-active, а то весь вывод красный.
👍1 ❤️1 🔥1 😄 🤔
Ответить
← Предыдущая глава
Циклы в Ansible: loop, списки и словари
Следующая глава →
Условия в Ansible: директива when и логика

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

Поделиться темой: ✈ Telegram VK
Похожие запросы: условие when в ansible как задать в задаче

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

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

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