Структура плейбука: play, task, модули и переменные

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

Структура плейбука: play, task, модули и переменные

Сообщение 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
Ты уже запускал отдельные модули через ad-hoc команды (ansible -m). Это удобно для разведки, но как только задача "поставить пакет, положить конфиг, открыть порт, перезапустить сервис" растягивается на десяток шагов, копипастить команды в терминал становится больно. Тут и появляется ansible playbook - один YAML-файл, который описывает желаемое состояние системы целиком и применяет его по порядку, идемпотентно, на сотне хостов сразу.

На экзамене EX294 это ядро. Тебе не дадут писать ad-hoc - тебе дадут задание вроде "настрой веб-сервер на группе webservers", и ты обязан написать многозадачный плейбук, который отработает с первого прогона и не сломается со второго. Поэтому в этом уроке разбираем структуру плейбука по косточкам: из чего состоит play, из чего task, как туда подставляются переменные и что означают цветные строчки ok/changed в выводе.

Из чего собрана структура плейбука: play и task

Плейбук - это список (YAML-массив) из одного или нескольких play. Каждый play - это связка "к каким хостам применяем" плюс "что делаем". Внутри play живут задачи (tasks), а каждая задача вызывает ровно один модуль.

Иерархия простая:
  • playbook - файл целиком, список play
  • play - элемент списка, начинается с дефиса, привязан к группе хостов
  • task - один шаг внутри play, дёргает один модуль
  • module - готовый кирпич (copy, dnf, service...), который и меняет систему
Минимальный, но честный пример:

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

---
- name: Базовая настройка веб-сервера
  hosts: webservers
  become: true
  gather_facts: true
  vars:
    http_port: 80
    web_pkg: httpd

  tasks:
    - name: Установить пакет {{ web_pkg }}
      ansible.builtin.dnf:
        name: "{{ web_pkg }}"
        state: present

    - name: Положить главную страницу
      ansible.builtin.copy:
        content: "Привет с {{ inventory_hostname }}\n"
        dest: /var/www/html/index.html
        owner: root
        group: root
        mode: "0644"

    - name: Запустить и включить httpd в автозагрузку
      ansible.builtin.service:
        name: httpd
        state: started
        enabled: true

    - name: Открыть HTTP в firewalld
      ansible.posix.firewalld:
        service: http
        state: enabled
        permanent: true
        immediate: true
Три верхних строки тире-блока (name, hosts, become...) - это директивы уровня play. Разберём их.

hosts - обязательная директива, отвечает на вопрос "ansible playbook host - где это всё выполнять". Сюда идёт имя группы из инвентаря (webservers), отдельный хост, all, или выражение типа webservers:&production. Если в инвентаре нет такой группы - play просто отработает вхолостую, ни на ком.

become - повышение привилегий (по умолчанию sudo до root). На RHEL/Fedora почти всё системное требует become: true. На Ubuntu/Debian механизм тот же, меняются разве что пакеты (apt вместо dnf) и имена сервисов (apache2 вместо httpd).

gather_facts - собирать ли факты о хосте перед задачами (это переменные вроде ansible_facts['os_family'], ansible_default_ipv4). По умолчанию true. Если факты не нужны, ставь false - play стартует заметно быстрее.

vars и vars_files - переменные прямо в play или вынесенные в отдельный файл. Это то, что в Яндексе ищут как "ansible playbook vars". Пример с вынесением:

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

- name: Настройка с внешними переменными
  hosts: dbservers
  become: true
  vars_files:
    - vars/db_secrets.yml
  vars:
    db_port: 5432
Изображение

ansible task: атрибуты задачи, которые надо знать наизусть

Любой ansible task - это словарь. Один ключ - имя модуля и его параметры, остальные ключи - атрибуты задачи, которые управляют тем, как и когда модуль выполнится.

