ADG Оставить заявку
Блог Автоматизация 4 мин чтения

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, а разобраться какие абстракции теперь работают, и переписать роли так, чтобы следующий новый отечественный дистрибутив добавлялся в одну переменную, а не в отдельный файл.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.