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

defaults против vars: где лежат переменные роли ansible
Это место, где спотыкаются почти все новички, и на экзамене за это тоже срезаются. Внешне defaults/main.yml и vars/main.yml одинаковые - оба задают переменные. Разница в приоритете.
ansible role defaults - это значения по умолчанию. Самый низкий приоритет во всей иерархии переменных. Их смысл: дать роли разумные настройки из коробки, которые пользователь роли спокойно переопределит - в инвентаре, в group_vars, в самом плейбуке при подключении роли. Сюда кладёшь всё, что хочешь дать менять снаружи: порт, имя пакета, корень сайта, версию.
vars/main.yml наоборот - переменные с высоким приоритетом. Их трудно перебить снаружи (только через -e в командной строке или include_vars). Сюда идёт то, что менять не положено: внутренние константы роли, маппинг "имя дистрибутива - имя пакета", служебные значения, от которых зависит логика самой роли.
Простое правило: если переменную предполагается настраивать снаружи - это defaults. Если это внутренняя кухня роли - это vars. Перепутаешь местами - и пользователь не сможет переопределить порт, потому что ты зашил его в vars с высоким приоритетом. Классические грабли.
Код: Выделить всё
# defaults/main.yml - пользователь это переопределит
web_package: httpd
web_service: httpd
web_port: 80
web_doc_root: /var/www/html
web_index_message: "Развёрнуто через Ansible"
Собираем рабочую роль целиком. Задачи в tasks/main.yml - установка пакета, доставка шаблона конфига, открытие порта в firewalld, запуск сервиса. Обрати внимание: имена пакета/сервиса/порта берём из переменных - так роль параметризуется и переиспользуется.
Код: Выделить всё
# tasks/main.yml
- name: Установить веб-сервер
ansible.builtin.dnf:
name: "{{ web_package }}"
state: present
- name: Развернуть главную страницу из шаблона
ansible.builtin.template:
src: index.html.j2
dest: "{{ web_doc_root }}/index.html"
owner: root
group: root
mode: "0644"
notify: Перезапустить веб-сервер
- name: Открыть HTTP в firewalld
ansible.posix.firewalld:
service: http
state: enabled
permanent: true
immediate: true
- name: Запустить и включить сервис
ansible.builtin.service:
name: "{{ web_service }}"
state: started
enabled: true
Код: Выделить всё
<html>
<head><title>{{ ansible_facts['hostname'] }}</title></head>
<body>
<h1>{{ web_index_message }}</h1>
<p>Хост: {{ ansible_facts['fqdn'] }}, порт {{ web_port }}</p>
</body>
</html>
Код: Выделить всё
# handlers/main.yml
- name: Перезапустить веб-сервер
ansible.builtin.service:
name: "{{ web_service }}"
state: restarted
Код: Выделить всё
# site.yml
- name: Поднять веб-серверы
hosts: web
become: true
roles:
- role: webserver
vars:
web_port: 8080
web_index_message: "Стенд QA"
tags: web
- role: webserver
tags: web
Код: Выделить всё
ansible-playbook site.yml --tags webКод: Выделить всё
ansible-playbook site.yml --skip-tags webНе забудь meta/main.yml - там хотя бы автор, лицензия и galaxy_info с platforms. Если роль зависит от другой роли, перечисли её в dependencies - Ansible прогонит зависимости перед твоей ролью.
На Ubuntu/Debian модуль dnf заменишь на ansible.builtin.apt, пакет будет apache2, сервис apache2, а вместо firewalld обычно ufw (community.general.ufw). В RED OS и Astra - тот же apt либо dnf по семейству, логика роли не меняется, отличается только маппинг имён, и его как раз держат в vars/main.yml через переменные по дистрибутиву.
Типичные грабли
- Порт зашит в vars вместо defaults - пользователь не может его переопределить. Настраиваемое - в defaults.
- notify не срабатывает: имя в notify не совпадает с name хендлера. Проверяй посимвольно, регистр важен.
- Хендлеры выполняются в конце плея, а не сразу после задачи. Если нужно прямо сейчас - meta: flush_handlers.
- template ищет .j2 в templates/, copy ищет файлы в files/. Полные пути писать не надо, иначе сломается переносимость.
- Роль не находится: лежит не в roles/ и не в roles_path. Проверь ansible.cfg.
- meta/main.yml с битым YAML или пустым dependencies валит запуск ещё до задач. Держи его валидным.
- Создай роль: . Разбери дерево руками.
Код: Выделить всё
ansible-galaxy role init roles/webserver - Заполни defaults/main.yml (package, service, port, doc_root, message), tasks, templates/index.html.j2, handlers.
- Напиши site.yml, подключи роль дважды - со своими vars и с дефолтами.
- Прогони , затем
Код: Выделить всё
ansible-playbook site.yml --syntax-check, затем боевой запуск.Код: Выделить всё
--check - Запусти второй раз и убедись, что задачи стали ok/green (идемпотентность), хендлер не дёрнулся.
- Поменяй web_index_message и проверь, что на этот раз хендлер перезапустил сервис.
- Погоняй и
Код: Выделить всё
--tags web.Код: Выделить всё
--skip-tags web
- Чем defaults/main.yml отличается от vars/main.yml по приоритету и для чего каждый предназначен?
- Какая команда создаёт скелет роли и какие каталоги получаются?
- Как передать роли параметры при подключении в плейбуке, чтобы перебить её значения по умолчанию?
- Почему хендлер может не сработать и где Ansible ищет файлы для template и copy?
Роль - это аккуратно упакованная автоматизация: скелет от ansible-galaxy role init, настраиваемые значения в defaults, внутренние константы в vars, задачи в tasks, перезапуски через handlers, переносимость через templates и files. Параметризуешь переменными - и одна роль ставит веб-сервер хоть на стенде, хоть в проде. На RHCE (EX294) собрать рабочую роль - типовое задание, и оценивают там ровно это: правильная структура, defaults для настраиваемого, идемпотентность и работающий хендлер.