Ansible 7.2 и community.general: адаптируем роли под RedOS и Astra Linux без дублирования логики
В community.general появилась поддержка redos и astra_linux. Разбираем как адаптировать playbook-и под dnf/apt-rpm без написания отдельных веток логики.
Ansible 7.x / ansible-core 2.14 - поддержка RedOS и Astra Linux в community.general collection
Когда community.general добавляет поддержку нового дистрибутива - это обычно тихое событие в changelog, которое обнаруживаешь случайно. С RedOS и Astra Linux в Ansible 7.2 / ansible-core 2.14 так и вышло: один из коллег наткнулся на это при разборе release notes, и мы решили проверить что именно изменилось на практике - потому что у нас как раз несколько клиентов переходят на эти дистрибутивы в рамках импортозамещения, и роли под них уже написаны. Частично кривовато.
Что конкретно появилось
В community.general os_family теперь корректно определяет RedOS как часть семейства RedHat (дистрибутив на базе Fedora, менеджер пакетов - dnf), а Astra Linux - как семейство Debian-подобных. Ключевое изменение - в ansible_facts['os_family'] и ansible_facts['distribution'] эти дистрибутивы перестали «пропадать» или определяться как Unknown.
До этого момента стандартная идиома с when: ansible_os_family == "RedHat" на RedOS не срабатывала - факт либо не подтягивался, либо приходил пустым, зависело от версии RedOS и того как был установлен python3. Приходилось городить дополнительные условия или хардкодить ansible_distribution == "RED" - что ломалось при минорных версиях и в целом было некрасиво.
Как выглядели старые роли
У нас в нескольких ролях была классическая грязь - отдельные task-файлы под каждый семейство ОС:
tasks/
main.yml
install-redhat.yml
install-debian.yml
install-redos.yml # добавили в какой-то момент
install-astra.yml # тоже
В main.yml - каскад include_tasks с условиями. Каждый новый дистрибутив требовал нового файла. При изменении логики установки - правок в нескольких местах. Стандартное техдолговое болото.
Как адаптировать без отдельных веток
После обновления до Ansible 7.2 можно пользоваться нормальными фактами. Основной паттерн - единый task-файл с ansible_os_family:
- name: Установить пакеты
ansible.builtin.package:
name: "{{ packages }}"
state: present
vars:
packages: "{{ _packages[ansible_os_family] | default(_packages['default']) }}"
vars_files: []
vars:
_packages:
RedHat:
- httpd
- mod_ssl
Debian:
- apache2
- apache2-utils
default:
- apache2
RedOS при этом попадает в RedHat - семейство определяется корректно, dnf вызывается через ansible.builtin.package без явного указания менеджера. Astra Linux идёт в Debian, apt-rpm (если используется) тоже подхватывается через family.
Нюанс с Astra: в зависимости от редакции (CE или SE) и версии ядра поведение apt может отличаться. Astra Linux SE 1.7.x построена на Debian 9/10 базе, и часть пакетов в репозитории Astra называется иначе, чем в upstream Debian. Так что ansible_os_family == "Debian" - условие необходимое, но не достаточное для полного переиспользования debian-ролей. Если пакет существует в обоих репозиториях под одним именем - всё хорошо. Если нет - нужна переменная на уровне дистрибутива:
vars:
_pkg_name:
Astra Linux: "astra-specific-pkg"
Debian: "generic-pkg"
Ubuntu: "generic-pkg"
Здесь уже ansible_distribution - потому что os_family не поможет различить Astra и Debian.
Где реально помогло
Первое, что переписали - роль установки агента мониторинга. Раньше там был отдельный файл для RedOS с захардкоженным dnf install, хотя логика ничем не отличалась от RHEL. После обновления - убрали отдельный файл, роль работает на всей линейке RedHat-family включая RedOS 7.3, который сейчас активно идёт в продакшн у нескольких клиентов.
Второе - роль настройки sysctl и ulimits. Там вообще не было package-задач, просто условия на конфигурационные файлы. Но ansible_facts собирались криво на Astra - некоторые переменные типа ansible_service_mgr возвращали пустую строку вместо systemd, и задачи с when: ansible_service_mgr == "systemd" тихо скипались. С ansible-core 2.14 и правильными фактами это починилось само.
Что не починилось само - работа с firewalld на RedOS. Там установлен firewalld, но в некоторых конфигурациях он не запущен и не enabled - и роли, которые предполагают его наличие по ansible_os_family == "RedHat", всё равно падают. Это не проблема факт-сбора, это особенность конкретного дистрибутива. Проверяем через ansible_facts.services и оборачиваем в block с rescue.
Где стоим
Роли под отечественные дистрибутивы в нашем внутреннем репозитории потихоньку приводим к единой схеме - без отдельных файлов на каждый дистрибутив там, где это не оправдано реальными различиями. Ansible 7.2 сделал это существенно проще: факты теперь приходят предсказуемо, и можно строить логику на них, а не на костылях.
Полностью убрать ветвление по дистрибутивам не выйдет - Astra и RedOS всё равно имеют свои особенности в именах пакетов и конфигурационных путях. Но одна ветка на os_family вместо пяти файлов на каждый конкретный дистрибутив - это уже прогресс.
Это часть текущей работы по managed-сопровождению инфраструктуры клиентов: не просто накатить новую версию Ansible, а разобраться какие абстракции теперь работают, и переписать роли так, чтобы следующий новый отечественный дистрибутив добавлялся в одну переменную, а не в отдельный файл.