Ansible Execution Environment is a containerized environment that provides a consistent way to execute Ansible playbooks. It is a key component of the Ansible Automation Platform (AAP), however, its capabilities extend beyond AAP, and it can be utilized independently.
Usually, there is need to support variety of distros like Solaris, Linux, Oracle Virtual Server, etc. This includes support of older releases such as Solaris 10 and Linux 5. Likely this is limited to use Ansible 2.12.10, as newer Ansible versions are not compatible with these legacy systems.
The challenge is not being able to upgrade to newer Ansible versions, which means you are missing out on new features and improvements. To address this, the plan is to leverage Ansible Execution Environment, and create separate environments with different Ansible versions.
For example, Ansible 2.12.10 environment can be used to manage hosts in some Sustaining mode (ex. Solaris 10, Linux 5-7). Additionally, you can have environment with the latest Ansible version, that will be used to manage newer OS releases. This allows use of latest ansible modules and features.
You can run Ansible automation in containers, like any other application. Ansible uses container images known as Execution Environments (EE) that act as control nodes. The EE remove complexity to scale out automation projects.
Ansible EE is a container image (built using ansible-builder CLI) that bundles Ansible, its dependencies, and any additional requirements needed to execute Ansible playbooks.
The EE image contains the following packages as standard:
The ansible-builder CLI is used to configure and build images, that can be used by containerization tools like Podman. These images are then used to run containers, and are known as Ansible EE.
This is the YAML file, where you define your EE. By default, it's named execution-environment.yml. It tells ansible-builder how to creates build instruction file (Containerfile for Podman), and use that file to build context for the image.
NOTE: ansible-builder v3 is the latest one, do not use previous version!$ pip3 show ansible-builder Name: ansible-builder Version: 3.1.0 Summary: "A tool for building Ansible Execution Environments" Home-page: https://ansible-builder.readthedocs.io Author: Ansible, Inc. Author-email: info@ansible.com License: Apache Software License, Version 2.0 Location: /root/virtenv/ansible-builder-3.1.0/lib/python3.9/site-packages Requires: bindep, jsonschema, packaging, PyYAML |
--- # File: execution-environment.yml # schema for ansible-builder 3 version: 3 images: base_image: name: container-registry.oracle.com/os/oraclelinux:9-slim # Can't use it here, the base image doesn't have python #options: # package_manager_path: /usr/bin/microdnf dependencies: # Install ansible collections galaxy: requirements.yml # Python dependency installed with pip python: requirements.txt # Binary (OS) dependency installed with yum/dnf system: bindep.txt # pip install these ansible_core: package_pip: ansible-core==2.12.10 ansible_runner: package_pip: ansible-runner # src is file in same folder as execution-environment.yml # dest is sub-folder under _build additional_build_files: - { src: ansible.cfg, dest: configs } # Building layers: base, galaxy, builder (not user accessible), final. additional_build_steps: # Commands to run before building base leyer, usually OS pkgs install prepend_base: - RUN >- microdnf install -y python3 python3-devel python3-pip dnf crypto-policies-scripts && python3 -m pip install --upgrade pip --user && update-crypto-policies --set LEGACY # Customize galaxy setup/install prepend_galaxy: - COPY _build/configs/ansible.cfg /etc/ansible/ansible.cfg # Customize runtime environment append_final: - COPY _build/configs/ansible.cfg /etc/ansible/ansible.cfg ... |
--- # File: requirements.yml # Collections installed with ansible-galaxy # ansible.posix latest doesn't support ansible-core 2.12.10 collections: - name: ansible.netcommon - name: ansible.posix version: 1.5.4 - name: ansible.utils - name: ansible.windows - name: community.crypto - name: community.dns - name: community.general - name: community.libvirt - name: community.mysql - name: community.network - name: community.postgresql - name: containers.podman - name: oracle.oci - name: ovirt.ovirt ... |
# File: requirements.txt # Python modules installed with pip ansible-lint==5.4 |
# File: bindep.txt # Binary dependency, installed with yum/dnf sshpass git |
# Build the image # Use verbosity level 3 # Specify image tag $ ansible-builder build -v 3 --tag ol9-core2.12.10 ... Expect successful completion with output: [3/3] COMMIT ansible-execution-env:latest --> 93ec8f24cbc2 Successfully tagged localhost/ol9-ansible-core2.12.10:latest 93ec8f24cbc2d4bd0348ac424d72b87686e6f79696a985e8c0b078cf2a4a40b5 # Run podman images to review images on your host. |
Simple playbook to report distro version of remote host, and ping it.
--- # Simple playbook to report distro version of remote host, and ping it. - name: Play hosts: all gather_facts: true tasks: - name: Debug ansible.builtin.debug: msg: "{{ ansible_distribution }}_{{ ansible_distribution_version }}" - name: Connection check ansible.builtin.ping: ... |
Start container from image, and run playbook.
# Mount local folder to containers' /runner # In this folder, you have your inventory and playbook # Remove/delete container after run with --rm # Interactive terminal is option -it $ podman run --rm -it -v $PWD:/runner localhost/ol9-core2.12.10 ansible-playbook -i inventory playbook.yml |
When it comes to distributing Podman images among multiple hosts, the native approach is to utilize a container registry, like OCIR. The OCIR serves as a centralized repository for storing and managing container images.
However, in environments where a container registry is not available, alternative methods can be employed. One such approach is to use the Podman CLI to manually distribute images.
Save podman image as tar archive.
# List images $ podman images REPOSITORY TAG IMAGE ID CREATED SIZE localhost/ol8-python39-core2.12.10 latest e7848f65da2a 9 days ago 1.44 GB localhost/ol8-python38-core2.12.10 latest 60a3bf2e5540 2 weeks ago 1.43 GB <- save it # Save the image (ex. ol8-python38-core2.12.10), use oci-archive format, and specify output name. $ podman save --format oci-archive -o ee-ol8-core21210 60a3bf2e5540 Copying blob 1dcf70a42af2 done | Copying blob 97779b2da816 done | Copying config 60a3bf2e55 done | Writing manifest to image destination # Verify the result. $ ls -lah 8-38-21210 -rw-r--r-- 1 root root 583M Jul 3 08:41 ee-ol8-core21210 |
Import podman image on another host.
# Copy image to another host $ scp ee-ol8-core21210 another-host.domain.com:/tmp/ # On another host, load the image $ cd /tmp $ podman load -i ee-ol8-core21210 Getting image source signatures Copying blob 93388424bfae done | Copying config 60a3bf2e55 done | Writing manifest to image destination Loaded image: sha256:60a3bf2e554057f6d8eadeafc76ee1de7c29759149fa83777cb7698278dfffa1 # List images $ podman images REPOSITORY TAG IMAGE ID CREATED SIZE ee-ol8-core21210 none 60a3bf2e5540 2 weeks ago 1.43 GB # Verify the image # Start container, as root, and run command to verify OL and Ansible versions. $ podman run -u root --rm -it 60a3bf2e5540 bash-4.4# cat /etc/oracle-release Oracle Linux Server release 8.10 bash-4.4# ansible --version ansible [core 2.12.10] python version = 3.8.17 (default, Oct 25 2023, 04:18:23) [GCC 8.5.0 20210514 (Red Hat 8.5.0-20.0.1)] jinja version = 3.1.6 |