Если задать pre_tasks в плейбуке, таски из него будут выполняться до всех остальных тасков (задач), включая роли. Если задать post_tasks в плейбуке, они будут выполняться после всего остального, включая все хендлеры, заданные в других тасках. Рассмотрим примеры полезного использования этих опций.
Можно использовать роли тремя способами:
- на уровне плея через опцию
roles
: это классический способ использования роли в плее. - на уровне тасков через опцию включения роли
include_role
: можно переиспользовать роли динамически в любом месте секции тасковtasks
плея. - на уровне тасков через опцию импорта роли
import_role
: можно переиспользовать роль статически в любом месте в секции тасковtasks
плея.
Стандартный способ включения ролей в плейбуке с помощью опции roles
:
---
- hosts: webservers
roles:
- common
- webservers
При использовании опции roles
на уровле плея, для каждой роли ‘x’:
- Если roles/x/tasks/main.yml существует, Ansible добавляет таски из этого файла в плей.
- Если roles/x/handlers/main.yml существует, Ansible добавляет хендлеры из этого файла в плей.
- Если roles/x/vars/main.yml существует, Ansible добавляет переменные из этого файла в плей.
- Если roles/x/defaults/main.yml существует, Ansible добавляет переменные из этого файла в плей.
- Если roles/x/meta/main.yml существует, Ansible добавляет все зависимости роли из этого файла в список ролей.
- Любые таски
copy
,script
,template
илиinclude
(в роли) могут ссылаться на файл роли из каталогов roles/x/{files,templates,tasks}/ (каталог зависит от таски) необходимости задания абсолютного или относительного пути.
При использовании ролей roles
на уровне плея в плейбуке, Ansible обрабатывает роли как статический импорт и вставляет их во время парсинга плейбука. Ansible выполняет каждый плей в следующем порядке:
- Все таски из
pre_tasks
. - Все хендлеры, которые триггерились тасками из pre_tasks.
- Каждая роль, перечисленная в
roles:
, выполняется в порядке перечисления сверху-вниз. Все зависимости ролей, заданные в файлеmeta/main.yml
роли выполняются первыми, учитывая теги и условия. Смотреть использование зависимостей ролей . - Все таски
tasks
, прописанные в плее. - Все хендлеры, которые триггерятся ролями или тасками из tasks.
- Все
post_tasks
, прописанные в плее. - Все хендлеры, которые триггерятся из post_tasks.
Управление хостами в балансировщике нагрузки
Мы всегда хотим минимизировать простои (перерывы) в продакшн, и многие архитекторы используют балансировку нагрузки для этого. Хорошая идея расположить системы бэкенда за балансировщиком и исключить конкретный бекенд из работы при выполнении работ, требующих перерывов, а по окончании работ вернуть его обратно в работу. Плейбук patch-webservers.yml выведет сервер из балансировщика HAProxy, выполнит обновления патчей и полную перезагрузку, а затем снова добавит сервер в балансировщик:
---
- hosts: webservers
pre_tasks:
- name: Выключаем web сервер в балансировщике
community.general.haproxy:
state: disabled
host: '{{ inventory_hostname_short }}'
fail_on_not_found: yes
delegate_to: loadbalancer.example.com
roles:
- full_patches
post_tasks:
- name: Включаем web сервер в балансировщике
community.general.haproxy:
state: enabled
host: '{{ inventory_hostname_short }}'
fail_on_not_found: yes
delegate_to: loadbalancer.example.com
Выполнение задач первичной настройки
Секция pre_tasks также полезна для задания фактов значениями, полученными во время прогона роли. Например, предположим, для некоторых из ролей нам надо получить последние доступные версии софта. Можно получить эту информация с сервера артифактов в секции pre_tasks и задать факт, который может быть доступен таскам нашей роли. В примере факт latest_app_version задан при вызове API endpoint. Т.к. вызов API выполняется только если latest_app_version не задан, он все еще позволяет пользователю перезаписать этот факт, который затем становится доступным всем ролям плейбука:
---
- hosts: webservers
pre_tasks:
- name: Получить последнюю версию софта с сервера артифактов
ansible.builtin.uri:
url: http://artifact-server.example.com:8080/software/latest
return_content: yes
delegate_to: localhost
register: uri_output
when: latest_app_version is not defined
- name: Задать факт latest_software_version
ansible.builtin.set_fact:
latest_app_version: "{{ uri_output.json.version }}"
when: latest_app_version is not defined
- name: Отобразить последнюю версию latest_app_version
ansible.builtin.debug:
msg: "{{ latest_app_version }}"
roles:
- appserver
- apache
- monitored_host
Временное отключение системы от мониторинга
Пример похож на пример с балансировщиком нагрузки. Перед началом работ надо исключить хост из мониторинга, чтобы не создавать ложных алармов. По окончании работ хост включается в мониторинг. Если система мониторинга поддерживает HTTP запросы, можно создать такой плейбук:
---
- hosts: database_servers
pre_tasks:
- name: Выключение хоста из мониторинга
ansible.builtin.uri:
url: "http://monitoring-server.example.com:8080/hosts/{{ inventory_hostname }}/schedule_downtime"
method: POST
body_format: json
body:
downtime_duration: 30m
delegate_to: localhost
roles:
- full_patches
post_tasks:
- name: Включение хоста в мониторинг
ansible.builtin.uri:
url: "http://monitoring-server.example.com:8080/hosts/{{ inventory_hostname }}/clear_downtime"
method: POST
body_format: json
delegate_to: localhost
Использование включений (includes)
В предыдущих примерах таски прописаня прямо в плейбуке. Для одной-двух тасков это удобно, но при использовании большого количества тасков и тем более если эти таски используются в разных плейбуках, лучше сделать их переиспользуемыми, для чего используем include . В примере задачи (таски) мониторинга расположены в их собственном каталоге tasks/
. Это делает код чище и позволяет переиспользовать код:
---
- hosts: database_servers
pre_tasks:
- name: Исключение хоста их мониторинга
ansible.builtin.include: tasks/monitoring/silence-host.yml
roles:
- full_patches
post_tasks:
- name: Включение хоста в мониторинг
ansible.builtin.include: tasks/monitoring/enable-host.yml
Note that this tasks/
directory is in the same directory as my main playbook and not the tasks/
directory under any particular role. To make this point clearer, the directory structure looks like this:
$ tree --noreport
├── ansible.cfg
├── inventory.ini
├── patch-databases-with-include.yml
├── patch-databases.yml
├── patch-webservers.yml
├── roles
│ ├── apache
│ │ └── tasks
│ │ └── main.yml
│ ├── appserver
│ │ └── tasks
│ │ └── main.yml
│ ├── full_patches
│ │ └── tasks
│ │ └── main.yml
│ └── monitored_host
│ └── tasks
│ └── main.yml
├── software-version.yml
├── tasks
│ └── monitoring
│ ├── enable-host.yml
│ └── silence-host.yml
└── templates
└── hosts.j2