Обработка ошибок в плейбуках
Когда Ansible получает ненулевой код возврата от команды или сообщение об ошибке от модуля, по умолчанию он прекращает выполнение на этом хосте и продолжает работу на других хостах. Однако в некоторых случаях может потребоваться другое поведение. Иногда ненулевой код возврата означает успешное выполнение. Иногда нужно, чтобы ошибка на одном хосте останавливала выполнение на всех хостах. Ansible предоставляет инструменты и настройки для обработки таких ситуаций и помогает получить желаемое поведение, вывод и отчётность.
- Игнорирование ошибок команд
- Игнорирование ошибок недоступных хостов
- Повторная активация недоступных хостов
- Обработчики и ошибки
- Определение ошибки
- Определение состояния «изменено»
- Обеспечение успешного выполнения команд и shell
- Управление ошибками в блоках
Игнорирование ошибок команд
По умолчанию Ansible прекращает выполнение задач на хосте, если задача на этом хосте завершается с ошибкой. Чтобы продолжить выполнение несмотря на ошибку, можно использовать ignore_errors.
- name: Do not count this as a failure ansible.builtin.command: /bin/false ignore_errors: true
Директива ignore_errors работает только в том случае, если задача может быть запущена и возвращает значение «failed». Она не заставляет Ansible игнорировать ошибки неопределённых переменных, сбои подключения, проблемы выполнения (например, отсутствие пакетов) или синтаксические ошибки.
Игнорирование ошибок недоступных хостов
Добавлено в версии 2.7.
Можно игнорировать ошибку задачи, вызванную тем, что экземпляр хоста недоступен («UNREACHABLE»), с помощью ключевого слова ignore_unreachable. Ansible игнорирует ошибки задачи, но продолжает выполнять последующие задачи на недоступном хосте. Например, на уровне задачи:
- name: This executes, fails, and the failure is ignored ansible.builtin.command: /bin/true ignore_unreachable: true - name: This executes, fails, and ends the play for this host ansible.builtin.command: /bin/true
А также на уровне плейбука:
- hosts: all
ignore_unreachable: true
tasks:
- name: This executes, fails, and the failure is ignored
ansible.builtin.command: /bin/true
- name: This executes, fails, and ends the play for this host
ansible.builtin.command: /bin/true
ignore_unreachable: false
Повторная активация недоступных хостов
Если Ansible не может подключиться к хосту, он помечает его как «UNREACHABLE» и удаляет из списка активных хостов на время выполнения. С помощью meta: clear_host_errors можно повторно активировать все хосты, чтобы последующие задачи снова попытались подключиться к ним.
Обработчики и ошибки
Ansible запускает обработчики в конце каждого плейбука. Если задача вызывает обработчик, но позднее в плейбуке другая задача завершается с ошибкой, по умолчанию обработчик на этом хосте не запускается, из-за чего хост может остаться в неожиданном состоянии. Например, задача может обновить файл конфигурации и вызвать обработчик для перезапуска службы. Если более поздняя задача в том же плейбуке завершится с ошибкой, файл конфигурации может измениться, но служба не будет перезапущена.
Это поведение можно изменить с помощью параметра командной строки --force-handlers, включив force_handlers: True в плейбук или добавив force_handlers = True в ansible.cfg. Если обработчики принудительно запускаются, Ansible выполнит все вызванные обработчики на всех хостах, даже на тех, где задачи завершились с ошибкой. (Обратите внимание, что некоторые ошибки всё же могут помешать запуску обработчика, например, если хост станет недоступен.)
Определение ошибки
Ansible позволяет определить, что означает «ошибка» для каждой задачи, с помощью условия failed_when. Как и во всех условиях Ansible, списки из нескольких условий failed_when объединяются неявным оператором and, то есть задача считается ошибочной, только когда выполняются все условия. Если нужно вызвать ошибку при выполнении любого из условий, необходимо задать условия в одной строке с явным оператором or.
Например, чтобы вызвать ошибку, если истинно одно из двух условий:
- name: Fail task when either condition is met ansible.builtin.command: /usr/bin/example-command register: command_result failed_when: command_result.rc != 0 or 'ERROR' in command_result.stdout
Можно проверить наличие слова или фразы в выводе команды
- name: Fail task when the command error output prints FAILED ansible.builtin.command: /usr/bin/example-command -x -y -z register: command_result failed_when: "'FAILED' in command_result.stderr"
или ориентироваться на код возврата
- name: Fail task when both files are identical ansible.builtin.raw: diff foo/file1 bar/file2 register: diff_cmd failed_when: diff_cmd.rc == 0 or diff_cmd.rc >= 2
Для определения ошибки можно также объединить несколько условий. Эта задача завершится с ошибкой, если истинны оба условия:
- name: Check if a file exists in temp and fail task if it does
ansible.builtin.command: ls /tmp/this_should_not_be_here
register: result
failed_when:
- result.rc == 0
- '"No such" not in result.stderr'
Если задача должна завершаться с ошибкой, когда выполнено только одно условие, измените определение failed_when на
failed_when: result.rc == 0 or "No such" not in result.stderr
Если условий слишком много, чтобы уместить их в одной строке, можно разбить их на несколько строк в значении YAML с помощью >.
- name: example of many failed_when conditions with OR
ansible.builtin.shell: "./myBinary"
register: ret
failed_when: >
("No such file or directory" in ret.stdout) or
(ret.stderr != '') or
(ret.rc == 10)
Определение состояния «изменено»
Ansible позволяет определить, когда конкретная задача «изменяет» удалённый узел, с помощью условия changed_when. Так, по коду возврата или выводу можно определить, следует ли сообщать об изменении в статистике Ansible и запускать ли обработчик. Как и во всех условиях Ansible, списки из нескольких условий changed_when объединяются неявным оператором and, то есть об изменении сообщается, только когда выполняются все условия. Если нужно сообщать об изменении при выполнении любого из условий, задайте условия в строке с явным оператором or. Например:
tasks:
- name: Report 'changed' when the return code is not equal to 2
ansible.builtin.shell: /usr/bin/billybass --mode="take me to the river"
register: bass_result
changed_when: "bass_result.rc != 2"
- name: This will never report 'changed' status
ansible.builtin.shell: wall 'beep'
changed_when: False
- name: This task will always report 'changed' status
ansible.builtin.command: /path/to/command
changed_when: True
Для переопределения результата «изменено» можно также объединить несколько условий.
- name: Combine multiple conditions to override 'changed' result
ansible.builtin.command: /bin/fake_command
register: result
ignore_errors: True
changed_when:
- '"ERROR" in result.stderr'
- result.rc == 2
В условиях можно ссылаться на простые переменные, чтобы избежать повторения определённых выражений, как в следующем примере:
- name: Example playbook
hosts: myHosts
vars:
log_path: /home/ansible/logfolder/
log_file: log.log
tasks:
- name: Create empty log file
ansible.builtin.shell: mkdir {{ log_path }} || touch {{ log_path }}{{ log_file }}
register: tmp
changed_when:
- tmp.rc == 0
- 'tmp.stderr != "mkdir: cannot create directory ‘" ~ log_path ~ "’: File exists"'
Примечание
Обратите внимание: вокруг переменной log_path в выражении changed_when отсутствуют двойные фигурные скобки {{ }}.
Как и when, эти два условия не требуют разделителей шаблонов ({{ }}), поскольку являются необработанными выражениями Jinja2.
Если вы всё же используете их, Ansible выдаст предупреждение о том, что условные выражения не должны содержать разделители шаблонов Jinja2.
Дополнительные примеры синтаксиса условий см. в разделе Определение ошибки.
Обеспечение успешного выполнения command и shell
Модули command и shell учитывают коды возврата. Поэтому, если у вас есть команда, для которой успешный код завершения не равен нулю, можно сделать следующее:
tasks:
- name: Run this command and ignore the result
ansible.builtin.shell: /usr/bin/somecommand || /bin/true
Прерывание выполнения плейбука на всех хостах
Иногда требуется, чтобы ошибка на одном хосте или ошибки на определённом проценте хостов прерывали весь плейбук на всех хостах. Выполнение плейбука можно остановить после первой ошибки с помощью any_errors_fatal. Для более точного управления можно использовать max_fail_percentage, чтобы прервать выполнение после того, как ошибки возникнут на заданном проценте хостов.
Прерывание при первой ошибке: any_errors_fatal
Если задать any_errors_fatal и задача завершится с ошибкой, Ansible завершит выполнение задачи, вызвавшей ошибку, на всех хостах текущей группы, а затем остановит выполнение плейбука на всех хостах. Последующие задачи и плейбуки выполняться не будут. Восстановиться после критических ошибок можно, добавив в блок раздел rescue. Значение any_errors_fatal можно задать на уровне плейбука или блока.
- hosts: somehosts
any_errors_fatal: true
roles:
- myrole
- hosts: somehosts
tasks:
- block:
- include_tasks: mytasks.yml
any_errors_fatal: true
Эту возможность можно использовать, когда для продолжения выполнения плейбука все задачи должны завершиться успешно. Например, если вы запускаете службу на машинах в нескольких центрах обработки данных, а балансировщики нагрузки направляют пользовательский трафик к службе, перед остановкой службы для обслуживания нужно отключить все балансировщики нагрузки. Чтобы любая ошибка в задаче отключения балансировщиков нагрузки останавливала все остальные задачи:
---
- hosts: load_balancers_dc_a
any_errors_fatal: true
tasks:
- name: Shut down datacenter 'A'
ansible.builtin.command: /usr/bin/disable-dc
- hosts: frontends_dc_a
tasks:
- name: Stop service
ansible.builtin.command: /usr/bin/stop-software
- name: Update software
ansible.builtin.command: /usr/bin/upgrade-software
- hosts: load_balancers_dc_a
tasks:
- name: Start datacenter 'A'
ansible.builtin.command: /usr/bin/enable-dc
В этом примере Ansible начинает обновление программного обеспечения на фронтендах только в том случае, если все балансировщики нагрузки успешно отключены.
Установка максимального процента ошибок
По умолчанию Ansible продолжает выполнять задачи, пока остаются хосты, на которых ещё не произошла ошибка. В некоторых ситуациях, например при последовательном обновлении, может потребоваться прервать выполнение плейбука при достижении определённого порога ошибок. Для этого можно задать максимальный процент ошибок на уровне плейбука:
--- - hosts: webservers max_fail_percentage: 30 serial: 10
Настройка max_fail_percentage применяется к каждой группе, если используется вместе с параметром serial. В приведённом выше примере, если в первой (или любой другой) группе более 3 из 10 серверов завершат работу с ошибкой, оставшаяся часть плейбука будет прервана.
Примечание
Указанный процент должен быть превышен, а не достигнут. Например, если для serial задано значение 4 и нужно прервать выполнение плейбука при сбое 2 систем, установите max_fail_percentage равным 49, а не 50.
Управление ошибками в блоках
Для определения реакции на ошибки задач можно также использовать блоки. Этот подход похож на обработку исключений во многих языках программирования. Подробности и примеры см. в разделе Обработка ошибок с помощью блоков.
См. также
- Плейбуки Ansible
-
Введение в плейбуки
- Общие советы
-
Советы и рекомендации по работе с плейбуками
- Условные выражения
-
Условные операторы в плейбуках
- Использование переменных
-
Всё о переменных
- Общение
-
Есть вопросы? Нужна помощь? Хотите поделиться идеями? Посетите руководство по общению сообщества Ansible
© 2012–2018 Michael DeHaan
© 2018–2025 Red Hat, Inc.
Licensed under the GNU General Public License version 3.
https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_error_handling.html