Циклы в Ansible: loop, списки и словари

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

Циклы в Ansible: loop, списки и словари

Сообщение 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
Представь задачу: создать десять пользователей, поставить пять пакетов, открыть три порта в firewalld. Если копировать одну и ту же таску десять раз, меняя только имя, плейбук превращается в простыню, которую невозможно читать и страшно править. Ошибся в одной строке - ищи потом, в какой именно из десяти. Тут и выходит на сцену ansible цикл: один блок логики прокручивается по списку значений, а ты пишешь его ровно один раз.

В этом уроке разберём, как устроен loop (современный способ), чем он отличается от старого with_items, как перебирать списки словарей, как ловить результаты через register и управлять выводом через loop_control. И обязательно поговорим про идемпотентность и грабли, на которых режутся на экзамене.

ansible loop: базовый перебор списка

Директива loop крепится к таске на том же уровне, что и имя модуля. Внутри таски доступна магическая переменная item - это текущий элемент списка на данной итерации. Простейший ansible loop пример выглядит так:

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

- name: Создать несколько каталогов
  ansible.builtin.file:
    path: "/opt/{{ item }}"
    state: directory
    mode: "0755"
  loop:
    - app
    - logs
    - backup
Таска выполнится три раза, и в item по очереди попадут app, logs и backup. Список можно не писать вручную, а подставить из переменной:

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

- name: Создать пользователей сервиса
  ansible.builtin.user:
    name: "{{ item }}"
    state: present
    shell: /sbin/nologin
  loop: "{{ service_users }}"
где service_users задан в group_vars или vars как обычный YAML-список. Это и есть суть циклов ansible - вынести данные отдельно от логики.

loop против with_items. Раньше тот же перебор писали через with_items, и ты до сих пор встретишь это в чужих ролях и в старых ответах на форумах. Запомни: ansible with_items и loop в простом случае со списком строк делают одно и то же. Разница в том, что with_* - семейство устаревших конструкций (with_dict, with_subelements, with_nested и так далее), а loop - единый современный механизм, который вместе с фильтрами Jinja покрывает все их сценарии. Новый код пиши на loop. Отдельный нюанс: with_items автоматически "разворачивал" вложенные списки на один уровень (flatten), а loop этого не делает - если нужно, добавь фильтр flatten явно: loop: "{{ mylist | flatten(levels=1) }}".

Изображение

Списки словарей и доступ к item.key

Строки - это разминка. На практике в одной итерации часто нужно несколько полей сразу: имя пользователя, его группа, комментарий. Для этого перебирают список словарей, а к полям обращаются через точку - item.name, item.group:

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

- name: Завести пользователей с группами
  ansible.builtin.user:
    name: "{{ item.name }}"
    group: "{{ item.group }}"
    comment: "{{ item.comment }}"
    state: present
  loop:
    - { name: deploy, group: wheel, comment: "CI deploy" }
    - { name: monitor, group: users, comment: "Zabbix agent" }
Так же удобно открывать порты в firewalld, когда у каждого порта свой протокол:

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

- name: Открыть нужные порты
  ansible.posix.firewalld:
    port: "{{ item.port }}/{{ item.proto }}"
    permanent: true
    immediate: true
    state: enabled
  loop:
    - { port: 80, proto: tcp }
    - { port: 443, proto: tcp }
    - { port: 53, proto: udp }
Перебор словаря через dict2items. Иногда данные приходят не списком, а словарём ключ-значение, и перебрать его напрямую loop не умеет - ему нужен список. Превращаем словарь в список пар фильтром dict2items, после чего у каждого элемента появляются поля key и value:

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

- name: Разложить конфиги из словаря
  ansible.builtin.copy:
    content: "{{ item.value }}"
    dest: "/etc/myapp/{{ item.key }}.conf"
    mode: "0644"
  loop: "{{ app_settings | dict2items }}"
  # app_settings:
  #   web: "listen 8080"
  #   db:  "max_conn 200"