name - человекочитаемое описание. Технически необязательно, но пиши его всегда. Без name в выводе ты увидишь сырой вызов модуля, а с тегами и --start-at-task без имён вообще не поработаешь. Хорошее имя - глагол + объект: "Установить nginx", "Открыть порт 443", а не "task1" или "nginx". В name можно вставлять {{ переменные }}, как в примере выше.

loop - повторить задачу по списку. Текущий элемент доступен как item:

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

- name: Поставить набор пакетов
  ansible.builtin.dnf:
    name: "{{ item }}"
    state: present
  loop:
    - vim
    - git
    - tmux
Замечу: для dnf можно просто передать список в name - модуль сам поставит всё одной транзакцией, это быстрее. loop нагляднее показать на firewalld или user.

when - условие. Задача выполнится, только если выражение истинно. Внутри when имена переменных пишутся без {{ }}:

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

- name: Поставить firewalld только на RHEL-семействе
  ansible.builtin.dnf:
    name: firewalld
    state: present
  when: ansible_facts['os_family'] == "RedHat"
register - сохранить результат задачи в переменную, чтобы использовать дальше (например, в when следующей задачи или в debug):

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

- name: Проверить статус сервиса
  ansible.builtin.command: systemctl is-active httpd
  register: httpd_status
  changed_when: false

- name: Показать вывод
  ansible.builtin.debug:
    var: httpd_status.stdout
notify - дёрнуть обработчик (handler), но только если задача реально что-то изменила (статус changed). Классика - перезапуск сервиса после правки конфига:

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

  tasks:
    - name: Залить конфиг nginx
      ansible.builtin.template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify: restart nginx

  handlers:
    - name: restart nginx
      ansible.builtin.service:
        name: nginx
        state: restarted
Важная механика: handlers запускаются НЕ сразу, а в конце play, и в том порядке, в каком они описаны в секции handlers (а не в порядке notify). Если конфиг не менялся - handler не сработает, лишнего рестарта не будет. Если несколько задач уведомили один и тот же handler - он выполнится один раз.

tags - метки для выборочного запуска. ansible-playbook site.yml --tags web прогонит только помеченные задачи, --skip-tags пропустит их.

Переменные через Jinja2 и идемпотентность задач

