Execution Environments

What are Execution Environments?

Execution Environments (EEs) are container images that package Ansible, collections, Python dependencies, and system packages into a portable, reproducible runtime environment. They solve dependency conflicts and ensure consistent automation across different systems.

Key Benefits:
  • Reproducibility: Same environment everywhere (dev, test, prod)
  • Isolation: No conflicts with system Python or packages
  • Portability: Run anywhere containers are supported
  • Versioning: Pin exact versions of Ansible and collections

Core Components

ansible-builder

Tool for creating Execution Environment container images

ansible-navigator

Command-line interface for running playbooks and ad-hoc commands inside Execution Environments

ansible-runner

Python library and CLI for interfacing with Ansible, designed to work with Execution Environments

Installation

# Install ansible-builder
pip install ansible-builder

# Install ansible-navigator
pip install ansible-navigator

# Install ansible-runner (optional)
pip install ansible-runner

# Verify installations
ansible-builder --version
ansible-navigator --version

Building Execution Environments

Basic Structure

# execution-environment.yml
---
version: 3

images:
  base_image:
    name: quay.io/ansible/ansible-runner:latest

dependencies:
  galaxy: requirements.yml
  python: requirements.txt
  system: bindep.txt

additional_build_steps:
  prepend_base:
    - RUN echo "Custom build step"
  append_final:
    - RUN chmod -R ug+rw /runner

Dependencies Files

# requirements.yml - Ansible collections
---
collections:
  - name: community.general
    version: ">=5.0.0"
  - name: ansible.posix
  - name: community.docker

# requirements.txt - Python packages
jinja2>=3.0
netaddr
requests>=2.28
boto3

# bindep.txt - System packages
python3-dev [platform:rpm]
python3-devel [platform:dpkg]
git [platform:rpm platform:dpkg]
rsync [platform:rpm platform:dpkg]

Building the Image

# Build with default settings
ansible-builder build --tag my-ee:latest

# Build with custom name
ansible-builder build \
  --tag my-organization/my-ee:1.0.0 \
  --container-runtime podman

# Build with verbose output
ansible-builder build --tag my-ee:latest -v 3

# Build and push to registry
ansible-builder build --tag quay.io/myuser/my-ee:1.0.0
podman push quay.io/myuser/my-ee:1.0.0

Using ansible-navigator

Configuration

# ansible-navigator.yml
---
ansible-navigator:
  execution-environment:
    image: my-ee:latest
    pull:
      policy: missing
    container-engine: podman
    enabled: true
    volume-mounts:
      - src: /path/on/host
        dest: /path/in/container
    environment-variables:
      pass:
        - HOME
        - USER
      set:
        ANSIBLE_FORCE_COLOR: 'true'

  logging:
    level: info
    file: /tmp/navigator.log

  playbook-artifact:
    enable: true
    save-as: /tmp/playbook-artifacts/{playbook_name}-{ts_utc}.json

Running Playbooks

# Run playbook with default EE
ansible-navigator run playbook.yml

# Run with specific EE image
ansible-navigator run playbook.yml \
  --execution-environment-image my-ee:latest

# Run in interactive mode
ansible-navigator run playbook.yml --mode interactive

# Run in stdout mode (traditional output)
ansible-navigator run playbook.yml --mode stdout

# Pass extra vars
ansible-navigator run playbook.yml \
  -e "version=1.2.3" \
  -e "environment=production"

# Use specific inventory
ansible-navigator run playbook.yml \
  -i inventory/production

# Disable EE (use local Ansible)
ansible-navigator run playbook.yml \
  --execution-environment false

Interactive Features

# Start in interactive mode
ansible-navigator

# Available actions:
# :run playbook.yml         - Run a playbook
# :collections              - Browse installed collections
# :config                   - View Ansible configuration
# :doc module_name          - View module documentation
# :inventory                - Explore inventory
# :images                   - List EE images
# :quit or :q               - Exit

# Example session
ansible-navigator
:doc ansible.builtin.copy
:collections
:run site.yml

Example Execution Environment Definitions

Web Application Deployment

# execution-environment.yml
---
version: 3

images:
  base_image:
    name: quay.io/ansible/ansible-runner:latest

dependencies:
  galaxy: |
    ---
    collections:
      - name: community.general
      - name: ansible.posix
      - name: community.docker

  python: |
    docker
    kubernetes
    openshift
    jinja2>=3.0
    netaddr

  system: |
    git [platform:rpm platform:dpkg]
    python3-dev [platform:dpkg]
    python3-devel [platform:rpm]

additional_build_steps:
  append_final:
    - COPY custom_plugins /usr/share/ansible/plugins/
    - RUN chmod -R 755 /usr/share/ansible/plugins

Cloud Infrastructure

# execution-environment.yml
---
version: 3

images:
  base_image:
    name: quay.io/ansible/ansible-runner:latest

dependencies:
  galaxy: |
    ---
    collections:
      - name: amazon.aws
      - name: azure.azcollection
      - name: google.cloud
      - name: community.general

  python: |
    boto3>=1.26
    botocore>=1.29
    azure-cli-core
    google-auth
    requests

  system: |
    git
    jq
    aws-cli [platform:rpm]

Network Automation

# execution-environment.yml
---
version: 3

images:
  base_image:
    name: quay.io/ansible/ansible-runner:latest

dependencies:
  galaxy: |
    ---
    collections:
      - name: cisco.ios
      - name: cisco.nxos
      - name: junipernetworks.junos
      - name: arista.eos

  python: |
    netaddr
    jxmlease
    paramiko
    ncclient
    textfsm

  system: |
    openssh-client

