Что такое Execution Environment и зачем он нужен
Execution Environment (EE) - это контейнерный образ, внутри которого упакован весь стек, нужный для запуска Ansible: сам ansible-core, ansible-runner, набор коллекций, Python-зависимости и системные пакеты. Идея простая до гениальности: вместо того чтобы тащить и синхронизировать всё это по куче управляющих машин, ты собираешь один образ и запускаешь Ansible ВНУТРИ него. Образ один и тот же у тебя на ноуте, у коллеги, на CI и в проде. Версии зафиксированы, зависимости вмёрзли в слои контейнера. Плейбук теперь работает одинаково везде - потому что везде один и тот же execution environment ansible.
Раньше эту роль играл хост: ставишь ansible через dnf или pip, докидываешь коллекции через ansible-galaxy, ставишь python-зависимости в systemwide или venv. Хрупкая конструкция. Обновил одну библиотеку для другого проекта - сломал три плейбука. EE решает это через изоляцию: окружение - это артефакт, который ты версионируешь и распространяешь как любой другой контейнер. По сути ansible контейнер становится единицей поставки автоматизации, ровно как Docker-образ - единицей поставки приложения.
Важно: для Ansible Automation Platform (AAP 2.x) EE - это не опция, а основа исполнения. Automation Controller (бывший Tower) вообще не умеет запускать джобы иначе как внутри execution environment. Так что если ты целишься в энтерпрайз с AAP - тема обязательная.EE - это для Ansible примерно то же, чем стал контейнер для приложений: "работает на моей машине" перестаёт быть оправданием, потому что машина теперь едет вместе с кодом.

Из чего собран ansible ee: разбираем по слоям
Образ EE строится инструментом ansible-builder из декларативного файла execution-environment.yml (актуальная схема - version 3). Он описывает четыре кита окружения:
- Базовый образ - от чего наследуемся. Red Hat даёт официальные ee-minimal и ee-supported, но можно взять и чистую базу вроде registry.fedoraproject.org/fedora или ubi9.
- ansible-core - версия движка. Ставится через pip, версию фиксируешь явно.
- Коллекции - описываются в requirements.yml (формат ansible-galaxy). Сюда складываешь community.general, ansible.posix, kubernetes.core и прочее.
- Python-зависимости - requirements.txt. Все pip-пакеты, которые нужны модулям: jmespath для json_query, pyvmomi для VMware, kubernetes для k8s.
- Системные пакеты - bindep.txt в формате bindep. Сюда идут rpm/deb уровня ОС: openssh-clients, sshpass, git, gcc для сборки нативных питоновских колёс.
Код: Выделить всё
version: 3
images:
base_image:
name: registry.redhat.io/ansible-automation-platform-25/ee-minimal-rhel9:latest
dependencies:
ansible_core:
package_pip: ansible-core==2.16.*
ansible_runner:
package_pip: ansible-runner
python: requirements.txt
galaxy: requirements.yml
system: bindep.txt
options:
package_manager_path: /usr/bin/microdnf
Код: Выделить всё
---
collections:
- name: ansible.posix
version: ">=1.5.0"
- name: community.general
- name: kubernetes.core
Код: Выделить всё
kubernetes>=24.2.0
jmespath
Код: Выделить всё
git [platform:rpm]
openssh-clients [platform:rpm]
sshpass [platform:rpm]
Код: Выделить всё
ansible-builder build --tag my-ee:1.0 -f execution-environment.yml -v 3
Практика: запускаем плейбук в execution environment
Запускать плейбук в EE удобнее всего через ansible-navigator - это современная замена ansible-playbook, которая умеет работать с контейнерами из коробки. Простейший плейбук test.yml:
Код: Выделить всё
---
- name: Проверка идемпотентной настройки
hosts: web
become: true
tasks:
- name: Установить nginx
ansible.builtin.dnf:
name: nginx
state: present
- name: Открыть http в firewalld
ansible.posix.firewalld:
service: http
permanent: true
state: enabled
immediate: true
- name: Положить конфиг
ansible.builtin.copy:
src: nginx.conf
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: "0644"
Код: Выделить всё
ansible-navigator run test.yml \
--execution-environment-image my-ee:1.0 \
--mode stdout \
--pull-policy missing
Код: Выделить всё
ansible-navigator images --execution-environment-image my-ee:1.0
Типичные грабли
- Забыл version: 3. Если в execution-environment.yml нет строки version, ansible-builder считает, что это схема v1, и валится на незнакомых ключах. Всегда указывай версию явно.
- Коллекция есть, а модуль падает. Классика: положил kubernetes.core в requirements.yml, а питоновский пакет kubernetes в requirements.txt не добавил. Коллекция - это обёртка, ей нужны python-библиотеки. ansible-builder подтягивает зависимости коллекций автоматически, но не всегда полно - проверяй руками.
- Нет компилятора для нативных колёс. Часть pip-пакетов собирается из исходников. Нет gcc и python3-devel в bindep.txt - сборка образа падает с невнятной ошибкой про wheel. Докинь их в системные зависимости.
- SELinux и тома. Когда монтируешь рабочую директорию в контейнер на RHEL, без правильной метки (:Z) SELinux заблокирует доступ. navigator обычно метит сам, но если используешь podman run напрямую - помни про :Z.
- pull-policy always на медленном канале. По умолчанию navigator может лезть в реестр каждый запуск. Для локального образа ставь --pull-policy missing, иначе будешь ждать сеть на ровном месте.
Повтори руками, без этого тема не уляжется:
- Поставь инструменты: dnf install ansible-builder ansible-navigator (или pip в venv).
- Создай каталог проекта, положи в него execution-environment.yml (schema v3), requirements.yml с community.general, requirements.txt с jmespath, bindep.txt с git.
- Собери образ: ansible-builder build --tag lab-ee:1.0 -v 3. Дождись успеха и найди образ в podman images.
- Напиши плейбук с одной задачей через community.general (например, community.general.timezone) и фильтром json_query (он требует jmespath - так проверишь, что python-зависимость уехала в образ).
- Запусти через ansible-navigator run ... --eei lab-ee:1.0 --mode stdout. Прогони дважды - убедись, что второй прогон идемпотентен (changed=0).
- Глянь содержимое: ansible-navigator images --eei lab-ee:1.0 и проверь версию ansible-core.
- Какие четыре класса зависимостей описывает execution-environment.yml и в каких файлах живут коллекции, python- и системные пакеты?
- Чем грозит отсутствие строки version: 3 в определении EE и почему?
- Зачем коллекции kubernetes.core нужен ещё и pip-пакет kubernetes, и где его прописать?
- Каким флагом ansible-navigator указываешь, какой execution environment использовать для прогона?
Execution Environment превращает капризное "у меня работает" в воспроизводимый артефакт: один контейнерный образ с зафиксированными ansible-core, коллекциями и зависимостями ездит вместе с твоим кодом по всем машинам и пайплайнам. Собираешь его через ansible-builder из execution-environment.yml (schema v3), запускаешь через ansible-navigator с флагом --eei. Для AAP это фундамент, а на современном RHCE (версия EX294 на RHEL 9 c AAP 2.x) понимание EE и ansible-navigator - уже часть программы, а не экзотика. Разберёшься здесь - перестанешь ловить плавающие баги окружения навсегда.