Как обработать отказ операции в Ansible playbook

«Как обработать отказ операции в Ansible playbook» — вопрос из категории Ansible, который задают на 23% собеседований Devops Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В 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