Тестирование и проверка перед прод

Урок 19 - проверяем playbook до того, как он коснётся боевого сервера.

«Лучший способ узнать, что playbook сломает прод - прогнать его сначала вхолостую, а не на проде».

Перед боевым запуском playbook стоит проверить: что синтаксис верен, что он сделает на самом деле, что он идемпотентен и не нарушает best practices. У Ansible для этого есть встроенные инструменты.

Проверка синтаксиса и сухой прогон

# Проверить синтаксис без выполнения
ansible-playbook site.yml --syntax-check

# Сухой прогон: показать, что было бы сделано, ничего не меняя
ansible-playbook site.yml --check

# То же, но с показом конкретных изменений в файлах
ansible-playbook site.yml --check --diff

Режим --check (dry-run) проходит все задачи, но не применяет изменения - только сообщает, что было бы changed. --diff вдобавок показывает построчную разницу в файлах. Это бесценно перед прод-выкатом.

Линтер ansible-lint

# Проверить playbook на соответствие best practices
ansible-lint site.yml

ansible-lint ловит типичные проблемы: использование command вместо модуля, отсутствие FQCN, незакавыченные mode, задачи без name, потенциальную неидемпотентность. Это автоматический ревьюер твоих playbook'ов.

Проверка идемпотентности

Главный тест зрелости playbook - двойной прогон. Запусти playbook дважды подряд: первый раз - привёл систему в порядок (могут быть changed), второй раз - всё должно быть ok и ноль changed. Если второй прогон что-то меняет - в playbook есть неидемпотентная задача.

   Прогон 1:  changed=5  ok=10   <- система настроена
   Прогон 2:  changed=0  ok=15   <- ИДЕМПОТЕНТНО (хорошо!)

   Если Прогон 2:  changed=2     <- ПРОБЛЕМА: где-то не check-then-act
# Тест идемпотентности: второй прогон должен дать changed=0
def run_playbook(state):
    changed = 0
    if state.get("nginx") != "installed":
        state["nginx"] = "installed"; changed += 1
    if state.get("config") != "deployed":
        state["config"] = "deployed"; changed += 1
    # НЕИДЕМПОТЕНТНАЯ задача (всегда что-то делает) - для контраста:
    # state["counter"] = state.get("counter", 0) + 1; changed += 1
    return changed

system = {}
print("Прогон 1: changed =", run_playbook(system))  # 2
print("Прогон 2: changed =", run_playbook(system))  # 0 -> идемпотентно
print("Прогон 3: changed =", run_playbook(system))  # 0

Попробуй сам ▶ Второй и третий прогоны дают changed=0 - playbook идемпотентен. Раскомментируй строку со счётчиком - и каждый прогон станет changed, показав неидемпотентную задачу, которую надо чинить.

Как работает под капотом

В режиме --check модули вызываются в специальном «проверочном» режиме: они вычисляют, нужно ли изменение, и сообщают результат, но не применяют его. Не все модули поддерживают check-режим одинаково (особенно сырой command) - это нормально, Ansible пометит такие задачи как пропущенные в проверке.

Частые ошибки

  • Тестировать сразу на проде. Сначала staging, сначала --check --diff, потом боевой запуск.
  • Игнорировать ansible-lint. Его замечания - это и есть накопленный опыт сообщества о том, что ломается.
  • Не делать двойной прогон. Без проверки идемпотентности неочевидные баги всплывут на проде.

Best practices

  • Перед прод-выкатом всегда: --syntax-check, затем --check --diff, затем staging.
  • Встрой ansible-lint и двойной прогон в CI - пусть проверяет каждый коммит.
  • Доводи playbook до состояния «второй прогон = все ok» - это эталон идемпотентности.

В реальной работе

Проверки перед прод-выкатом выстраивают в конвейер, который проходит каждый коммит. В CI обычно стоят три ступени: ansible-lint ловит стилевые и потенциально опасные конструкции, --syntax-check отсекает синтаксические ошибки, а на тестовом хосте playbook прогоняется дважды - чтобы поймать неидемпотентность. Для серьёзных ролей подключают molecule: он в одноразовом контейнере поднимает чистую систему, применяет роль, проверяет результат тестами и отдельно гоняет idempotence-сценарий. Такой конвейер превращает «надеюсь, не сломается» в «доказано, что работает». Привычка прогонять --check --diff на staging перед каждым продом - дешёвая страховка, которая много раз окупается, показывая неожиданные изменения до того, как они дойдут до боевых серверов.

Итоги

Перед прод-запуском проверяй playbook: --syntax-check для синтаксиса, --check --diff для сухого прогона, ansible-lint для best practices, двойной прогон для идемпотентности. Эти проверки ловят ошибки до того, как они дойдут до боевых серверов. Дальше соберём всё в целостный прод-проект.

Проверьте себя
1. Что делает режим --check (dry-run)?
AУдаляет все изменения на сервере
BПроходит задачи, не применяя изменений, и сообщает, что было бы changed
CУскоряет выполнение в два раза
DШифрует playbook
2. Как проверить, что playbook идемпотентен?
AЗапустить ansible-lint один раз
BПрогнать playbook дважды подряд: второй раз должно быть changed=0
CПосмотреть размер файла
DЗапустить с --syntax-check