Разработка Ansible Collections для переиспользуемых модулей

Когда стандартные модули Ansible не справляются с нестандартными задачами, мы разрабатываем Ansible Collections — переиспользуемые пакеты автоматизации. Наша команда создаёт кастомные модули, inventory-плагины и коллекции под ключ, публикуя их в Ansible Galaxy или automation hub. Результат — надёжные решения, которые версионируются, тестируются и масштабируются вместе с вашим бизнесом.
Часто задаваемые вопросы

Разработка Ansible Collections для переиспользуемых модулей

Мы часто сталкиваемся с ситуацией, когда стандартные модули Ansible не покрывают потребности проекта: нужно интегрироваться с внутренним API, добавить нестандартный источник инвентаря или распространить набор автоматизации между командами. В таких случаях мы разрабатываем Ansible Collections — пакеты, которые можно версионировать, тестировать и публиковать независимо от Ansible Core. За годы практики мы реализовали более 50 проектов с кастомными коллекциями — от простых модулей до полноценных inventory-плагинов для внутренних CMDB.

Collections появились в Ansible 2.9 как ответ на хаос с пространствами имён и версионированием. До collections всё было в одном репозитории: модули для AWS, GCP, VMware жили рядом с базовыми и обновлялись вместе с Core. Collection — это пакет, содержащий роли, плагины, модули и фильтры, с независимым циклом релиза. Сейчас это стандарт де-факто для распространения Ansible-контента.

Какие проблемы решаем

  • Кастомные модули. Стандартного модуля для API вашего сервиса нет? Пишем модуль, который взаимодействует с REST API, обрабатывает ошибки, поддерживает check mode. Пример: модуль регистрации хоста в NetBox (см. ниже) заменяет 50+ строк bash-скрипта, работает в 10 раз быстрее и безопаснее (токен не светится в логах).
  • Inventory-плагины. Источник данных (AD, OpenStack, собственный CMDB) не поддерживается community.general? Разрабатываем плагин, который подгружает инвентарь динамически — без скриптов-обёрток и костылей.
  • Унификация контента. Роли, модули и фильтры разбросаны по git-репозиториям? Собираем их в collection с единым versioning и зависимостями — упрощаем on-boarding новых инженеров.

Как устроена структура Ansible Collection?

ansible-galaxy collection init mycompany.infrastructure 

Создаётся структура:

mycompany/infrastructure/
├── galaxy.yml # метаданные, версия, зависимости
├── README.md
├── plugins/
│   ├── modules/ # кастомные модули
│   ├── inventory/ # inventory-плагины
│   ├── filter/ # Jinja2 фильтры
│   └── lookup/ # lookup-плагины
├── roles/
│   ├── hardening/
│   └── monitoring/
└── playbooks/

galaxy.yml:

---
namespace: mycompany
name: infrastructure
version: 1.2.0
description: Internal infrastructure automation collection
authors:
  - DevOps Team <[email protected]>
dependencies:
  community.general: ">=7.0.0"
  ansible.posix: ">=1.5.0"
---

Почему стоит использовать кастомные модули вместо скриптов?

Кастомный модуль — это Python-скрипт с AnsibleModule. В отличие от shell- или command-задач, модуль:

  • поддерживает check mode (idempotent preview);
  • возвращает структурированные данные (result) для использования в последующих задачах;
  • логирует чувствительные параметры (no_log) безопасно;
  • интегрируется с Ansible-тестированием.

Пример модуля для регистрации хоста в NetBox:

# plugins/modules/netbox_register.py
from ansible.module_utils.basic import AnsibleModule
import requests

DOCUMENTATION = r'''
module: netbox_register
short_description: Register host in NetBox CMDB
options:
    hostname:
        description: Hostname to register
        required: true
    ip_prefix:
        description: IP prefix to allocate from (e.g. 10.20.30.0/24)
        required: true
    netbox_url:
        description: NetBox URL
        required: true
    netbox_token:
        description: NetBox API token
        required: true
        no_log: true
'''

def run_module():
    module_args = dict(
        hostname=dict(type='str', required=True),
        ip_prefix=dict(type='str', required=True),
        netbox_url=dict(type='str', required=True),
        netbox_token=dict(type='str', required=True, no_log=True),
    )

    result = dict(changed=False, ip_address=None)
    module = AnsibleModule(argument_spec=module_args, supports_check_mode=True)

    headers = {'Authorization': f"Token {module.params['netbox_token']}"}
    nb_url = module.params['netbox_url']

    # Проверяем, не зарегистрирован ли уже
    resp = requests.get(
        f"{nb_url}/api/ipam/ip-addresses/",
        params={'dns_name': module.params['hostname']},
        headers=headers
    )
    if resp.json()['count'] > 0:
        result['ip_address'] = resp.json()['results'][0]['address']
        module.exit_json(**result)

    if module.check_mode:
        result['changed'] = True
        module.exit_json(**result)

    # Выделяем IP из prefix
    prefix_resp = requests.get(
        f"{nb_url}/api/ipam/prefixes/",
        params={'prefix': module.params['ip_prefix']},
        headers=headers
    )
    prefix_id = prefix_resp.json()['results'][0]['id']

    ip_resp = requests.post(
        f"{nb_url}/api/ipam/prefixes/{prefix_id}/available-ips/",
        json={'dns_name': module.params['hostname'], 'status': 'active'},
        headers=headers
    )

    result['changed'] = True
    result['ip_address'] = ip_resp.json()['address']
    module.exit_json(**result)

