Перейти к содержанию

Ansible Molecule — тестирование Ansible-ролей в Docker

Molecule позволяет запускать Ansible-роль в изолированном тестовом окружении.

В этом примере Molecule создаёт Docker-контейнер с Ubuntu, подготавливает его и применяет к нему тестируемую Ansible-роль.

Схема работы:

Molecule
    │
    ├── create
    │     └── создаёт Docker-контейнер
    │
    ├── prepare
    │     └── подготавливает контейнер для Ansible
    │
    ├── converge
    │     └── применяет тестируемую Ansible-роль
    │
    ├── verify
    │     └── проверяет результат
    │
    └── destroy
          └── удаляет тестовое окружение

Требования

На управляющей машине должны быть установлены:

  • Docker
  • Python
  • Ansible
  • Molecule
  • Molecule Docker plugin

Проверить Docker:

docker version

Проверить Python:

python3 --version

Для нового окружения лучше использовать актуальную поддерживаемую версию Python.


Создание Python virtual environment

Создадим каталог проекта:

mkdir test_docker_role
cd test_docker_role

Создадим виртуальное окружение:

python3 -m venv venv

Активируем:

source venv/bin/activate

После активации проверить:

python --version
which python

Обновить pip:

python -m pip install --upgrade pip

Установить Ansible, Molecule и Docker plugin:

python -m pip install ansible-core molecule "molecule-plugins[docker]"

Проверить:

ansible --version
molecule --version

Создание Ansible-роли

Создадим каталог ролей:

mkdir roles
cd roles

Создадим роль:

ansible-galaxy role init docker

Получим примерно такую структуру:

test_docker_role/
├── venv/
└── roles/
    └── docker/
        ├── defaults/
        ├── files/
        ├── handlers/
        ├── meta/
        │   └── main.yml
        ├── tasks/
        │   └── main.yml
        ├── templates/
        ├── tests/
        └── vars/

Перейти в роль:

cd docker

Настройка metadata роли

Molecule использует метаданные Ansible Galaxy для определения имени роли.

Открыть:

vim meta/main.yml

Минимальный пример:

---
galaxy_info:
  role_name: docker
  namespace: devopslearning
  author: Andrey
  description: Install and configure Docker
  license: MIT
  min_ansible_version: "2.15"

dependencies: []

Полное имя роли в данном случае:

devopslearning.docker

Не путать:

docker                 # каталог роли

default                # Molecule scenario

devopslearning.docker  # полное имя Ansible-роли

Создание Molecule scenario

Находясь внутри:

roles/docker/

создать scenario:

molecule init scenario default

Появится:

roles/docker/
└── molecule/
    └── default/
        ├── molecule.yml
        ├── create.yml
        ├── destroy.yml
        ├── converge.yml
        ├── prepare.yml
        └── ...

Warning

В новых версиях Molecule команда из старых руководств:

molecule init scenario -d docker default

может не работать.

Например:

Error: No such option '-d'

Использовать:

molecule init scenario default

Настройка molecule.yml

Открыть:

vim molecule/default/molecule.yml

Минимальная конфигурация:

---
driver:
  name: docker

platforms:
  - name: instance
    image: ubuntu:24.04
    pre_build_image: true

provisioner:
  name: ansible

Здесь:

Параметр Назначение
driver тип тестового окружения
docker использовать Docker
platforms список тестовых машин
instance имя тестового экземпляра
image Docker image
provisioner чем конфигурировать instance

Создание Docker-контейнера

В некоторых версиях/сценариях Molecule create.yml создаётся как generic-заготовка.

Например, можно увидеть:

# TODO: Developer must implement and populate 'server' variable

Сам по себе такой файл контейнер не создаёт.

Для минимального локального сценария можно создать контейнер через Ansible.

molecule/default/create.yml:

---
- name: Create
  hosts: localhost
  connection: local
  gather_facts: false

  tasks:
    - name: Create Molecule container
      community.docker.docker_container:
        name: instance
        image: ubuntu:24.04
        state: started
        command: sleep infinity

После этого:

molecule create

Проверить:

docker ps

Должен существовать контейнер:

NAMES
instance

Подготовка контейнера

Обычный образ:

ubuntu:24.04

минимальный и может не содержать Python.

Это проблема для Ansible, потому что большинство Ansible-модулей выполняются на target через Python.

Без Python можно получить:

The module interpreter '/usr/bin/python3' was not found

или ошибку на:

TASK [Gathering Facts]

Получается bootstrap-проблема:

Ansible
   │
   ├── хочет использовать apt module
   │
   └── apt module требует Python
                     │
                     └── Python ещё не установлен

Для первоначальной установки Python используется raw, поскольку raw не требует Python на удалённой системе.

Создать:

vim molecule/default/prepare.yml
---
- name: Prepare
  hosts: all
  gather_facts: false

  tasks:
    - name: Install Python
      ansible.builtin.raw: apt-get update && apt-get install -y python3
      changed_when: false

Важно:

gather_facts: false

Пока Python не установлен, Ansible не сможет нормально выполнить сбор facts.

Запустить:

molecule prepare

Подключение тестируемой роли

Настроить:

vim molecule/default/converge.yml
---
- name: Converge
  hosts: all
  gather_facts: true
  become: true

  tasks:
    - name: Apply Docker role
      ansible.builtin.include_role:
        name: devopslearning.docker

Важно использовать имя роли:

devopslearning.docker

а не:

default.docker

