Ansible Collections: мигрируем legacy-роли и настраиваем Nexus-прокси для Galaxy
Ansible 2.9/2.10 делает Collections основным способом дистрибуции ролей. Разбираем миграцию, FQCN, requirements.yml и внутренний Nexus-прокси вместо прямого Galaxy.
Ansible Collections становятся основным способом дистрибуции ролей начиная с Ansible 2.9/2.10 - Galaxy переориентируется на collections вместо отдельных ролей
Когда Ansible 2.9 вышел в конце прошлого года, мы пробежались по release notes и отметили Collections как «интересно, разберёмся позже». Потом команда Ansible объявила план 2.10, где часть модулей должна переехать из core в collections - и стало понятно, что «позже» заканчивается. Разобрались заранее, пока не пришлось в авральном режиме.
Что изменилось и зачем
До Collections ролевая экосистема Ansible держалась на Galaxy с отдельными ролями: ansible-galaxy install geerlingguy.docker - и вперёд. Проблема в том, что роль живёт сама по себе: никаких зависимостей на конкретные версии модулей, нет пространства имён, конфликты имён между ролями разных авторов решались «на глазок». Для пары ролей в небольшом проекте это не страшно, но у нас их накопилось за несколько лет прилично.
Collection - это пакет с пространством имён вида namespace.collection, который может содержать роли, модули, плагины, фильтры и тесты разом. Версионируется через semver. Galaxy умеет с ними работать как с единицей дистрибуции. Идея правильная: примерно то же, что Terraform сделал с модулями в частном реестре, только для Ansible.
Структура и FQCN
Коллекция живёт по пути ~/.ansible/collections/ansible_collections/namespace/collection/ или в collections/ внутри проекта. Внутри стандартная структура:
adg/
infra/
roles/
base_linux/
postgres_cluster/
plugins/
modules/
filter/
galaxy.yml
galaxy.yml - манифест: namespace, name, version, authors, зависимости от других коллекций. Наш namespace - adg, первая коллекция назвали infra.
Отсюда вытекает FQCN - Fully Qualified Collection Name. По плану 2.10: вместо просто postgresql_query будет community.postgresql.postgresql_query, вместо docker_container - community.docker.docker_container. В плейбуках, которые написаны под 2.8 и старше, модули вызывались короткими именами - в Ansible 2.9 это пока ещё работает через обратную совместимость, но по дорожной карте 2.10 маппинг уберут. Лучше переписывать сразу.
У нас переход занял пару вечеров: прошлись grep-ом по плейбукам по паттерну ^ - name: и для каждого task-а сверились с документацией, в какую коллекцию переехал модуль. Скучная работа, но разовая.
requirements.yml теперь двойной
Раньше requirements.yml описывал только роли:
roles:
- name: geerlingguy.docker
version: 6.0.1
Теперь добавился раздел collections:
collections:
- name: ansible.netcommon
version: ">=1.0.0"
- name: f5networks.f5_modules
version: 1.0.0
- name: adg.infra
version: ">=0.3.0"
roles:
- name: geerlingguy.nginx
version: 2.8.0
Устанавливается одной командой: ansible-galaxy install -r requirements.yml. Роли и коллекции ставятся параллельно. Версионирование с range-операторами работает так же, как в pip - >=, !=, точная версия.
Внутренний Nexus как прокси Galaxy
Прямой выход на galaxy.ansible.com в продакшн-окружении нас не устраивает по тем же причинам, что и прямой registry.terraform.io - см. как мы настраивали Terraform-реестр через GitLab. Те же соображения: контроль над тем, что реально устанавливается, кэш на случай недоступности upstream, возможность разместить внутренние коллекции рядом.
Nexus Repository Manager 3.x умеет работать с Ansible Galaxy через тип репозитория proxy. Настройка прямолинейная:
- Создаём proxy-репозиторий с Remote URL
https://galaxy.ansible.comи форматомraw. - Создаём hosted-репозиторий для внутренних коллекций.
- Объединяем в group.
Дальше в ansible.cfg указываем наш Nexus вместо публичного Galaxy:
[galaxy]
server_list = nexus_group, nexus_internal
[galaxy_server.nexus_group]
url = https://nexus.internal.company/repository/ansible-group/
auth_url = https://nexus.internal.company/service/rest/v1/security/saml
token = {{ lookup('env', 'NEXUS_ANSIBLE_TOKEN') }}
[galaxy_server.nexus_internal]
url = https://nexus.internal.company/repository/ansible-internal/
token = {{ lookup('env', 'NEXUS_ANSIBLE_TOKEN') }}
Токен в CI через переменную окружения, в ansible.cfg не хранится. В managed-сопровождении мы не кладём секреты в репозиторий - это не обсуждается.
ansible-galaxy опрашивает серверы из server_list по очереди: сначала group (там community-коллекции через прокси), потом internal (там наша adg.infra). Если коллекция не найдена нигде - ошибка. Всё прозрачно.
Обратная совместимость и что с ней делать
В Ansible 2.9 короткие имена модулей пока работают - плейбук с docker_container: без namespace не упадёт немедленно. Но по дорожной карте 2.10 маппинг сохранится только через файлы _redirects с [DEPRECATION WARNING] в логах, а потом уберут и это.
Мы решили не тянуть с переходом: депрекейшн-предупреждения в CI превращаются в шум, который все начинают игнорировать, а потом удивляются почему что-то сломалось. Переписали все task-и на FQCN за один проход. Плюс прописали в .ansible-lint правило fqcn-builtins чтобы новый код без FQCN не проходил lint.
Роли, которые мы перенесли в adg.infra, теперь вызываются как adg.infra.base_linux вместо просто base_linux. В playbook-ах поменяли все roles: секции. Немного многословнее, зато сразу понятно откуда роль.
Где сейчас
adg.infra версии 0.4.1 живёт в нашем hosted-репозитории на Nexus. Внутри - четыре роли, которые раньше болтались по разным репозиториям россыпью. CI публикует новую версию через ansible-galaxy collection publish при появлении тега. Потребители прописывают adg.infra: ">=0.4.0" в requirements.yml и получают обновления при следующем ansible-galaxy install.
Боль от самой миграции - один раз и прошла. Зато теперь при переходе на Ansible 2.10, когда он выйдет, мы не будем гадать, что именно переехало и куда: коллекции версионированы, зависимости зафиксированы, Nexus кэширует. GitHub Actions пайплайн для этого описывали отдельно, принцип тот же.
Одно наблюдение напоследок: Galaxy UI для collections на момент написания ощутимо сырее, чем для ролей. Поиск работает не так как ожидаешь, документация коллекции рендерится не всегда. Это рабочий момент, но если планируете публиковать внутренние коллекции - Nexus с его UI окажется удобнее, чем полагаться на Galaxy в качестве основного хранилища.