def main():
    run_module()

if __name__ == '__main__':
    main()

Использование в playbook:

---
- name: Register server in NetBox
  mycompany.infrastructure.netbox_register:
    hostname: "{{ inventory_hostname }}"
    ip_prefix: "10.20.30.0/24"
    netbox_url: "https://netbox.internal"
    netbox_token: "{{ vault_netbox_token }}"
  register: netbox_result
- name: Set IP address fact
  set_fact:
    server_ip: "{{ netbox_result.ip_address | ansible.utils.ipaddr('address') }}"

Сравнение: Collection vs Role

Критерий Role Collection
Область применения Конфигурация одного сервиса Пакет модулей, плагинов, ролей
Кастомные модули Нет Да, в plugins/modules
Inventory-плагины Нет Да
Фильтры и lookup Нет Да
Версионирование Зависит от repo Независимое, через galaxy.yml
Распространение Git clone Galaxy / Automation Hub

Как тестировать Ansible Collection?

Тестирование — ключевой этап. Используем три уровня:

  • Unit-тесты (pytest с ansible-test units) — проверяют логику модулей и фильтров на mock-данных.
  • Sanity-тесты (ansible-test sanity) — валидируют совместимость с Ansible API, PEP8, отсутствие deprecated вызовов.
  • Integration-тесты (ansible-test integration) — развёртывают тестовую инфраструктуру (Docker или Vagrant) и проверяют работу модулей в реальном окружении.

Пример конфигурации CI в GitHub Actions:

---
name: CI
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - run: pip install ansible-core
      - run: ansible-test sanity --docker -v
      - run: ansible-test units --python 3.11 -v
      - run: ansible-test integration --docker -v

Официальная документация Ansible по тестированию: Ansible Testing Guide

Сравнение средств тестирования

Инструмент Назначение Скорость Когда использовать
ansible-test units Юнит-тесты модулей Быстро На каждый коммит
ansible-test sanity Статический анализ Быстро Перед PR
ansible-test integration Интеграционные тесты Медленно Перед релизом
Molecule Тестирование ролей Средне Для изолированных ролей

Процесс работы

  1. Аналитика — определяем состав collection: какие модули, плагины, зависимости.
  2. Проектирование — разрабатываем структуру, интерфейсы модулей, тестовые сценарии.
  3. Разработка — пишем код модулей, плагинов, ролей.
  4. Тестирование — unit, sanity, integration тесты в CI.
  5. Деплой — публикация в приватный Galaxy или Automation Hub, документирование.

Что входит в работу

  • Разработка 2-3 кастомных модулей (или плагинов) с документацией.
  • Создание galaxy.yml с зависимостями и метаданными.
  • Настройка CI (GitLab CI / GitHub Actions) с запуском тестов при каждом PR.
  • Публикация в приватный Galaxy-сервер или Automation Hub.
  • Передача исходного кода и README.
  • Обучение команды использованию collection (1 час).
  • Гарантия совместимости с актуальными версиями Ansible (на момент сдачи).

Сроки ориентировочно

  • Collection с 2–3 модулями и базовыми тестами — 3–5 дней.
  • Inventory-плагин для специфичного источника — 2–3 дня.
  • Настройка CI с публикацией — 1–2 дня.

Стоимость рассчитывается индивидуально после анализа ваших требований. Свяжитесь с нами — оценим проект за 1 рабочий день.

Типичные ошибки при разработке Collections
  • Не включать check mode — нарушает idempotency.
  • Хранить чувствительные параметры без no_log.
  • Забыть про sanity-тесты перед публикацией (Ansible Galaxy требует их прохождения).
  • Версионировать коллекцию не семантически (следуйте SemVer).
  • Не указывать зависимости в galaxy.yml — collection не соберётся на чистом окружении.

Разработка Ansible Collections — это способ стандартизировать и ускорить автоматизацию, снижая operational costs на 30-50%. Закажите разработку коллекции под ваши задачи — получите готовый пакет с тестами и CI.

Наши компетенции:

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1394
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1369
  • Разработка логотипа компании B2B Advance
    Разработка логотипа компании B2B Advance
    816
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1141