default — это имя Molecule scenario, а не namespace Ansible-роли.


Первый полный запуск

Для чистого теста:

molecule destroy
molecule create
molecule prepare
molecule converge

Последовательность:

molecule destroy
       │
       ▼
удалить старое окружение

molecule create
       │
       ▼
создать Ubuntu container

molecule prepare
       │
       ▼
установить Python

molecule converge
       │
       ▼
применить devopslearning.docker

Успешный converge выглядит примерно так:

TASK [Gathering Facts]
ok: [instance]

TASK [Apply Docker role]
included: devopslearning.docker for instance

TASK [devopslearning.docker : ...]
changed: [instance]

PLAY RECAP
instance : ok=3 changed=1 failed=0

Главное:

failed=0

и:

converge: Executed: Successful

Повторный запуск

После первого успешного запуска:

molecule converge

Molecule не обязан пересоздавать контейнер.

Если instance уже существует, можно увидеть:

create: Skipping, instances already created.

А если prepare уже выполнялся:

prepare: Skipping, instances already prepared.

После чего выполняется непосредственно:

converge

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

Ansible-роль должна по возможности быть идемпотентной.

Первый запуск:

changed=5

может быть нормальным — система конфигурируется.

При повторном запуске желательно получить:

changed=0

То есть:

Первый запуск
Ubuntu
  ↓
роль
  ↓
Docker установлен
  ↓
changed > 0


Второй запуск
Ubuntu + Docker
  ↓
та же роль
  ↓
система уже в нужном состоянии
  ↓
changed = 0

Для ручной проверки:

molecule converge
molecule converge

Проверка состояния контейнера

Посмотреть контейнер:

docker ps

Все контейнеры, включая остановленные:

docker ps -a

Зайти внутрь:

docker exec -it instance bash

Также при корректно настроенном scenario можно использовать:

molecule login

Удаление тестового окружения

После тестирования:

molecule destroy

Проверить:

docker ps -a

Тестовый instance должен быть удалён.


Полный lifecycle

В итоге Molecule используется примерно так:

                    MOLECULE
                       │
                       ▼
                   dependency
                       │
                       ▼
                     create
                       │
                       ▼
                    prepare
                       │
                       ▼
                    converge
                       │
                       ▼
                  idempotence
                       │
                       ▼
                     verify
                       │
                       ▼
                    destroy

Назначение этапов:

Этап Назначение
dependency установить зависимости роли
create создать тестовое окружение
prepare подготовить окружение
converge применить тестируемую роль
idempotence проверить повторное применение
verify проверить результат
destroy удалить окружение

Полезные команды

Создать окружение:

molecule create

Подготовить:

molecule prepare

Применить роль:

molecule converge

Войти в instance:

molecule login

Посмотреть состояние:

molecule list

Удалить:

molecule destroy

Полный тестовый цикл:

molecule test

Типовые ошибки

No such option '-d'

Старые руководства могут использовать:

molecule init scenario -d docker default

В используемой версии Molecule такой параметр отсутствует.

Использовать:

molecule init scenario default

а driver определить в molecule.yml.


docker ps пуст после molecule create

Проверить:

cat molecule/default/create.yml

Если там находится:

TODO: Developer must implement...

то это generic create.yml, который фактически не создаёт Docker-контейнер.

Проверить вывод:

molecule create
docker ps -a

Важно: успешный return code create.yml ещё не гарантирует, что Docker-контейнер действительно был создан.


/usr/bin/python3 was not found

Пример:

The module interpreter '/usr/bin/python3' was not found

Причина:

минимальный Ubuntu image
        +
нет Python
        +
Ansible modules требуют Python

Решение — установить Python на этапе prepare через:

ansible.builtin.raw:

The role 'default.docker' was not found

Причина — перепутано имя scenario и имя роли.

default                → Molecule scenario
docker                 → каталог роли
devopslearning.docker  → Ansible role

Использовать:

ansible.builtin.include_role:
  name: devopslearning.docker

Molecule использует старые версии

Проверить:

molecule --version

Также:

python --version

Старый Python может ограничивать версии устанавливаемых Python-пакетов.

Посмотреть доступные версии:

pip index versions molecule-plugins

Если virtual environment был создан старым Python, простого:

pip install --upgrade ...

может быть недостаточно.

Нужно пересоздать venv современным Python:

deactivate
rm -rf venv

python3 -m venv venv
source venv/bin/activate

python -m pip install --upgrade pip
python -m pip install ansible-core molecule "molecule-plugins[docker]"

После этого проверить:

python --version
molecule --version

Итоговая структура

После настройки:

test_docker_role/
├── venv/
└── roles/
    └── docker/
        ├── defaults/
        ├── handlers/
        ├── meta/
        │   └── main.yml
        ├── tasks/
        │   └── main.yml
        ├── templates/
        ├── vars/
        └── molecule/
            └── default/
                ├── molecule.yml
                ├── create.yml
                ├── prepare.yml
                ├── converge.yml
                ├── verify.yml
                └── destroy.yml

Главная идея:

роль
│
├── tasks/             ← код роли
│
└── molecule/
    └── default/       ← тесты этой роли

Molecule позволяет уничтожать и пересоздавать тестовую систему сколько угодно раз, не затрагивая рабочие серверы.

Это позволяет разрабатывать роль по циклу:

написал task
    ↓
molecule converge
    ↓
посмотрел результат
    ↓
исправил task
    ↓
molecule converge
    ↓
проверил idempotence
    ↓
molecule verify