Регистрация результатов и специальные переменные

Рейтинг: 80.6% · 16 голосов
Подробный курс по Ansible с прицелом на экзамен RHCE EX294: установка и инвентарь, плейбуки, переменные и факты, Vault, циклы и условия, шаблоны Jinja2, роли и коллекции, Execution Environments и ansible-navigator, управление системами (диски, LVM, cron, SELinux, firewalld). Примеры на RHEL/Fedora. Актуально на 2026.
Ответить
Аватара пользователя
Maksim_DevOps
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Регистрация результатов и специальные переменные

Сообщение Maksim_DevOps »

Оглавление курса (47)
  1. Что такое Ansible и зачем он нужен: автоматизация без агентов
  2. Сертификация RHCE и экзамен EX294: что внутри и как готовиться
  3. Архитектура Ansible: control node, узлы, модули и плагины
  4. Установка Ansible на control node: dnf, pip и версии ansible-core
  5. Подготовка управляемых узлов: SSH, пользователь и sudo
  6. Инвентарь Ansible: hosts, группы и переменные
  7. Настройка Ansible: файл ansible.cfg и приоритеты конфигурации
  8. Host patterns и инструменты командной строки Ansible
  9. Ad hoc команды Ansible: быстрые задачи без плейбука
  10. Ansible playbook: что это такое и как устроен
  11. Структура плейбука: play, task, модули и переменные
  12. Запуск плейбуков: проверка, теги, ограничения и отладка
  13. Параллелизм и порядок выполнения: forks, serial, strategy
  14. Факты Ansible: сбор информации об узлах
  15. Переменные Ansible: типы, объявление и приоритет
  16. Регистрация результатов и специальные переменные (вы здесь)
  17. Ansible Vault: шифрование паролей и секретов
  18. Организация инвентаря: group_vars, host_vars и вложенные группы
  19. Динамический инвентарь и инвентарные плагины
  20. Циклы в Ansible: loop, списки и словари
  21. Повтор задачи до условия: until, retries и delay
  22. Условия в Ansible: директива when и логика
  23. Jinja2 в Ansible: выражения, фильтры и подстановки
  24. Обработчики Ansible: handlers, notify и flush_handlers
  25. Обработка ошибок: failed_when, changed_when и ignore_errors
  26. Блоки в Ansible: block, rescue и always
  27. Управление файлами: модули file, copy, fetch и stat
  28. Архивы и сборка файлов: archive, unarchive, assemble
  29. Точечное редактирование файлов: lineinfile и blockinfile
  30. Шаблоны Jinja2: модуль template и динамические конфиги
  31. Jinja2 продвинуто: фильтры, циклы, макросы и lookup
  32. Модули Ansible: ansible-doc и поиск нужного модуля
  33. Разработка собственного модуля Ansible на Python
  34. Роли Ansible и Ansible Galaxy: переиспользование
  35. Разработка роли Ansible: создание с нуля
  36. Зависимости ролей и requirements.yml
  37. Коллекции Ansible: ansible.posix, community и своя коллекция
  38. Execution Environments: контейнеры для запуска Ansible
  39. Сборка Execution Environment с ansible-builder
  40. ansible-navigator: запуск плейбуков в Execution Environment
  41. Управление SSH-ключами через Ansible: authorized_key
  42. Управление дисками: filesystem, mount и parted
  43. LVM через Ansible: тома, группы и расширение
  44. Планировщик задач: cron и systemd timers через Ansible
  45. Безопасность: hardening SSH и репозитории dnf/yum
  46. SELinux через Ansible: булевы, контексты и порты
  47. Сквозной проект и подготовка к экзамену EX294
Зачем вообще ловить вывод задачи

