Разработка 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 | Тестирование ролей | Средне | Для изолированных ролей |
Процесс работы
- Аналитика — определяем состав collection: какие модули, плагины, зависимости.
- Проектирование — разрабатываем структуру, интерфейсы модулей, тестовые сценарии.
- Разработка — пишем код модулей, плагинов, ролей.
- Тестирование — unit, sanity, integration тесты в CI.
- Деплой — публикация в приватный 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.







