Что такое роли Ansible и зачем они нужны
Роль (role) - это способ разложить плейбук по полочкам. Вместо одного гигантского файла ты получаешь стандартный набор каталогов, где у каждого типа сущностей свое место: задачи отдельно, хендлеры отдельно, шаблоны отдельно. Ansible сам знает, где что искать, поэтому тебе не нужно прописывать пути руками.
Главных причин использовать роли две. Первая - переиспользование: настроил один раз роль nginx, и подключаешь ее в десятке проектов одной строкой. Вторая - порядок и читаемость: когда ansible roles разбиты по структуре, в чужом (и своем полугодовалом) коде разбираешься за минуты, а не за день. На экзамене EX294 роли - это крупный блок, и спрашивают там именно умение создать роль с нуля и грамотно ее применить, а не пересказ теории.
Создать каркас роли проще всего командой:
Код: Выделить всё
ansible-galaxy role init webserver
Код: Выделить всё
roles/
└── webserver/
├── tasks/
│ └── main.yml # точка входа: основные задачи
├── handlers/
│ └── main.yml # обработчики (notify их дергает)
├── templates/
│ └── nginx.conf.j2 # Jinja2-шаблоны для template
├── files/
│ └── index.html # статические файлы для copy
├── vars/
│ └── main.yml # переменные с высоким приоритетом
├── defaults/
│ └── main.yml # переменные по умолчанию (низкий приоритет)
├── meta/
│ └── main.yml # метаданные и зависимости роли
└── README.md
Разница между defaults/ и vars/ важна на практике. В defaults кладут значения, которые пользователь роли спокойно переопределит (порт, имя сайта) - у них самый низкий приоритет. В vars держат то, что менять не стоит, у них приоритет высокий. Если хочешь, чтобы роль легко настраивалась снаружи, выноси параметры в defaults.

Ansible Galaxy: где брать готовые роли
Не все нужно писать самому. Ansible Galaxy - это публичный репозиторий ролей и коллекций, своего рода npm для Ansible. Тысячи готовых ролей: настройка PostgreSQL, Docker, Nginx, мониторинга. Прежде чем писать роль docker руками, загляни туда - наверняка уже есть отлаженный вариант.
Установка одной роли делается так:
Код: Выделить всё
ansible-galaxy role install geerlingguy.nginx
ansible-galaxy role list
Но в реальных проектах роли не ставят по одной руками. Описывают их списком в файле requirements.yml и кладут его в репозиторий проекта. Тогда любой коллега одной командой развернет тот же набор зависимостей:
Код: Выделить всё
# requirements.yml
roles:
- name: geerlingguy.nginx
version: "3.1.4"
- name: my_app
src: https://github.com/myorg/ansible-role-app.git
scm: git
version: main
collections:
- name: ansible.posix
- name: community.general
Код: Выделить всё
ansible-galaxy install -r requirements.yml -p roles/
Подключение роли: roles vs include_role и порядок выполнения
Способ первый, классический - секция roles: в плее. Это статическое подключение: Ansible читает роли на этапе разбора плейбука, до старта выполнения.
Код: Выделить всё
- name: Настройка веб-сервера
hosts: web
become: true
roles:
- webserver
- geerlingguy.nginx
Код: Выделить всё
tasks:
- name: Базовая роль всегда
ansible.builtin.import_role:
name: common
- name: А эту - только для прода
ansible.builtin.include_role:
name: monitoring
when: env == "prod"
Отдельно разберем порядок выполнения - это любят на экзамене. Внутри одного плея этапы идут строго так: сначала pre_tasks, затем хендлеры из pre_tasks, потом roles (роли из секции roles:), за ними обычные tasks, и в конце post_tasks. Хендлеры запускаются после каждого блока, если их кто-то notify-нул.
Код: Выделить всё
- hosts: web
become: true
pre_tasks:
- name: Обновить кеш пакетов
ansible.builtin.dnf:
update_cache: true
roles:
- webserver
tasks:
- name: Открыть порт в firewalld
ansible.posix.firewalld:
service: http
permanent: true
state: enabled
immediate: true
post_tasks:
- name: Дернуть healthcheck
ansible.builtin.uri:
url: http://localhost/health
status_code: 200
Небольшая ремарка про дистрибутивы. Все примеры заточены под RHEL/Fedora: dnf, systemd, firewalld, SELinux. Готовые роли из Galaxy обычно умеют определять ОС и сами выбирают между dnf и apt - например geerlingguy.nginx работает и на RHEL, и на Ubuntu/Debian. На RU-системах (RED OS, Astra) роли тоже едут, но загляни в их defaults/main.yml: иногда имя пакета или сервиса отличается, и его надо переопределить.
Типичные грабли
- Забыл version в requirements.yml - и однажды роль обновилась, переменные переименовались, плейбук упал. Пинуй версии всегда.
- Положил переменную в vars/ вместо defaults/ и удивляешься, почему ее не получается переопределить снаружи. Высокий приоритет vars перебивает почти все.
- Ждешь, что include_role с when выполнится статически, а он динамический - и тег на внутренние задачи не сработал. Для тегов нужен import_role.
- Путаешь приоритет блоков: думал, что tasks идут перед ролями. Нет, roles раньше tasks. Из-за этого порт в firewalld открывался до того, как роль успевала поставить сервис.
- Поставил роль без -p roles/, она легла в ~/.ansible/roles/, а коллега ее не видит, потому что у него этого каталога нет. Держи зависимости в проекте.
- Создай роль командой ansible-galaxy role init webserver и изучи дерево каталогов.
- В tasks/main.yml роли поставь nginx через dnf, развесь конфиг шаблоном template из templates/, добавь хендлер перезапуска сервиса.
- Вынеси порт и имя сайта в defaults/main.yml, подключи роль через секцию roles: и переопредели порт в плейбуке.
- Опиши в requirements.yml роль geerlingguy.nginx с пином версии и установи ее командой ansible-galaxy install -r requirements.yml -p roles/.
- Добавь в плей pre_tasks (обновление кеша), post_tasks (проверка через uri) и убедись по выводу, что порядок именно pre - roles - tasks - post.
- Чем отличается каталог defaults/ от vars/ внутри роли и где хранить переопределяемые параметры?
- В чем разница между import_role и include_role, и когда обязателен именно динамический include?
- В каком порядке выполняются pre_tasks, roles, tasks и post_tasks в одном плее?
- Зачем нужен requirements.yml и что делает флаг -p в команде ansible-galaxy install?
Роли превращают хаос из одного длинного плейбука в переиспользуемые блоки со стандартной структурой каталогов. Готовое тащи из Ansible Galaxy через requirements.yml с пином версий, подключай через roles: или import_role для предсказуемости, а include_role доставай, когда нужны цикл или условие. И держи в голове порядок pre_tasks - roles - tasks - post_tasks: на EX294 этот вопрос всплывает регулярно, а в реальной отладке экономит часы.