Делегирование, отказоустойчивые обновления и локальные действия
- Размер пакета отказоустойчивого обновления
- Максимальный процент сбоев
- Делегирование
- Делегированные факты
- Выполнение один раз
- Локальные плейбуки
- Прерывание выполнения при любой ошибке
Разработанный с самого начала для многоуровневых развертываний, Ansible отлично справляется с выполнением задач на одном хосте от имени другого или выполнением локальных шагов со ссылкой на некоторые удаленные хосты.
Это особенно применимо при настройке инфраструктуры непрерывной доставки или отказоустойчивых обновлений без простоев, когда вы можете взаимодействовать с балансировщиками нагрузки или системами мониторинга.
Дополнительные функции позволяют настраивать порядок завершения задач и назначать размер пакетного окна для обработки сразу нескольких машин во время отказоустойчивого обновления.
В этом разделе рассматриваются все эти функции. Примеры использования этих элементов см. в репозитории ansible-examples. Там есть довольно много примеров процедур обновления без простоев для разных типов приложений.
Также следует обратиться к разделу Работа с модулями, различные модули, такие как ‘ec2_elb’, ‘nagios’, ‘bigip_pool’ и ‘netscaler’, тесно связаны с концепциями, упомянутыми здесь.
Также необходимо ознакомиться с Ролями, так как концепции ‘pre_task’ и ‘post_task’ — это места, где обычно вызываются эти модули.
Обратите внимание, что некоторые задачи невозможно делегировать, например, include, add_host, debug, и т. д., так как они всегда выполняются на контроллере.
Размер пакета отказоустойчивого обновления
По умолчанию Ansible пытается управлять всеми машинами, указанными в плейбуке, параллельно. Для сценариев отказоустойчивого обновления вы можете определить, сколько хостов Ansible должен обрабатывать одновременно, используя ключевое слово serial.
- name: test play
hosts: webservers
serial: 2
gather_facts: False
tasks:
- name: task one
comand: hostname
- name: task two
command: hostname
В приведенном выше примере, если в группе ‘webservers’ было 4 хоста, то 2 хоста полностью завершат плейбуке до перехода к следующим 2 хостам:
PLAY [webservers] **************************************** TASK [task one] ****************************************** changed: [web2] changed: [web1] TASK [task two] ****************************************** changed: [web1] changed: [web2] PLAY [webservers] **************************************** TASK [task one] ****************************************** changed: [web3] changed: [web4] TASK [task two] ****************************************** changed: [web3] changed: [web4] PLAY RECAP *********************************************** web1 : ok=2 changed=2 unreachable=0 failed=0 web2 : ok=2 changed=2 unreachable=0 failed=0 web3 : ok=2 changed=2 unreachable=0 failed=0 web4 : ok=2 changed=2 unreachable=0 failed=0
Ключевое слово serial также может быть указано в виде процента, который будет применен к общему количеству хостов в плейбуке, чтобы определить количество хостов за проход:
- name: test play hosts: webservers serial: "30%"
Если количество хостов не делится на количество проходов без остатка, то последний проход будет содержать остаток.
Начиная с Ansible 2.2, размер пакета может быть указан в виде списка, как показано ниже:
- name: test play hosts: webservers serial: - 1 - 5 - 10
В приведенном выше примере первый пакет будет содержать один хост, следующий — 5 хостов, и (если останутся хосты), каждый последующий пакет будет содержать 10 хостов до тех пор, пока все доступные хосты не будут использованы.
Также возможно указать несколько размеров пакета в процентах:
- name: test play hosts: webservers serial: - "10%" - "20%" - "100%"
Вы также можете смешивать значения:
- name: test play hosts: webservers serial: - 1 - 5 - "20%"
Примечание
Независимо от того, насколько мал процент, количество хостов за проход всегда будет равно 1 или больше.
Максимальный процент сбоев
По умолчанию Ansible продолжает выполнение задач до тех пор, пока есть хосты в пакете, которые еще не потерпели неудачу. Размер пакета для плейбука определяется параметром serial. Если serial не задан, то размер пакета включает все хосты, указанные в поле hosts:. В некоторых ситуациях, таких как отказоустойчивые обновления, описанные выше, может быть желательно прервать плейбук при достижении определенного порога сбоев. Для этого можно установить максимальный процент сбоев для плейбука следующим образом:
- hosts: webservers max_fail_percentage: 30 serial: 10
В приведенном выше примере, если более 3 из 10 серверов в группе потерпят неудачу, остальная часть плейбука будет прервана.
Примечание
Установленный процент должен быть превышен, а не равен ему. Например, если serial было установлено на 4, и вы хотите, чтобы задача прервалась при отказе 2 систем, процент должен быть установлен на 49, а не на 50.
Делегирование
Это не совсем специфично для отказоустойчивого обновления, но часто используется в таких случаях.
Если вы хотите выполнить задачу на одном хосте со ссылкой на другие хосты, используйте ключевое слово ‘delegate_to’ в задаче. Это идеально подходит для размещения узлов в пуле балансировщиков нагрузки или их удаления. Это также очень полезно для управления окнами простоя. Имейте в виду, что делегировать все задачи не имеет смысла, отладка, добавление хостов, включение и т. д. всегда выполняются на контроллере. Использование этого ключевого слова вместе с ключевым словом ‘serial’ для управления количеством хостов, выполняющих задачи одновременно, также является хорошей идеей:
---
- hosts: webservers
serial: 5
tasks:
- name: take out of load balancer pool
command: /usr/bin/take_out_of_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1
- name: actual steps would go here
yum:
name: acme-web-stack
state: latest
- name: add back to load balancer pool
command: /usr/bin/add_back_to_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1
Эти команды будут выполняться на 127.0.0.1, который является машиной, на которой работает Ansible. Также есть сокращенная синтаксическая конструкция, которую вы можете использовать для каждой задачи: ‘local_action’. Вот тот же плейбук, что и выше, но с использованием сокращенной синтаксической конструкции для делегирования на 127.0.0.1:
---
# ...
tasks:
- name: take out of load balancer pool
local_action: command /usr/bin/take_out_of_pool {{ inventory_hostname }}
# ...
- name: add back to load balancer pool
local_action: command /usr/bin/add_back_to_pool {{ inventory_hostname }}
Распространенным шаблоном является использование локального действия для вызова ‘rsync’ для рекурсивного копирования файлов на управляемые серверы. Вот пример:
---
# ...
tasks:
- name: recursively copy files from management server to target
local_action: command rsync -a /path/to/files {{ inventory_hostname }}:/path/to/target/
Обратите внимание, что для работы необходимо иметь ключи SSH без пароля или настроенный ssh-agent, в противном случае rsync запросит пароль.
В случае необходимости указать больше аргументов, можно использовать следующий синтаксис:
---
# ...
tasks:
- name: Send summary mail
local_action:
module: mail
subject: "Summary Mail"
to: "{{ mail_recipient }}"
body: "{{ mail_body }}"
run_once: True
Переменная ansible_host (ansible_ssh_host в версиях 1.x или специфическая для плагинов ssh/paramiko) отражает хост, к которому делегирована задача.
Делегированные факты
По умолчанию любые факты, собранные делегированной задачей, назначаются хосту inventory_hostname (текущий хост), а не хосту, который фактически сгенерировал эти факты (хосту, которому делегирована задача). Директива delegate_facts может быть установлена в True, чтобы назначать собранные факты задачи делегированному хосту, а не текущему:
- hosts: app_servers
tasks:
- name: gather facts from db servers
setup:
delegate_to: "{{item}}"
delegate_facts: True
loop: "{{groups['dbservers']}}"
Вышеуказанный код соберет факты для машин в группе dbservers и назначит эти факты этим машинам, а не app_servers. Таким образом, вы можете обратиться к hostvars[‘dbhost1’][‘default_ipv4’][‘address’], даже если dbservers не были частью плейбука или были пропущены при использовании –limit.
Выполнение один раз
В некоторых случаях может потребоваться выполнить задачу только один раз для группы хостов. Это можно сделать, настроив “run_once” для задачи:
---
# ...
tasks:
# ...
- command: /opt/application/upgrade_db.py
run_once: true
# ...
Эта директива заставляет задачу выполнить попытку выполнения на первом хосте текущего пакета, а затем применяет все результаты и факты ко всем хостам в том же пакете.
Этот подход похож на применение условия к задаче, например:
- command: /opt/application/upgrade_db.py when: inventory_hostname == webservers[0]
Но результаты применяются ко всем хостам.
Как и большинство задач, это можно дополнительно использовать с “delegate_to” для указания отдельного хоста для выполнения:
- command: /opt/application/upgrade_db.py run_once: true delegate_to: web01.example.org
Как всегда при делегировании, действие будет выполнено на делегированном хосте, но информация по-прежнему будет относиться к исходному хосту в задаче.
Примечание
При использовании вместе с “serial”, задачи, помеченные как “run_once”, будут выполняться на одном хосте в каждом пакете. Если крайне важно, чтобы задача выполнялась только один раз независимо от режима “serial”, используйте конструкцию when: inventory_hostname == ansible_play_hosts[0].
Примечание
Любое условие (например, when:) будет использовать переменные «первого хоста», чтобы определить, запускается ли задача или нет, другие хосты не будут проверены.
Локальные плейбуки
Иногда полезно использовать плейбук локально, а не подключаясь по SSH. Это может быть полезно для проверки конфигурации системы, поместив плейбук в crontab. Это также может быть использовано для запуска плейбука внутри установщика ОС, такого как Anaconda kickstart.
Чтобы запустить весь плейбук локально, просто установите строку “hosts:” на “hosts: 127.0.0.1”, а затем запустите плейбук так:
ansible-playbook playbook.yml --connection=local
В качестве альтернативы, локальное соединение можно использовать в отдельном плейбуке, даже если другие плейбуки в плейбуке используют стандартный тип удаленного соединения:
- hosts: 127.0.0.1 connection: local
Примечание
Если вы устанавливаете соединение на локальное и не задан ansible_python_interpreter, модули будут запускаться в /usr/bin/python, а не в {{ ansible_playbook_python }}. Убедитесь, что вы установили ansible_python_interpreter: “{{ ansible_playbook_python }}” в host_vars/localhost.yml, например. Вы можете избежать этой проблемы, используя local_action или delegate_to: localhost.
Прерывание выполнения при любой ошибке
С помощью опции ‘’any_errors_fatal’’ любая ошибка на любом хосте в плейбуке с несколькими хостами будет рассматриваться как фатальная, и Ansible немедленно завершит работу, не дожидаясь других хостов.
Иногда выполнение ‘’serial’’ не подходит; количество хостов непредсказуемо (из-за динамичной инвентаризации) и скорость критически важна (требуется одновременное выполнение), но все задачи должны быть выполнены на 100%, чтобы продолжить выполнение плейбука.
Например, рассмотрим сервис, расположенный во многих дата-центрах с некоторыми балансировщиками нагрузки для передачи трафика от пользователей к сервису. Есть плейбук развертывания для обновления deb-пакетов сервиса. Плейбук содержит этапы:
- отключение трафика на балансировщиках нагрузки (должно быть выключено одновременно)
- безопасная остановка сервиса
- обновление программного обеспечения (этот этап включает тесты и запуск сервиса)
- включение трафика на балансировщиках нагрузки (должно быть включено одновременно)
Сервис не может быть остановлен при «работающих» балансировщиках нагрузки; они должны быть отключены в первую очередь. Из-за этого второй этап не может быть выполнен, если какой-либо сервер потерпел неудачу на первом этапе.
Для дата-центра «А» плейбук можно записать следующим образом:
---
- hosts: load_balancers_dc_a
any_errors_fatal: True
tasks:
- name: 'shutting down datacenter [ A ]'
command: /usr/bin/disable-dc
- hosts: frontends_dc_a
tasks:
- name: 'stopping service'
command: /usr/bin/stop-software
- name: 'updating software'
command: /usr/bin/upgrade-software
- hosts: load_balancers_dc_a
tasks:
- name: 'Starting datacenter [ A ]'
command: /usr/bin/enable-dc
В этом примере Ansible запустит обновление программного обеспечения на front-энд-системах только в том случае, если все балансировщики нагрузки будут успешно отключены.
См. также
- Работа с Playbook
- Введение в playbook
- Примеры Ansible на GitHub
- Множество примеров развертывания полных стеков
- Список рассылки пользователей
- Есть вопросы? Заходите в группу Google!
- irc.freenode.net
- Канал IRC-чата #ansible
© 2012–2018 Michael DeHaan
© 2018–2019 Red Hat, Inc.
Licensed under the GNU General Public License version 3.
https://docs.ansible.com/ansible/2.7/user_guide/playbooks_delegation.html