invoke ansible execution environments within a container
Over time you may notice your Ansible control server requires more and more specialized setup: the Ansible version, the pip modules your collections depend on, maybe even custom collections and custom modules that require their own custom libraries installed at various specific versions.
If this growth of complexity sounds familiar maybe you could benefit from Execution Environments. Execution Environments are basically a standard way of building an Ansible control server environment in a Docker image: https://docs.ansible.com/ansible/latest/getting_started_ee/index.html
Now, nothing stops you from building your own custom Docker image with Ansible and all your custom playbook dependencies installed, but tapping into the official Ansible method for doing this has its advantages. The most obvious one is that newer generations of Ansible CLI tools (like ansible-navigator
and ansible-runner
) have native support for running playbooks within Execution Environments. For example:
ansible-navigator run --inventory MyTargetSystems.yml --pp missing --execution-environment-image ansible-execution-env:latest my_playbook.yml --vault-password-file ~/my_password -m stdout
But what if you want to take things a step further and containerize the playbooks and inventory files themselves too? Why would you do this? Well, imagine the playbook targets themselves where containerized and your only interface into running your playbooks was just a Docker compose file…
If that scenario appies to you, or you’re just interested in invoking Ansible Execution Environments from inside a Docker container, here’s a few of my findings for making this work:
Finding 1 - Sharing the Docker Socket
You don’t need to install Docker in your “main” container (as opposed to your Execution Environment container). You can simply pass the host Docker socket through as a mount and install the Docker CLI (docker-ce-cli
on RedHat/Rocky):
volumes:
- /var/run/docker.sock:/var/run/docker.sock
Now when you start a container from your main container it’s started on your Docker host which keeps things “flat”, that is, no nested containers.
Finding 2 - Ansible Navigator Config File
Ansible Navigator seems a little half baked at the moment. Many of the documented CLI options don’t really work. For example, I couldn’t get any of the --container-options
working. They do appear to work when provided via ansible-navigator.yml
though.
Here’s what my ansible-navigator.yml
ended up looking like:
ansible-navigator:
ansible:
inventory:
entries:
- /tmp/ee/MyTargetSystems.yml
playbook:
path: /tmp/ee/my_playbook.yml
execution-environment:
image: artprod.dev.bloomberg.com/broadway/deployment-engineering/ansible-execution-env
container-options:
- "--volume=my_project_playbook_project:/tmp/ee"
- "--workdir=/tmp/ee"
- "--env=ANSIBLE_STDOUT_CALLBACK=yaml"
- "--network=host"
mode: stdout
Finding 3 - Working Around Ansible Navigator Half-Bakedness
I had to pass through --env=ANSIBLE_STDOUT_CALLBACK=yaml
via container options. My ansible.cfg
option was ignored:
[defaults]
stdout_callback = yaml
Without this setting the execution environment failed with the message: ERROR! Invalid callback for stdout specified: awx_display
. It seems that currently there are hard-coded assumptions that you are using AWX when you are using an Execution Environment. I chimed in with this learning here: https://github.com/ansible/ansible-navigator/issues/1917
Finding 4 - Avoid Hardcoded Absolute Paths with Volume Names
Use the volumes
directive in your compose file. Your docker compose file supports blocks like this:
volumes:
playbook_project:
driver: local
driver_opts:
type: none
device: ./project
o: bind
One advantage to these blocks is that you can pass a reference to your volume my name instead of my absolute path. See --volume=my_project_playbook_project:/tmp/ee
in my ansible-navigator.yml
file. The project working directory name is appended to your volume name by default, but this is much better than needing to hard code the absolute path in your EE yaml.
Finding 5 - Easy Logging
To view your playbook’s output, simply run the following on your Docker host: docker logs -f $(docker ps --format '' | grep ansible)
.