loop с register, loop_control и вложенные циклы

Когда нужно поймать результат каждой итерации, навешиваем register. В отличие от обычной таски, ты получишь не один результат, а ключ results - список со словарём по каждой итерации. Дальше по этому списку можно пройти ещё одним циклом:

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

- name: Проверить наличие файлов
  ansible.builtin.stat:
    path: "/etc/{{ item }}"
  loop:
    - hosts
    - resolv.conf
    - fstab
  register: stat_out

- name: Показать, чего не хватает
  ansible.builtin.debug:
    msg: "Нет файла {{ item.item }}"
  loop: "{{ stat_out.results }}"
  when: not item.stat.exists
Обрати внимание на item.item во втором цикле: внешний item - это элемент results, а вложенный .item хранит исходное значение из первого прохода. Звучит запутанно, поэтому привыкай читать структуру register через debug.

loop_control - наводим порядок. У длинных словарей вывод цикла раздувается: Ansible печатает весь item целиком, и лог становится нечитаемым (а если в item лежат пароли - это ещё и утечка в консоль). Лечится через loop_control:

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

- name: Создать пользователей тихо
  ansible.builtin.user:
    name: "{{ item.name }}"
    password: "{{ item.pw | password_hash('sha512') }}"
    state: present
  loop: "{{ users }}"
  loop_control:
    label: "{{ item.name }}"     # в логе будет только имя
    index_var: idx               # номер итерации с нуля
    pause: 2                     # пауза 2 сек между итерациями
label задаёт, что показывать вместо громоздкого item, index_var даёт счётчик (удобно для нумерации), pause притормаживает между шагами - выручает, когда поднимаешь сервисы пачкой и не хочешь долбить API.

Вложенные циклы. Декартово произведение двух списков - например, выдать набор прав каждому пользователю - делается фильтром product. Старый аналог - with_nested:

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

- name: Дать права на каталоги
  ansible.builtin.file:
    path: "/srv/{{ item.0 }}/{{ item.1 }}"
    state: directory
  loop: "{{ ['team_a', 'team_b'] | product(['in', 'out']) | list }}"
  # item.0 - команда, item.1 - подкаталог
Когда внутри каждого словаря есть свой вложенный список (пользователь и его ключи SSH), берут фильтр subelements - современная замена with_subelements:

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

- name: Разложить SSH-ключи
  ansible.posix.authorized_key:
    user: "{{ item.0.name }}"
    key: "{{ item.1 }}"
  loop: "{{ users | subelements('keys') }}"
  # item.0 - сам пользователь, item.1 - один ключ из его списка keys
Идемпотентность и типичные грабли

Грабля номер один - цикл вокруг модуля, который сам умеет списки. Классика и любимый ловильный вопрос: установка пакетов через loop.

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

# ПЛОХО: dnf вызывается N раз, медленно, шумно
- ansible.builtin.dnf:
    name: "{{ item }}"
    state: present
  loop: [git, vim, htop]

# ХОРОШО: одна транзакция dnf, один проход
- ansible.builtin.dnf:
    name:
      - git
      - vim
      - htop
    state: present
Модули dnf (на RHEL/Fedora; на Debian/Ubuntu - apt, в RED OS тоже dnf, в Astra - apt), а также package, yum принимают список в параметре name напрямую и ставят всё одной транзакцией. Цикл тут не просто лишний - он в разы медленнее и портит вывод. То же касается ansible.builtin.user с несколькими группами, ansible.builtin.lineinfile частично и других модулей, у которых параметр и так список. Правило простое: прежде чем оборачивать модуль в loop, загляни в его документацию - вдруг нужный параметр уже принимает список.

Идемпотентность в циклах. Цикл сам по себе её не ломает и не чинит - всё держится на модуле внутри. Если на каждой итерации вызывается идемпотентный модуль (file, copy, user, firewalld), повторный прогон не наделает изменений и отрапортует ok. А вот если внутри loop стоит command или shell, который что-то дописывает в файл, - получишь N изменений при каждом запуске. Тогда либо добавляй creates/removes, либо защищайся через when, либо ищи нормальный модуль.