Рано или поздно ты упрёшься в стену: плейбук должен принять решение на основе того, что вернула предыдущая команда. Версия пакета, наличие файла, код возврата скрипта, имя текущего интерфейса. Ansible по умолчанию выполняет задачу и забывает результат - он нигде не лежит, в следующей задаче его не достать. Вот тут и нужен ansible register: ключевое слово, которое сохраняет полный результат модуля в переменную, чтобы дальше с ним работать.

Без register люди начинают городить shell-конвейеры с временными файлами на /tmp, теряют идемпотентность и злятся. С register всё становится на свои места: запустил команду, поймал её вывод, проверил ansible stdout или код возврата, и принял решение через when. Связка register + when - это база, и на экзамене RHCE (EX294) она встречается постоянно. Если её не чувствуешь руками, часть задач просто не закроешь в отведённое время.

В этом уроке разберём, что именно попадает в зарегистрированную переменную, как её отлаживать через debug, как ведут себя результаты внутри циклов (привет уроку про loop), и зачем нужны magic-переменные - hostvars, groups, inventory_hostname и компания.

Изображение

Что лежит внутри зарегистрированной переменной

Берём простой пример. Запускаем команду и сохраняем результат:

Код: Выделить всё

- name: Проверить версию ядра
  hosts: webservers
  gather_facts: false
  tasks:
    - name: Узнать текущее ядро
      ansible.builtin.command: uname -r
      register: kernel
      changed_when: false

    - name: Показать всю структуру результата
      ansible.builtin.debug:
        var: kernel
Обрати внимание на changed_when: false - команда uname ничего не меняет в системе, поэтому глупо красить её жёлтым как changed. Это хорошая привычка для read-only команд.

В переменной kernel лежит не просто строка, а целый словарь. Главные поля, которые тебе реально пригодятся:
  • rc - код возврата (return code). 0 - успех, всё остальное - ошибка.
  • stdout - стандартный вывод одной строкой, с символами переноса внутри.
  • stdout_lines - тот же вывод, но уже разбитый в список по строкам. Удобно для loop.
  • stderr и stderr_lines - поток ошибок, аналогично.
  • changed - изменила ли задача состояние системы (true/false).
  • failed - провалилась ли задача.
  • skipped - была ли пропущена из-за when.
Достучаться до конкретного поля можно через точку. Это и есть тот самый ansible stdout, за которым все охотятся:

Код: Выделить всё

- name: Решение на основе вывода
  ansible.builtin.debug:
    msg: "Ядро: {{ kernel.stdout }}, код возврата {{ kernel.rc }}"
Самый частый рабочий паттерн - register плюс when. Регистрируем результат, а следующую задачу запускаем условно:

Код: Выделить всё

- name: Проверить, установлен ли nginx
  ansible.builtin.command: rpm -q nginx
  register: nginx_check
  changed_when: false
  failed_when: false

- name: Поставить nginx, если его нет
  ansible.builtin.dnf:
    name: nginx
    state: present
  when: nginx_check.rc != 0
Здесь failed_when: false критичен. Команда rpm -q вернёт ненулевой код, если пакета нет, и без этой строки плейбук свалится с ошибкой ещё до того, как мы успеем проверить условие. Мы же хотим именно поймать этот код и принять решение, а не падать. На Ubuntu/Debian тот же приём, только проверка через dpkg -l, а ставит apt; на RED OS и Astra - всё как в RHEL-семействе, dnf или apt-rpm соответственно. По уму, конечно, для установки пакетов лучше сразу модуль dnf со state present - он идемпотентен сам по себе. Но как демонстрация register + when пример честный, а в реальных задачах такой паттерн нужен, когда штатного модуля под проверку нет.

Register внутри циклов: ловушка results

Вот место, где спотыкаются почти все, кто только прошёл урок про loop. Если ты регистрируешь результат у задачи с циклом, структура переменной меняется. Привычных kernel.stdout и kernel.rc на верхнем уровне больше нет. Вместо этого появляется список results - по одному элементу на каждую итерацию.

Код: Выделить всё

