Ответ
В Ansible я управляю ошибками через комбинацию директив ignore_errors, failed_when, changed_when и блоков rescue. Это позволяет писать идемпотентные и отказоустойчивые плейбуки.
1. Игнорирование ожидаемых ошибок (ignore_errors):
Использую, когда неудача задачи не должна останавливать выполнение всего плейбука.
- name: Остановить сервис, если он запущен
ansible.builtin.systemd:
name: my-old-service
state: stopped
ignore_errors: yes # Не падать, если сервис уже остановлен или не существует
register: stop_result
2. Кастомные условия неудачи (failed_when):
Позволяет определить, что считать ошибкой, на основе вывода задачи. Это мощнее простого ignore_errors.
- name: Проверить, что порт 8080 свободен
ansible.builtin.shell: netstat -tln | grep ':8080'
register: port_check
failed_when: port_check.rc == 0 # Считаем ошибкой, если порт занят (команда grep нашла совпадение)
changed_when: false
3. Блоки обработки ошибок (block / rescue / always):
Аналогично try-catch-finally в языках программирования. Основной инструмент для сложных сценариев.
- name: Развертывание с откатом при ошибке
block:
- name: Копировать новую конфигурацию
ansible.builtin.copy:
src: /tmp/new_nginx.conf
dest: /etc/nginx/nginx.conf
- name: Проверить синтаксис nginx
ansible.builtin.command: nginx -t
register: nginx_test
- name: Перезагрузить nginx
ansible.builtin.systemd:
name: nginx
state: reloaded
rescue:
- name: Восстановить старую конфигурацию при ошибке
ansible.builtin.copy:
src: /tmp/backup_nginx.conf
dest: /etc/nginx/nginx.conf
notify: restart nginx
- name: Записать ошибку в лог
ansible.builtin.debug:
msg: "Развертывание провалилось на шаге '{{ ansible_failed_task.name }}'. Конфигурация откачена."
always:
- name: Всегда удалять временные файлы
ansible.builtin.file:
path: /tmp/new_nginx.conf
state: absent
Практический пример из CI/CD: При развертывании в Kubernetes может потребоваться проверить, что новый под запустился, прежде чем удалять старый.
- name: Развернуть новую версию приложения
kubernetes.core.k8s:
definition: "{{ lookup('file', 'deployment.yaml') }}"
wait: yes
wait_timeout: 60
register: deployment_result
failed_when: not deployment_result.resources[0].status.availableReplicas