Везде, где нужно подставить значение переменной, используется синтаксис Jinja2 - двойные фигурные скобки {{ var }}. Это работает в параметрах модулей, в name, в content. Отдельное правило про кавычки: если значение параметра НАЧИНАЕТСЯ с {{, весь параметр надо взять в кавычки, иначе YAML решит, что это начало словаря, и упадёт с ошибкой. Поэтому пишут name: "{{ web_pkg }}", а не name: {{ web_pkg }}.

Теперь про порядок выполнения - он строго сверху вниз. play за play, внутри play задача за задачей. Ansible выполняет первую задачу на ВСЕХ хостах группы, потом переходит ко второй, и так далее. Это важно: не "весь плейбук на хосте 1, потом весь на хосте 2", а наоборот - построчно по всему парку.

Несколько play в одном файле - это нормально и часто нужно, когда разные группы хостов настраиваются по-разному:

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

---
- name: Настройка веб-серверов
  hosts: webservers
  become: true
  tasks:
    - name: Поставить httpd
      ansible.builtin.dnf:
        name: httpd
        state: present

- name: Настройка баз данных
  hosts: dbservers
  become: true
  tasks:
    - name: Поставить mariadb-server
      ansible.builtin.dnf:
        name: mariadb-server
        state: present
Первый play целиком отработает на webservers, потом второй - на dbservers. Внутри одного запуска ansible-playbook.

И главное понятие всего Ansible - идемпотентность. Хорошая задача описывает СОСТОЯНИЕ ("пакет установлен", "строка есть в файле"), а не действие ("выполни команду"). Поэтому повторный прогон ничего не делает, если система уже в нужном виде. В выводе ты увидишь статусы:
  • ok (зелёный) - проверено, уже как надо, менять нечего
  • changed (жёлтый) - состояние было изменено сейчас
  • failed (красный) - задача упала, по умолчанию хост выбывает из дальнейших задач
  • skipped (голубой) - пропущено по when
Признак правильного плейбука: первый запуск даёт changed, ВТОРОЙ запуск даёт только ok, без changed. Если задача упорно показывает changed на каждом прогоне - почти всегда виноваты модули command/shell, которые сами не умеют в идемпотентность. Их либо заменяют нормальным модулем, либо прикручивают changed_when и creates.

Типичные грабли
  • Параметр начинается с {{ без кавычек - YAML-ошибка про "could not find expected ':'". Лечится кавычками.
  • В when написали {{ var }} вместо var. when - это уже выражение Jinja2, фигурные скобки лишние.
  • Отступы пробелами, причём строго. Один таб - и плейбук не парсится. Настрой редактор на пробелы.
  • Забыл become: true - получаешь Permission denied на системных задачах.
  • command/shell вечно changed. Добавь changed_when: false для чисто проверочных команд или creates для тех, что создают файл.
  • notify ссылается на handler, имя которого не совпадает буква в букву с name обработчика - тихо ничего не произойдёт.
Мини-лаба

Руками, на тестовой RHEL/Fedora или RED OS:
  • Напиши плейбук web.yml с одним play на группу webservers: установить httpd, положить index.html через ansible.builtin.copy, запустить и enabled сервис, открыть http в firewalld.
  • Запусти дважды. Убедись, что второй прогон - сплошь ok, ни одного changed. Если где-то changed - разберись почему.
  • Добавь второй play на группу dbservers (поставить mariadb-server) в тот же файл. Прогони, посмотри порядок в выводе.
  • Вынеси http_port и имя пакета в секцию vars, подставь через {{ }} в name и параметры.
  • Добавь задачу с template + notify на handler, перезапускающий сервис. Поменяй шаблон, прогони - поймай changed и срабатывание handler в конце.
  • Навесь tags на веб-задачи и запусти с --tags.
Контрольные вопросы
  • Чем play отличается от task, и сколько модулей вызывает одна задача?
  • В каком порядке Ansible выполняет задачи, если в инвентаре пять хостов: построчно по всем хостам или по очереди весь плейбук на каждом?
  • Почему правильный плейбук на втором запуске не должен давать changed, и какие модули чаще всего это правило ломают?
  • Когда и в каком порядке запускаются handlers, и при каком условии задача их вообще уведомляет?
Итог

Плейбук = список play, play = "где" (hosts) + директивы (become, vars, gather_facts) + список task, task = один модуль плюс атрибуты (name, loop, when, register, notify, tags). Переменные подставляются через {{ }}, выполнение идёт строго сверху вниз и построчно по хостам, а мерило качества - идемпотентность: changed на первом прогоне, чистый ok на втором. Освоишь эту структуру руками - и многозадачные плейбуки EX294 перестанут быть проблемой.
👍 ❤️4 🔥1 😄 🤔
Аватара пользователя
deepstack
Сообщения: 1
Зарегистрирован: 19 май 2026, 05:07

Re: Структура плейбука: play, task, модули и переменные

Сообщение deepstack »

Дошло наконец, почему мой плейбук каждый раз показывал changed - там был shell без changed_when. Добавил creates и всё, теперь чистый ok на повторе. Спасибо за раздел про идемпотентность.
👍 ❤️1 🔥 😄 🤔
Аватара пользователя
regexnerd
Сообщения: 1
Зарегистрирован: 11 май 2026, 06:26

Re: Структура плейбука: play, task, модули и переменные

Сообщение regexnerd »

А handlers реально один раз срабатывают, даже если три задачи notify дёрнули? Думал на каждое уведомление будет рестарт, перепроверю на лабе.
👍1 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Ansible playbook: что это такое и как устроен
Следующая глава →
Запуск плейбуков: проверка, теги, ограничения и отладка

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

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

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

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

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