- name: Пинговать список хостов
  ansible.builtin.command: "ping -c1 {{ item }}"
  loop:
    - 8.8.8.8
    - 1.1.1.1
  register: ping_result
  changed_when: false
  failed_when: false

- name: Разобрать результаты по итерациям
  ansible.builtin.debug:
    msg: "{{ item.item }} -> rc={{ item.rc }}"
  loop: "{{ ping_result.results }}"
Смотри внимательно. Внутри каждого элемента ping_result.results есть знакомые rc, stdout, changed, а ещё ключ item - это значение исходной итерации цикла (8.8.8.8 и 1.1.1.1). То есть во втором цикле item.item - это адрес, а item.rc - код возврата пинга по нему. На верхнем уровне ping_result.changed будет true, если хоть одна итерация что-то изменила, и failed - если хоть одна провалилась.

Главная ошибка новичка - написать ping_result.stdout после цикла и удивляться ошибке "dict object has no attribute stdout". Запомни правило: есть loop - значит результат в .results, и его надо перебирать.

Когда не уверен в структуре - не гадай, а отлаживай. debug с var: показывает дерево целиком:

Код: Выделить всё

- name: Глянуть всю структуру
  ansible.builtin.debug:
    var: ping_result
    verbosity: 1
Параметр verbosity: 1 означает, что вывод покажется только при запуске с ключом -v. Удобно: оставил отладку в плейбуке, она не мешает в обычном прогоне, но включается одним флагом.

Magic-переменные: hostvars, groups, inventory_hostname

Кроме того, что ты регистрируешь сам, Ansible всегда держит наготове набор служебных переменных. В русскоязычной документации их называют специальными, в англоязычной - magic variables ansible. Их не нужно объявлять, они появляются автоматически и описывают сам процесс выполнения: кто текущий хост, какие есть группы, что вообще происходит.

Самые ходовые:
  • inventory_hostname - имя текущего хоста ровно так, как он записан в инвентаре. Не FQDN из фактов, а именно метка из inventory. Незаменим, когда надо генерировать пути или конфиги по имени узла.
  • groups - словарь всех групп инвентаря и их членов. groups['webservers'] вернёт список хостов в группе webservers.
  • group_names - список групп, в которые входит текущий хост.
  • hostvars - доступ к переменным и фактам ЛЮБОГО хоста, а не только текущего.
  • ansible_play_hosts - список хостов, которые ещё участвуют в текущем play (упавшие отсюда выбывают). Это современная замена устаревшего play_hosts.
Самый мощный из них - ansible hostvars. Внутри одного плея каждый хост обрабатывается своим процессом и по умолчанию видит только себя. Но через hostvars можно дотянуться до данных соседа. Классическая задача: на балансировщике собрать IP-адреса всех веб-серверов.

Код: Выделить всё

- name: Собрать IP веб-серверов на балансировщике
  ansible.builtin.debug:
    msg: "{{ hostvars[item]['ansible_default_ipv4']['address'] }}"
  loop: "{{ groups['webservers'] }}"
Здесь мы перебираем список хостов из группы webservers (это groups), а через hostvars[item] лезем в факты каждого из них. Важная тонкость: чтобы факты соседа были в hostvars, на нём должен был отработать сбор фактов в этом же запуске. Если ты выключил gather_facts на веб-серверах, их ansible_default_ipv4 будет пустым - частые грабли при генерации конфигов через шаблоны.

А вот пример с inventory_hostname и group_names вместе, типичная развилка по роли узла:

Код: Выделить всё

- name: Открыть порт только на фронтендах
  ansible.posix.firewalld:
    service: https
    permanent: true
    immediate: true
    state: enabled
  when: "'webservers' in group_names"
Проверка 'webservers' in group_names - чистый и читаемый способ сказать "выполни это только на хостах из группы webservers", не плодя отдельные плеи.

