Какие модули или команды в Ansible являются неидемпотентными и как с этим работать?

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

Ответ

Идемпотентность — одно из ключевых преимуществ Ansible, но не все модули ей обладают по своей природе. Основные неидемпотентные модули, с которыми нужно работать аккуратно:

  1. command и shell: Выполняют произвольную команду в shell. Ansible не может проверить состояние системы до и после, поэтому таск будет выполняться при каждом запуске плейбука. Это основная причина неожиданных изменений.

    # НЕИДЕМПОТЕНТНО: выполнится всегда
    - name: Restart service crudely
      command: systemctl restart myservice
  2. raw: Аналогичен command, но работает даже на системах без Python (используется для bootstrap). Также неидемпотентен.

  3. setup / gather_facts: Собирает факты о хостах. Хотя сам по себе сбор фактов идемпотентен (информация просто обновляется), этот таск всегда выполняется, если не отключен явно.

Как обеспечить идемпотентность с этими модулями?

  • Использовать параметры creates или removes: Ansible проверит существование файла и выполнит команду, только если его нет (или есть).

    - name: Run database migration script only once
      command: /opt/app/migrate.sh
      args:
        creates: /opt/app/.migration_done # Команда выполнится, только если этот файл не существует
  • Использовать параметр changed_when: Позволяет вручную указать, привел ли таск к изменениям. Например, можно парсить вывод команды.

    - name: Check if a process is running
      shell: ps aux | grep myapp | grep -v grep
      register: process_status
      changed_when: false # Помечаем, что таск никогда не вносит изменений
      failed_when: process_status.rc != 0 # Но может упасть, если процесс не найден
  • Заменить специализированным идемпотентным модулем: Всегда это предпочтительный путь.

    • Вместо command: systemctl restart X → использовать модуль service (state: restarted).
    • Вместо shell: apt-get install nginx → использовать модуль apt (state: present).
  • Использовать check_mode (--check): Перед запуском на production всегда полезно проверить плейбук в режиме симуляции, чтобы увидеть, какие неидемпотентные таски были бы выполнены.