ADG Оставить заявку
Блог Автоматизация 5 мин чтения

Molecule 2.x: TDD для Ansible-ролей - тест в Docker до коммита, регрессии в prod пополам

Перевели разработку Ansible-ролей на Molecule + Testinfra: тест в Docker до коммита стал обязательным, и число регрессий в prod заметно упало.

Контекст момента

Molecule 2.x укрепляется как основной инструмент тестирования Ansible-ролей с поддержкой Docker, Vagrant и Testinfra

Примерно месяц назад мы наконец перевели разработку всех новых Ansible-ролей на Molecule 2.x с Testinfra в качестве test framework. Это не революция и не большой архитектурный переход - просто инструмент, который закрывает конкретную дыру: раньше роль считалась готовой, когда инженер прогнал её руками на тестовой машине и сказал «работает». Теперь это не так.

Почему вообще Molecule

У нас накопилась типичная история. Есть несколько десятков Ansible-ролей, часть из них живёт в общем репозитории. Роль написали, прогнали на одном окружении - ладно. Потом кто-то её чуть подправил под другой проект - тоже ладно, «я тут быстро поменял одну таску». А потом в prod у клиента в два часа ночи выясняется, что handlers не срабатывает или роль идемпотентна только при первом прогоне, а при повторном что-то ломает.

Это классика. И классическое же решение для неё - тесты до коммита.

Molecule - это именно это: фреймворк для тестирования Ansible-ролей. Версия 2.x переписана относительно 1.x, появились нормальные scenarios, поддержка нескольких driver-ов (Docker, Vagrant, OpenStack), и плагиновая архитектура для verifier-ов - мы взяли Testinfra, потому что на нём уже писали тесты инфраструктуры.

Как устроен типичный тест

Структура роли с Molecule выглядит так:

roles/nginx/
  defaults/main.yml
  tasks/main.yml
  handlers/main.yml
  molecule/
    default/
      molecule.yml       # конфигурация: driver, платформы, verifier
      converge.yml       # playbook который применяет роль
      verify.yml         # запуск verifier-а (Testinfra)
      tests/
        test_nginx.py    # сами тесты на Python

Конфигурация molecule.yml для Docker-driver минимальна:

driver:
  name: docker
platforms:
  - name: instance
    image: centos:7
verifier:
  name: testinfra
  options:
    sudo: true

И тест на Testinfra - три-пять строк на то, что раньше проверялось глазами:

def test_nginx_installed(host):
    nginx = host.package("nginx")
    assert nginx.is_installed

def test_nginx_running(host):
    service = host.service("nginx")
    assert service.is_running
    assert service.is_enabled

def test_nginx_port(host):
    assert host.socket("tcp://0.0.0.0:80").is_listening

Запуск - molecule test. Внутри это последовательность: поднять контейнер, применить роль (converge), проверить идемпотентность (применить ещё раз и убедиться что нет changed-таск), прогнать verifier, снести контейнер.

Шаг с идемпотентностью особенно ценный - он бесплатно ловит роли, которые при повторном прогоне ломают что-то что уже настроили.

Что изменилось в процессе

До этого у нас был джентльменский договор: «перед PR - прогони на тестовом стенде». Стенд один, занять его может только один инженер, и он не всегда свободен. В итоге часть PR-ов шла без реальной проверки - «там же маленькая правка, и так понятно».

Теперь правило другое: PR без прошедшего molecule test в CI не мержится. Точка. Проверка идёт в Docker, инфраструктура под неё есть в GitLab CI, никакого shared-стенда. Инженер может прогнать тест локально до пуша - занимает две-три минуты для типичной роли.

За последний месяц на managed-проектах число инцидентов, связанных с регрессиями в Ansible-ролях, упало примерно вдвое по сравнению с предыдущим месяцем. Это не статистически строгое измерение - месяц короткий, и одновременно мы чуть поменяли процесс code review. Но корреляция очевидная, и команда это чувствует.

Что не идеально

Docker не равен prod. Тест на CentOS 7 в контейнере проходит - это не гарантия что всё будет хорошо на реальной машине с особенностями сети, SELinux в enforcing или специфичным ядром. Docker-driver не запускает systemd по умолчанию, и тесты на сервисы (is_running, is_enabled) работают через molecule-специфичный образ с systemd внутри, либо через mock. Это надо понимать и держать в голове при написании тестов.

Покрытие растёт медленно. Molecule мы ввели только для новых ролей и для ролей при значительных изменениях. Большой корпус старых ролей пока без тестов - покрывать их задним числом не запрещено, но и не форсируем. Когда трогаем роль под конкретную задачу - добавляем тест. Иначе - не трогаем.

Testinfra требует Python-инфраструктуры. Кто любит YAML и не хочет писать Python - тот пойдёт смотреть на ansible-lint как альтернативу для части проверок. Но ansible-lint не проверяет поведение роли, только синтаксис и стиль. Для реального TDD нужен verifier, а из живых вариантов в Molecule 2.x это Testinfra или Goss.

Ansible Tower и CI: как это стыкуется

Ansible Tower у нас уже есть и используется для запуска ролей на prod. Molecule существует в другом слое: это инструмент разработчика и CI-пайплайн в GitLab. В Tower попадает только то, что прошло через CI. Связка получается разумная: разработка и тестирование - Molecule в контейнере, деплой и управление - Tower.

Что дальше

Хотим добавить второй scenario для Vagrant - чтобы можно было прогнать более тяжёлые роли (например, с настройкой сети или дисков) на полноценной VM перед мержем. Docker для таких вещей слишком ограничен. Это будет опциональный шаг: Docker-тест в CI обязателен, Vagrant-тест - по запросу инженера локально или в специальном pipeline.

Ещё смотрим на molecule-ec2 driver для части ролей, которые специфичны для AWS - там нет смысла симулировать окружение в контейнере.

В целом ощущение от инструмента положительное. Molecule 2.x не идеальный, документация местами куцая, но он делает ровно то что нужно: убирает барьер «лень настраивать тестовое окружение» и делает тест первым шагом, а не последним.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.