Типичные грабли
  • Забыл про .results в цикле. Самая массовая ошибка. Есть loop у зарегистрированной задачи - значит верхнего .stdout нет, всё внутри списка results.
  • Падение до проверки when. Если регистрируешь команду, которая штатно возвращает ненулевой rc (rpm -q, grep, test), без failed_when: false плейбук упадёт раньше, чем дойдёт до твоего условия.
  • changed на read-only командах. command и shell всегда красятся жёлтым. Для проверок добавляй changed_when: false, иначе отчёт врёт и идемпотентность ломается.
  • hostvars без фактов. Лезешь в hostvars[other]['ansible_eth0'], а там пусто, потому что на том хосте не было gather_facts. Либо включи сбор фактов, либо собери их явной задачей setup.
  • Кавычки в when. Когда условие начинается со строки в кавычках ('webservers' in group_names), всё выражение нужно завернуть в кавычки целиком, иначе YAML спотыкается на двоеточии и одиночных кавычках.
Мини-лаба

Собери инвентарь с двумя группами - webservers (2 хоста) и lb (1 хост). Руками прогони:
  • Зарегистрируй вывод команды hostname -I и выведи через debug сначала всю переменную (var), потом только .stdout.
  • Сделай register на задаче с loop по трём пакетам (rpm -q ...), failed_when: false, и перебери .results, печатая item.item и item.rc.
  • На хосте из группы lb выведи через hostvars и groups IP-адреса всех машин из webservers.
  • Добавь задачу с firewalld, которая срабатывает только when 'webservers' in group_names. Прогони и убедись, что на lb она пропущена (skipped).
  • Запусти плейбук с -v и без, посмотри, как ведёт себя debug с verbosity: 1.
Контрольные вопросы
  • Чем отличается структура зарегистрированной переменной у обычной задачи и у задачи с loop? Где искать stdout в каждом случае?
  • Зачем нужен failed_when: false при register команды rpm -q, и что будет без него?
  • Что вернёт inventory_hostname и чем оно отличается от ansible_hostname из фактов?
  • Как с балансировщика получить факт (например IP) каждого веб-сервера, не заходя на него отдельным плеем?
Итог

register превращает Ansible из "запустил и забыл" в инструмент, принимающий решения: ловишь rc, stdout, changed и ветвишь логику через when - это рабочая лошадка и на проде, и на экзамене. Помни про .results в циклах и про failed_when при проверках. А magic-переменные - hostvars, groups, group_names, inventory_hostname, ansible_play_hosts - дают тебе вид сверху на весь инвентарь сразу, без чего нормальные конфиги через шаблоны не собрать. Дальше эти кирпичи лягут в шаблоны Jinja2 и роли.
👍3 ❤️ 🔥1 😄 🤔2
Аватара пользователя
snio
Сообщения: 1
Зарегистрирован: 13 май 2026, 07:34

Re: Регистрация результатов и специальные переменные

Сообщение snio »

Спасибо, наконец дошло про results. Час бился с ошибкой dict object has no attribute stdout после цикла, оказалось буквально про это в уроке написано.
👍3 ❤️1 🔥 😄 🤔
Аватара пользователя
billen
Сообщения: 1
Зарегистрирован: 23 май 2026, 02:56

Re: Регистрация результатов и специальные переменные

Сообщение billen »

А есть разница между ansible_play_hosts и ansible_play_hosts_all? У меня после падения одного хоста список схлопнулся, не сразу понял почему.
👍 ❤️1 🔥 😄 🤔
Ответить
← Предыдущая глава
Переменные Ansible: типы, объявление и приоритет
Следующая глава →
Ansible Vault: шифрование паролей и секретов

Все главы курса «Ansible: автоматизация и подготовка к RHCE (EX294)»

Поделиться темой: ✈ Telegram VK
Похожие запросы: ansible inventory и hosts файл

Вернуться в «Ansible: автоматизация и подготовка к RHCE (EX294)»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость