Параллелизм и порядок выполнения: forks, serial, strategy

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

Параллелизм и порядок выполнения: forks, serial, strategy

Сообщение 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
Представь: у тебя 200 веб-серверов за балансировщиком, и ты катишь новую версию приложения. Запускаешь плейбук - и Ansible радостно начинает рестартить сервис разом на всех машинах. Через секунду балансировщик видит ноль живых бэкендов, мониторинг орёт, а ты судорожно жмёшь Ctrl+C. Знакомая боль? Весь этот урок - про то, как Ansible решает, сколько хостов трогать одновременно и в каком порядке, и как этим управлять, чтобы выкатка была безопасной, а не лотереей.

Разберём четыре рычага: forks (сколько параллельных соединений), serial (батчи для rolling update), strategy (как именно гонять задачи по хостам) и порядок плюс мелкие, но важные throttle, run_once, order.

Как Ansible выполняет задачи: forks и параллелизм

По умолчанию Ansible работает не последовательно хост за хостом, а параллельно. Берётся первая задача плея, и она запускается сразу на пачке хостов. Размер этой пачки определяет параметр forks - количество одновременных процессов-соединений. Дефолт скромный: 5. То есть на 100 хостах задача выполнится пятью волнами по 20, даже если железо контроллера тянет больше.

Поднять лимит можно тремя способами. Разово через флаг командной строки:

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

ansible-playbook deploy.yml -f 30
Или зашить в конфиг ansible.cfg, чтобы не вспоминать каждый раз:

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

[defaults]
forks = 30
inventory = ./inventory
host_key_checking = False
Третий вариант - переменная окружения ANSIBLE_FORKS=30. Приоритет такой: флаг -f бьёт ansible.cfg, ansible.cfg бьёт дефолт. Тема ansible forks - это в первую очередь про скорость: на сотнях хостов разница между forks=5 и forks=50 видна невооружённым глазом. Но не задирай вслепую: каждый форк - это процесс на контроллере и SSH-сессия. Упрёшься в CPU и память управляющей машины или в лимиты sshd на целевых. Реальный потолок ищут замером, а не на глаз.

Изображение

ansible serial: батчи и rolling update без даунтайма

forks - это про скорость на уровне всего плея. А вот когда задача "не свалить прод" важнее скорости, в дело вступает директива serial на уровне плея. Она режет выполнение на батчи: Ansible полностью прогоняет ВЕСЬ плей (все задачи и хендлеры) на первой группе хостов, и только потом берётся за следующую. Это и есть классический ansible rolling update - выкатка волнами.

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

---
- name: Rolling update веб-фермы
  hosts: webservers
  serial: 2
  tasks:
    - name: Вывести хост из балансировщика
      ansible.builtin.command: /usr/local/bin/lb-drain {{ inventory_hostname }}

    - name: Обновить пакет приложения
      ansible.builtin.dnf:
        name: myapp
        state: latest

    - name: Перезапустить сервис
      ansible.builtin.systemd:
        name: myapp
        state: restarted

    - name: Вернуть хост в балансировщик
      ansible.builtin.command: /usr/local/bin/lb-undrain {{ inventory_hostname }}
serial: 2 значит "не больше двух машин одновременно выведено из строя". Можно задавать процентом - serial: 25%, тогда от группы будут браться четвертями. А можно списком, чтобы первый батч был маленьким (канареечный), а дальше крупнее:

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

  serial:
    - 1
    - 5
    - "30%"
Логика: сначала 1 хост - смотрим, не сломалось ли. Потом 5. Потом по 30% от остатка. Это канареечная выкатка в чистом виде.

Важная связка - max_fail_percentage. По умолчанию Ansible останавливает плей, как только в текущем батче упал хоть один хост (точнее, когда падают ВСЕ хосты батча - но с serial батчи маленькие, и это срабатывает быстро). С max_fail_percentage ты задаёшь порог терпимости:

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

- name: Безопасная выкатка с порогом ошибок
  hosts: webservers
  serial: 10%
  max_fail_percentage: 30
  tasks:
    - ansible.builtin.dnf:
        name: myapp
        state: latest