Advanced Features

Multi-Stage Builds

# execution-environment.yml
---
version: 3

build_arg_defaults:
  ANSIBLE_GALAXY_CLI_COLLECTION_OPTS: '--pre'

images:
  base_image:
    name: quay.io/ansible/ansible-runner:latest

dependencies:
  galaxy: requirements.yml
  python: requirements.txt
  system: bindep.txt

additional_build_steps:
  prepend_base:
    - RUN whoami
    - RUN cat /etc/os-release

  prepend_galaxy:
    - RUN ansible-galaxy collection list

  append_final:
    - RUN echo "Build complete"
    - RUN ls -la /usr/share/ansible/collections

Private Collection Repositories

# execution-environment.yml with private repos
---
version: 3

images:
  base_image:
    name: quay.io/ansible/ansible-runner:latest

dependencies:
  galaxy: |
    ---
    collections:
      - name: my_namespace.my_collection
        source: https://private-galaxy.company.com

additional_build_steps:
  prepend_galaxy:
    - COPY ansible.cfg /etc/ansible/ansible.cfg
    - COPY galaxy_token /tmp/galaxy_token

  append_final:
    - RUN rm /tmp/galaxy_token

ansible-runner Integration

# Python script using ansible-runner
import ansible_runner

# Run playbook
r = ansible_runner.run(
    private_data_dir='/tmp/demo',
    playbook='playbook.yml',
    inventory='inventory',
    container_image='my-ee:latest',
    container_runtime='podman',
    envvars={
        'ANSIBLE_FORCE_COLOR': 'true'
    }
)

# Check results
print(f"Status: {r.status}")
print(f"Return code: {r.rc}")

# Access events
for event in r.events:
    print(event['stdout'])

CI/CD Integration

GitHub Actions

# .github/workflows/ee.yml
name: Build Execution Environment

on: [push]

jobs:
  build-ee:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.10'

      - name: Install ansible-builder
        run: pip install ansible-builder

      - name: Build EE
        run: ansible-builder build --tag my-ee:${{ github.sha }}

      - name: Login to registry
        run: echo "${{ secrets.REGISTRY_TOKEN }}" | \
          podman login -u ${{ secrets.REGISTRY_USER }} --password-stdin quay.io

      - name: Push to registry
        run: |
          podman tag my-ee:${{ github.sha }} quay.io/myorg/my-ee:latest
          podman push quay.io/myorg/my-ee:latest

Using EE in GitLab CI

# .gitlab-ci.yml
stages:
  - build
  - deploy

build-ee:
  stage: build
  image: quay.io/ansible/ansible-builder:latest
  services:
    - docker:dind
  script:
    - ansible-builder build --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

deploy:
  stage: deploy
  image: quay.io/ansible/ansible-navigator:latest
  script:
    - ansible-navigator run playbook.yml \
        --execution-environment-image $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

Best Practices

  1. Version Everything: Pin versions of Ansible, collections, and dependencies
  2. Tag Images: Use semantic versioning for EE images
  3. Minimize Size: Only include required dependencies
  4. Layer Efficiently: Order build steps to maximize cache hits
  5. Test Locally: Build and test EEs before CI/CD
  6. Document Dependencies: Comment why each dependency is needed
  7. Use Base Images: Start from official ansible-runner images
  8. Security Scan: Scan images for vulnerabilities

Troubleshooting

Common Issues:
  • Build Failures: Check requirements file syntax and network connectivity
  • Module Not Found: Ensure collection is in requirements.yml
  • Permission Errors: Verify volume mounts and user permissions
  • Large Image Size: Remove unnecessary dependencies
  • Slow Builds: Use container registry caching

Debugging Build Process

# Build with verbose output
ansible-builder build -v 3 --tag debug-ee:latest

# Inspect build context
ansible-builder create

# Check generated files
ls -la context/
cat context/Containerfile

# Build manually with podman/docker
cd context
podman build -t manual-ee:latest .

Inspecting Running Containers

# Run interactive shell in EE
ansible-navigator exec -- /bin/bash

# Check installed collections
ansible-navigator exec -- ansible-galaxy collection list

# Check Python packages
ansible-navigator exec -- pip list

# Check system packages
ansible-navigator exec -- rpm -qa  # or dpkg -l

Migration from Traditional Ansible

# Traditional approach
ansible-playbook -i inventory playbook.yml

# Execution Environment approach
ansible-navigator run playbook.yml -i inventory \
  --execution-environment-image my-ee:latest

# Hybrid approach (transition period)
ansible-navigator run playbook.yml \
  --execution-environment false  # Uses local Ansible

Quick Reference

# ansible-builder
ansible-builder build --tag my-ee:latest               # Build EE
ansible-builder create                                 # Create build context
ansible-builder build -v 3                             # Verbose build

# ansible-navigator
ansible-navigator run playbook.yml                     # Run playbook
ansible-navigator --mode interactive                   # Interactive mode
ansible-navigator doc module_name                      # Module docs
ansible-navigator collections                          # Browse collections
ansible-navigator inventory                            # Explore inventory
ansible-navigator config                               # View config

# EE image management
podman images                                          # List images
podman pull quay.io/ansible/ansible-runner:latest     # Pull image
podman push my-registry.com/my-ee:latest              # Push image
podman inspect my-ee:latest                           # Inspect image

Next Steps