Ещё две частые ошибки. Первая - забыть, что при register с циклом результат лежит в .results, а не в корне переменной; обращение напрямую даёт undefined. Вторая - перепутать уровень отступа: loop должен быть на одном уровне с модулем (ключом таски), а не внутри параметров модуля, иначе Ansible его просто не увидит и ругнётся на синтаксис.

Связь с экзаменом RHCE (EX294)

Циклы на EX294 нужны постоянно: создать пачку пользователей из переменной, открыть несколько портов, разложить файлы. Пиши на loop - это текущий стандарт, его принимают без вопросов. Где режутся: оборачивают dnf в цикл (формально работает, но это антипаттерн и трата времени на экзамене с таймером), путаются в item.key против item.value при разборе словарей и забывают про .results после register. Потренируй эти три места до автоматизма - и блок с циклами на экзамене не отнимет лишних минут.

Мини-лаба: повтори руками
  • Напиши плейбук, который через loop создаёт три каталога в /opt и проверь идемпотентность: второй прогон должен дать changed=0.
  • Заведи список из трёх словарей (имя, группа, комментарий) и создай пользователей одной таской с ansible.builtin.user.
  • Возьми словарь port: proto и открой порты в firewalld, перебрав его через dict2items.
  • Добавь loop_control с label, чтобы в выводе светилось только имя пользователя, а не весь словарь. Сравни логи до и после.
  • Сделай stat по списку файлов с register, затем вторым циклом по .results выведи через debug только отсутствующие.
  • Намеренно оберни dnf в loop, замерь время, потом переделай на список в name - почувствуй разницу.
Контрольные вопросы
  • Чем loop отличается от with_items и почему в новом коде предпочитают loop?
  • Как из словаря ключ-значение получить список для перебора и как обратиться к ключу и значению?
  • Где лежат результаты итераций после register с циклом и как по ним пройти повторно?
  • Почему ставить пакеты через loop с dnf - плохая идея, и как сделать правильно?
Итог

loop - единый современный механизм перебора: списки строк, списки словарей (item.key), словари через dict2items, вложенность через product и subelements. register на цикле даёт .results, а loop_control с label/index_var/pause наводит порядок в выводе. Главное помни про идемпотентность и не оборачивай в цикл модули, которые сами едят списки. Старые with_* знать полезно для чтения чужого кода, но писать пора на loop.
👍2 ❤️4 🔥1 😄 🤔1
Аватара пользователя
andreev
Сообщения: 1
Зарегистрирован: 27 май 2026, 03:12

Re: Циклы в Ansible: loop, списки и словари

Сообщение andreev »

Спасибо, наконец дошло про item.item после register, всегда путался почему results а не сама переменная. А pause в loop_control реально между каждой итерацией ждет или только в конце?
👍2 ❤️1 🔥 😄 🤔1
Аватара пользователя
denomaster
Сообщения: 1
Зарегистрирован: 22 май 2026, 06:44

Re: Циклы в Ansible: loop, списки и словари

Сообщение denomaster »

Поправьте если не прав: я думал with_items быстрее на пакетах, а оказалось наоборот - dnf со списком в name одной транзакцией ставит. Переписал роль, прогон стал секунд на 20 короче.
👍2 ❤️2 🔥 😄 🤔
Ответить
← Предыдущая глава
Динамический инвентарь и инвентарные плагины
Следующая глава →
Повтор задачи до условия: until, retries и delay

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

Поделиться темой: ✈ Telegram VK
Похожие запросы: что такое ansible простыми словамиansible для начинающих с чего начатьустановка ansible на linuxansible inventory и hosts файлчто такое playbook в ansibleструктура ansible playbook из чего состоит

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

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

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