Если в батче упало больше 30% хостов, плей аварийно останавливается и не катит дальше. Логично: лучше остановить выкатку на трёх сбойных машинах, чем доломать все двести.

ansible strategy: linear, free и host_pinned

Теперь самое тонкое. Параметр strategy управляет тем, как Ansible синхронизирует хосты между задачами. Значений три.

linear - дефолт. Все хосты батча выполняют задачу №1, Ansible ждёт, пока КАЖДЫЙ закончит (или упадёт), и только тогда все вместе идут на задачу №2. Это барьерная синхронизация: самый медленный хост тормозит всю группу на каждом шаге. Зато предсказуемо и удобно для отладки.

free - барьер снимается. Каждый хост несётся по списку задач в своём темпе, не дожидаясь соседей. Быстрый сервер может уже доделать весь плей, пока тормозной всё ещё на третьей задаче. Ускоряет выполнение на разнородном парке, но порядок завершения непредсказуем, и логика "сначала все обновились, потом все рестартят" ломается.

host_pinned - компромисс. Хост, попавший в слот (слотов столько, сколько forks или сколько разрешает serial), проходит задачи без ожидания остальных, как во free. Но как только он закончил весь плей, его слот освобождается и занимает следующий хост из очереди. Удобно, когда важно держать ровно N хостов в работе одновременно.

Задаётся на уровне плея:

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

- name: Быстрая независимая настройка
  hosts: all
  strategy: free
  tasks:
    - ansible.builtin.dnf:
        name: chrony
        state: present
    - ansible.posix.firewalld:
        service: http
        permanent: true
        state: enabled
        immediate: true
Можно задать стратегию по умолчанию в ansible.cfg секцией [defaults] strategy = free, но обычно это решают на уровне конкретного плея.

Точечный контроль: order, throttle и run_once

order - в каком порядке Ansible обходит хосты внутри плея. По умолчанию inventory - как они идут в инвентаре. Доступны: inventory, reverse_inventory, sorted (по алфавиту), reverse_sorted и shuffle (случайно каждый раз). shuffle полезен, чтобы не долбить всегда первым один и тот же хост; sorted - когда хочешь детерминированности независимо от порядка в файле.

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

- name: Обход хостов по алфавиту
  hosts: dbservers
  order: sorted
  serial: 1
throttle - ограничивает параллелизм на уровне ОТДЕЛЬНОЙ задачи, перекрывая forks. Допустим, плей идёт на 30 форках, но конкретная задача дёргает внешний API, который не выдержит больше пяти запросов разом:

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

    - name: Дёрнуть капризный внешний API
      ansible.builtin.uri:
        url: https://api.internal/register
        method: POST
      throttle: 5
run_once - задача выполняется ровно один раз, на первом хосте батча, но результат доступен всем. Классика - один раз накатить миграцию БД или дёрнуть API, а не повторять на каждом веб-сервере:

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

    - name: Накатить миграции один раз
      ansible.builtin.command: /opt/myapp/bin/migrate
      run_once: true
      delegate_to: "{{ groups['webservers'][0] }}"
Связь с RHCE/EX294. serial на экзамене вполне может всплыть в задании на rolling-обновление - от тебя ждут плей, который обновляет группу батчами, а не разом. Стратегии и throttle спрашивают реже, но понимать linear против free полезно, чтобы не удивляться поведению. Главное на экзамене - помнить, что serial и strategy ставятся на уровне ПЛЕЯ, а throttle и run_once - на уровне задачи.

Типичные грабли
  • Рестарт сервиса без serial. Самая дорогая ошибка - снести сервис на всём парке одновременно. Любая выкатка с рестартом за балансировщиком должна идти через serial.
  • serial внутри задачи. serial - директива ПЛЕЯ, не задачи. Засунешь в task - получишь ошибку парсинга. То же касается strategy и order.
  • Думать, что forks=100 ускорит всё. Контроллер с 2 ядрами на сотне форков начнёт свопиться и работать медленнее, чем на двадцати. Меряй.
  • free там, где нужен порядок. При strategy: free хост может дойти до "рестарт" раньше, чем все прошли "обнови конфиг". Для согласованных выкаток оставляй linear.
  • Забыть max_fail_percentage. С большим serial без порога плей может укатать половину парка, прежде чем заметит, что новый пакет битый.
  • run_once без delegate_to. run_once выполнит задачу на ПЕРВОМ хосте текущего батча - какой именно это хост, зависит от order. Если нужна конкретная машина (например, мастер БД), фиксируй её через delegate_to.
