Привет, Хабр! В этой статье хочу рассказать об основных модулях нашего приложения для развертывания больших инфраструктур и киберполигонов на основе Ansible. Эти модули мы называем A-services (сокращенно от Ansible services). Они содержат все сценарии подготовки, развертывания и настройки ИТ-инфраструктуры.
Общая архитектура приложения на основе Ansible, описана в предыдущей статье. Здесь хочу поделиться особенностями архитектуры модулей А-services.
Предыдущие статьи из серии:
Конфигуратор. Как подготовиться к развертыванию инфраструктур компаний по одной кнопке (Ansible)
Конфигуратор. Связываем хосты в единую инфраструктуру, используя функциональность Ansible inventory
Архитектура приложения. Разбиваем приложение Ansible на модули (Inventory, Deployer)
Техническое задание
Сценарии могут быть любого объема и качества и должны включать все — от развертывания виртуальных машин до тонкой настройки.
Скорость разработки должна быть максимальной (простой и переиспользуемый код, параллельная разработка).
Необходима быстрая адаптация под любые платформы (OpenStack, Proxmox, VMware).
Нужно обеспечить возможность установки любых версий ОС и приложений.
Запуск развертывания должен быть быстрым и контролируемым с возможностью управлять параллельной установкой.
Архитектурное решение
Как отмечалось ранее, A-service — это отдельный контейнер. Он содержит все необходимое для запуска:
внешние ссылки для скачивания дистрибутивов;
свою версия Ansible и свои библиотеки;
свою версию ОС и инструментария;
сценарий развертывания и настройки.
Команды для запуска контейнера A-service, команды управления и контракты для работы с Inventory – стандартизированы. Модули Deployer и Inventory могут работать с любыми модулями A-services.
Взаимодействие с внешними ресурсами
Для понимания «внешней» архитектуры опишу сценарий работы A-service:
Скачивание A-service с Git, модулем Deployer.
Запуск контейнера модулем Deployer с передачей ссылки на Inventory.
Подключение Inventory. По полученной от Deployer ссылке подключается Inventory, который сконфигурирован заранее. В нем — все нужные переменные и ссылки на внешние ресурсы.
Развертывание. Скачиваем дистрибутив для установки из интернета или с локального зеркала. Ссылки на локальные зеркала могут быть заданы в Inventory. Далее — установка. После установки остаются артефакты: ключи, учетные данные, файлы подключения и т. п. Они сохраняются в артефактории и в Inventory для оперативного использования.
A-service могут развертывать виртуальную машину, настраивать ее и делать интеграцию с другими виртуальными машинами. Важно, что может быть несколько A-service на одни и те же виртуальные машины — это добавляет гибкости в разработке и использовании.

Внутренняя архитектура
Так как модулей А-services большое количество, а постоянно писать один и тот же код, а тем более это все исправлять — очень трудозатратно, было принято решение использовать своего рода библиотеку. Эта библиотека представляет собой первичный контейнер (default A-service), который содержит общие сценарии для A-services.
Почему не использовали механизм коллекций и ролей? Было три основных ограничения, которые смогли обойти:
Нам бы все равно пришлось делать отдельные A-service с плейбуками. А это добавляет еще один уровень в архитектуру и усложняет ее.
Каждый A-service должен был хранить свои скрипты сборки(Dockerfile) и запуска(start.sh). Для улучшения и исправления который нужно было вносить изменения во все модули A-services.
Можно просто запутаться: коллекции и роли используются в A-service как основные прикладные сценарии.
Состав default A-service:
Конфигурация сборки контейнера. Все скрипты сборки и запуска контейнера правятся в одном месте.
Все зависимости и приложения ОС.
Ansible библиотеки и коллекции определенных версий.
-
Общие сценарии развертывания, которые можно расширять через A-service:
Установка ОС (Windows, Ubuntu, Debian, «РЕД ОС» и т. п.).
Установка виртуальных маршрутизаторов (CheckPoint, MikroTik, PT NGFW).
Работа с пользователями в стандартных приложениях (Active Directory, Microsoft Exchange, RoundCube, OpenLDAP и т. п.).
Работа с внешними сервисами (HashiCorp Vault, Passwork, ownCloud и другими).
Валидация входных данных из Inventory.
Особенности:
Default A-service версионируется так же, как и все остальное.
Default A-service с заранее скачанными основными зависимостями Ansible запускается гораздо быстрее.
Default A-service может быть несколько, можно делать их для разных платформ.
Дерево проекта выглядит так:

