Ansible Execution Environment: Optimizing Automation with Containers
A practical guide to creating Ansible Execution Environments using ansible-builder and Red Hat UBI 9, solving the problem of inconsistent dependencies.
Jean Paul López
The Story of a Common Problem
Imagine this situation: you’ve just written a perfect Ansible playbook on your local machine. It works beautifully. You test it over and over, and everything goes well. The moment comes to run it in production and… surprise! It fails because the server has a different Python version, is missing modules, or has incompatible dependencies.
If this sounds familiar, you’re not alone. It’s one of the most common headaches in the world of Ansible automation. And this is precisely the problem that Ansible Execution Environment (EE) comes to solve.
What Exactly is an Execution Environment?
Think of an Execution Environment as a “space capsule” for your Ansible playbooks. It’s a container that carries everything needed to run your automations: the correct Python version, all necessary modules, the Ansible collections you use, and any additional tools you require.
The big difference is that, while before you had to ensure each server had the correct environment configured (and keep it updated), now you simply run your container and you’re done.
Why Does This Change Everything?
1. Total Consistency Across Environments
No more “works on my machine.” Your playbook will run exactly the same in development, staging, and production. The same Python version, the same libraries, the same results.
2. Collaboration Without Headaches
When you share an EE with your team, everyone works with the same environment. No more wasting hours configuring dependencies or resolving version conflicts.
3. Multiple Versions Without Conflicts
Does a legacy playbook need Python 2.7 while another requires Python 3.11? No problem. Each EE is independent and can have its own versions of everything.
4. Enterprise Ready
If your company uses or plans to use Ansible Automation Platform, EEs are the standard. They facilitate governance, compliance, and scalability of your automations.
Getting Started: Creating Your First Execution Environment
Let’s create an EE using the latest version of ansible-builder. This guide has been tested and works perfectly with Red Hat UBI 9.
Step 1: Install the Necessary Tools
First, we need ansible-builder:
# Install the latest version of ansible-builder
pip install ansible-builder --upgrade
# Verify you have version 3.x
ansible-builder --version
# You'll also need Podman or Docker
# On Fedora/RHEL:
sudo dnf install podman
# On Ubuntu/Debian:
sudo apt install podman
Step 2: Create the Project Structure
Let’s create all the necessary files at once. Create a directory for your project:
mkdir my-ee-ubi9 && cd my-ee-ubi9
Step 3: Create the Main execution-environment.yml File
This is the heart of your EE. Create the file with this content:
version: 3
build_arg_defaults:
ANSIBLE_GALAXY_CLI_COLLECTION_OPTS: '--pre'
dependencies:
ansible_core:
package_pip: ansible-core==2.14.4
ansible_runner:
package_pip: ansible-runner
galaxy: requirements.yml
python: requirements.txt
system: bindep.txt
exclude:
python:
- docker
system:
- python3-Cython
images:
base_image:
name: docker.io/redhat/ubi9:latest
additional_build_files:
- src: files/ansible.cfg
dest: configs
additional_build_steps:
prepend_base:
- RUN echo "Starting build process..."
- RUN dnf install -y python3-pip
prepend_galaxy:
- COPY _build/configs/ansible.cfg /etc/ansible/ansible.cfg
prepend_final: |
RUN whoami
RUN cat /etc/os-release
append_final:
- RUN echo "Build completed!"
- RUN dnf clean all
- RUN rm -rf /var/cache/dnf
Step 4: Create System Dependencies (bindep.txt)
This file lists the operating system packages you need:
# Compilation
gcc [compile platform:rpm]
make [compile platform:rpm]
# Version control
git [platform:rpm]
# Network tools
iputils [platform:rpm]
net-tools [platform:rpm]
openssh-clients [platform:rpm]
# Python development
python3-devel [platform:rpm]
python3-cffi [platform:rpm]
python3-cryptography [platform:rpm]
python3-lxml [platform:rpm]
python3-pycparser [platform:rpm]
# SSH and connectivity
libssh-devel [platform:rpm]
sshpass [platform:rpm]
# Utilities
rsync [platform:rpm]
unzip [platform:rpm]
tar [platform:rpm]
Step 5: Create Ansible Collections (requirements.yml)
Define which Ansible collections you need:
---
collections:
- name: ansible.posix
version: ">=1.5.0"
- name: community.general
version: ">=7.0.0"
- name: ansible.utils
version: ">=2.9.0"
- name: community.crypto
version: ">=2.11.0"
Step 6: Create Python Dependencies (requirements.txt)
List the necessary Python packages:
# Core
six>=1.16.0
psutil>=5.9.0
# Networking
paramiko>=2.12.0
netaddr>=0.8.0
# Utilities
jinja2>=3.1.0
PyYAML>=6.0
requests>=2.28.0
urllib3>=1.26.0
Step 7: Create Ansible Configuration
First create the directory:
mkdir -p files
Then create files/ansible.cfg:
[defaults]
host_key_checking = False
retry_files_enabled = False
stdout_callback = yaml
interpreter_python = auto_silent
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible-facts
fact_caching_timeout = 3600
[ssh_connection]
pipelining = True
control_path = /tmp/ansible-ssh-%%h-%%p-%%r
[inventory]
enable_plugins = auto, yaml, ini, toml
Step 8: Build Your Execution Environment
Now comes the moment of truth:
ansible-builder build -t my-ee:latest
The process will take a few minutes the first time. You’ll see how:
- The UBI 9 base image is downloaded
- System packages are installed
- Python dependencies are configured
- Ansible collections are downloaded
- Custom configurations are applied
Step 9: Verify and Use Your EE
Verify it was built correctly:
# See the created image
podman images | grep my-ee
# See which collections are installed
podman run --rm my-ee:latest ansible-galaxy collection list
# See the Ansible version
podman run --rm my-ee:latest ansible --version
# See which Python modules are installed
podman run --rm my-ee:latest pip list
Use your EE to run playbooks:
# Install ansible-navigator if you don't have it
pip install ansible-navigator
# Run a playbook
ansible-navigator run site.yml --execution-environment-image my-ee:latest
Troubleshooting: Common Problems and Their Solutions
”The build fails with package not found error”
This is the most common error when using UBI. Red Hat Universal Base Image has limited repositories by design.
Quick solution: Check which package fails and:
- Look for it with an alternative name
- Remove it from
bindep.txtif it’s not critical - Install it from source in
additional_build_steps
Real example: If openshift-clients is not available:
additional_build_steps:
prepend_base:
- RUN curl -L https://mirror.openshift.com/pub/openshift-v4/clients/ocp/latest/openshift-client-linux.tar.gz | tar xz -C /usr/local/bin
“My EE weighs more than 1GB”
EEs can inflate quickly, especially with multiple collections and dependencies.
Optimization strategy:
- Review what you really need - less is more
- Clean up after installing:
additional_build_steps:
append_final:
- RUN dnf clean all && rm -rf /var/cache/dnf
- RUN find /usr/local -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
- RUN find /usr/local -type f -name "*.pyc" -delete
- RUN rm -rf /root/.cache /tmp/*
“Permission denied when mounting volumes”
The classic SELinux problem on Red Hat systems.
Solution: Always use the :Z option when mounting:
# Correct
podman run -v $(pwd):/runner:Z my-ee:latest
Best Practices
- Version your EEs: Use semantic tags like
v1.0.0, not justlatest - Document dependencies: Comment why you include each package
- Keep EEs specific: Create different EEs for different purposes
- Automate builds: Integrate the build into your CI/CD
- Test regularly: Validate that your playbooks work with the EE
Conclusion
Ansible Execution Environments represent a fundamental shift in how we handle dependencies and portability in Ansible. With this guide, you have everything you need to create robust and reproducible EEs based on Red Hat UBI 9.
The key is to start simple: create your first EE with minimal dependencies, test it, and add what you need. In no time, you’ll wonder how you survived without them.
Additional Resources
- Official Ansible Builder Documentation
- Ansible Navigator Documentation
- Red Hat UBI Documentation
- EE Examples on GitHub
Have you created your first Execution Environment? What problems did you solve? What customizations did you add?
Comments (0)