Короткая пометка для не-RHEL: всё перечисленное - forks, serial, strategy, throttle, run_once, order - это механика самого Ansible, она не зависит от дистрибутива. Меняются только модули внутри задач: на Ubuntu/Debian вместо ansible.builtin.dnf будет ansible.builtin.apt, на RED OS и Astra Linux - снова dnf или apt соответственно. Сама логика выкаток везде одна.

Мини-лаба

Подними 4-5 тестовых хостов (хоть контейнеры, хоть ВМ) в группе webservers и руками прощупай:
  • Запусти любой плей с -f 1 и с -f 5, сравни время. Почувствуй, что такое forks.
  • Поставь serial: 1 на плей с тремя задачами и посмотри в выводе, как Ansible полностью проходит весь плей на одном хосте, прежде чем взяться за второй.
  • Сделай serial: [1, "50%"] и убедись, что первый батч - один хост, а дальше половина от остатка.
  • Переключи strategy с linear на free на плее с разными по скорости задачами (добавь искусственный ansible.builtin.pause или command sleep на один хост) и посмотри, как ломается синхронный порядок.
  • Добавь задачу с throttle: 1 и run_once: true, проверь по выводу, что одна идёт строго по одному хосту за раз, а вторая - вообще единожды.
  • Поиграй с order: shuffle и order: sorted, запусти пару раз и сравни порядок хостов в выводе.
Контрольные вопросы
  • Чем forks отличается от serial - что из них про скорость, а что про безопасность выкатки, и на каком уровне (плей/задача) задаётся каждый?
  • Ты катишь обновление на 40 серверов и хочешь сначала проверить на одном, потом на пяти, потом по четверти. Как выглядит директива serial?
  • В чём практическая разница между strategy: linear и strategy: free, и в каком случае free тебя укусит?
  • Зачем нужны throttle и run_once, и почему run_once часто пишут вместе с delegate_to?
Итог

forks решает, сколько хостов Ansible трогает разом ради скорости. serial режет плей на батчи ради безопасных rolling-выкаток, а max_fail_percentage страхует от выката битого релиза на весь парк. strategy (linear, free, host_pinned) определяет, ждать ли хосты друг друга между задачами. throttle, run_once и order - точечные рычаги для особых случаев. Запомни главное правило прода: любой рестарт за балансировщиком - только через serial. Для RHCE держи в голове, что serial и strategy живут на уровне плея, а не задачи.

Источники по стратегиям и порядку: [Controlling playbook execution: strategies](https://docs.ansible.com/ansible/latest ... egies.html), [host_pinned strategy](https://docs.ansible.com/projects/ansib ... ategy.html), [Index of Strategy Plugins](https://docs.ansible.com/ansible/latest ... ategy.html).
👍3 ❤️5 🔥1 😄 🤔3
Аватара пользователя
Ansuz
Сообщения: 1
Зарегистрирован: 12 май 2026, 09:41

Re: Параллелизм и порядок выполнения: forks, serial, strategy

Сообщение Ansuz »

Вот это вовремя. На прошлой неделе ребутнул myapp разом на 12 нодах без serial, балансер ушёл в 502 на полминуты. Теперь понятно как надо было.
👍 ❤️ 🔥1 😄 🤔
Аватара пользователя
phamer
Сообщения: 1
Зарегистрирован: 26 май 2026, 00:32

Re: Параллелизм и порядок выполнения: forks, serial, strategy

Сообщение phamer »

А throttle и forks вместе как считаются? Если в плее forks=20, а на задаче throttle=5 - то именно эта задача пойдёт по 5, а остальные по 20? Правильно понял?
👍1 ❤️1 🔥1 😄 🤔
Ответить
← Предыдущая глава
Запуск плейбуков: проверка, теги, ограничения и отладка
Следующая глава →
Факты Ansible: сбор информации об узлах

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

Поделиться темой: ✈ Telegram VK

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

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

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