Dockerfile — сценарий сборки контейнера с зависимостями
requirements.txt — зависимости Python
./scripts/* — скрипты запуска A-service. Нужно, чтобы они были унифицированы для всех A-services, наследуемых этим default A-service.
./service/* — сценарии установки. В папках находятся playbook.yml и файл зависимостей для конкретного сценария.
./service/*_collection_requirements.yml — требуемые Ansible-коллекции.
Пример ./service/*/playbook.yml из default A-service (логика работы будет описана ниже):
- name: Basic Configuration | Ubuntu | Run
hosts: "{{ playbook_hostname }}"
gather_facts: false
become: true
tasks:
# ...
# Установка Default-роли создания виртуальной машины
- name: Role | vsphere_ubuntu
when:
- polygon_platform is match("vsphere")
- service_ubuntu_role_vsphere_ubuntu_down is not defined or
not service_ubuntu_role_vsphere_ubuntu_down
include_role:
name: vsphere_ubuntu
# Запуск ролей в зависящем A-service
- name: Role | Ubuntu | Run GameService Roles
when:
- service_ubuntu_gameservice_role is defined
- service_ubuntu_gameservice_role != ''
include_tasks: "{{ playbook_dir }}/../{{ service_ubuntu_gameservice_role }}"
# ...
Состав A-service:
Сценарий запуска контейнера (docker-compose.yml) на основе default A-service с подключением своих сценариев.
Зависимости Ansible, которые обогащают дефолтные библиотеки.
Файл playbook.yml — с полным сценарием установки, который хуками расширяет сценарии из default A-service.
На примере A-service развертывания seafile, дерево проекта выглядит так:

docker-compose.yml — сценарий запуска контейнера на основе default A-service.
requirements.yml — Ansible-зависимости для текущего A-service. Будут обогащать зависимости от сценария default A-service.
playbook.yml — книга, которая в определенной последовательности запускает индивидуальные сценарии из папки ./config, обогащающие сценарии default A-service. Далее запускает нужный playbook.yml из default A-service.
Пример playbook.yml из A-service (логика работы будет описана ниже):
- name: Include default ansible-service | Deploy GameService
vars:
# ...
# Файл для генерации служебного пользователя
service_ubuntu_custom_user: “config/custom_user.yml”
# Файл для настройки ролей основного сценария
service_ubuntu_gameservice_role: "config/gameservice_role.yml"
import_playbook: ubuntu/playbook.yml
Логика работы внутренней архитектуры

После старта контейнера A-service, основанного на default A-service (то есть каталоги и файлы A-service дополнили каталоги и файлы default A-service), запускается playbook.yml из A-service.
playbook.yml из A-service формирует ссылки на конфигурационные файлы (пример выше), которые будут встраиваться в сценарий из default A-service.
playbook.yml из A-service подключает playbook.yml из default A-service выбранного сценария (пример выше). Начинается основное развертывание.
-
Начинается процесс валидации:
Основной файл для валидации в default A-service может быть расширен хуком до и хуком после. То есть присутствует дефолтная проверка, но мы ее можем обогатить через сценарии A-service.
Настраиваем переменные для ролей default A-service. Их мы тоже можем расширить хуками до и после.
Настраиваем роли A-service.
Выполняем роли default A-service, которые настроены в п. 2.
Выполняем роли A-service с настройками из п. 3.
Сохраняем артефакты в Inventory. Это нужно для того, чтобы другие A-service могли получить информацию о ранее установленных сервисах. На этой основе и проходит интеграция внутри инфраструктуры.
Минус
Контроль версий. Нужно не забывать, что чем больше связей между компонентами, тем более внимательно необходимо следить за тем, что используешь и подключаешь в A-service. А здесь нужно следить за версиями default A-service, Ansible ролей, Ansible коллекций, Inventory и Deployer.
Итог
Налажена быстрая и независимая разработка сценариев развертывания и настройки.
Изменение платформы, обновление версии любого из компонентов не приносит много боли.
Есть возможность гибкого запуска сценариев — как в нужной последовательности, так и параллельно на основе блоков (A-services).
DmitriyEssensci
На мой взгляд очень сильно ломать базою структуру и арху FHS - кощунство.
Правильно понял, что под каждую роль есть свой a-s контейнер? Мне кажется подобная история это создание дополнительных прослоек, которые нужно как-то регулировать и версионировать. При 40+ ролей уже начинается зоопарк в репе, а это для каждого объекта ещё нужно сделать связи в VCS (inventory, playbooks, roles, templates)))). Так же не ясно как тут вообще будет работать молекула.
Но всё равно респект, материал интересный, в очередной раз говорит про гибкость всеми нами любимого ansible.