There are a few pitfalls worth noting when using Ansible when the remote is
an Arch machine. I know, a weird combination. It looks more and more like
basically no-one uses Arch remote with Ansible. But hey, I like it, so
learning (and documenting) a thing or two along the way might not be too
bad. Also note that this post is quite specific for a docker-compose
tool, so if you are not using it, you can safely skip the rest.
Getting started
First get a rootless Docker installed on a machine. It could be done by Ansible as well, but this is outside of the scope of this article. However, if you ever need help with that, just ping me (there is an email around the blog). I'll share my solution, or hopefully a whole post about it will be out by that time. Right now, you can gain some inspiration by looking up my previous article.
Next, we'll need two Ansible collection. The first is for
pacman collection,
referenced as community.general.pacman
:
ansible-galaxy collection install community.general
The second one is for
docker-compose collection,
referenced as community.docker.docker_compose
:
ansible-galaxy collection install community.docker
That should be it. Now let's get dirty.
Install docker-compose on the remote
Consider the most intuitive approach - install docker-compose and try to
the service, as two Ansible tasks, assuming
path/to/compose/project/docker-compose.yml
file exists:
tasks:
- name: Intall docker-compose
become: yes
community.general.pacman:
state: present
name:
- docker-compose
- name: Create and start the service
community.docker.docker_compose:
project_src: path/to/compose/project
When run as a playbook it fails:
TASK [Create and start the service] *********************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ModuleNotFoundError: No module named 'docker'
fatal: [X.X.X.X]: FAILED! => {"changed": false, "msg": "Failed to import the required Python library (Docker SDK for Python: docker above 5.0.0 (Python >= 3.6) or docker before 5.0.0 (Python 2.7) or docker-py (Python 2.6)) on vmi732184.contaboserver.net's Python /usr/bin/python3. Please read the module documentation and install it in the appropriate location. If the required library is installed, but Ansible is using the wrong Python interpreter, please consult the documentation on ansible_python_interpreter, for example via `pip install docker` (Python >= 3.6) or `pip install docker==4.4.4` (Python 2.7) or `pip install docker-py` (Python 2.6). The error was: No module named 'docker'"}
Yeah, right. The collection requirements state the docker PyPi package to be present, or at least docker-py for older Python versions like 2.6.
python-docker via pacman
Alright, the preferred
way to install python packages
on Arch is to prefer pacman
over pip
when possible. Luckily,
python-docker
is available in the community repository. No big deal. Let's add it into
the playlist and re-run it:
tasks:
- name: Intall docker-compose
become: yes
community.general.pacman:
state: present
name:
- docker-compose
- python-docker
- name: Create and start the service
community.docker.docker_compose:
project_src: path/to/compose/project
It fails again, with something along the lines of:
"msg": "Unable to load docker-compose. Try `pip install docker-compose`. Error: Traceback (most recent call last):\n File \"/tmp/ansible_community.docker.docker_compose_payload_0elyc4fq/ansible_community.docker.docker_compose_payload.zip/ansible_collections/community/docker/plugins/modules/docker_compose.py\", line 497, in <module>\nModuleNotFoundError: No module named 'compose'\n"
Now, there is
no package
in the official repositories containing words python
and compose
.
Following the recommendations above,
there is nothing relevant
in AUR either.
docker-compose via pip
Note that docker-compose
Python package pulls its docker
package
as a dependency. So it is
safe to not reference it with pacman in the playbooks here anymore. Since
there is no compose package in repositories, it's time to resort to pip
instead:
tasks:
- name: Intall docker-compose
become: yes
community.general.pacman:
state: present
name:
- docker-compose
- python-pip
- name: Install pip docker-compose
ansible.builtin.pip:
name: docker-compose
- name: Create and start the service
community.docker.docker_compose:
project_src: path/to/compose/project
Now the Python part gets resolved. We can now independently confirm the
docker
PyPi package is a dependency of the docker-compose
PyPi package:
pip show docker-compose | grep Requires | cut -d' ' -f2- | tr , '\n'
Resulting in the following with the version 1.29.2
:
docker
dockerpty
texttable
jsonschema
websocket-client
python-dotenv
requests
docopt
PyYAML
The above playbook however fails for the last time, and it was a little hard for me to pinpoint the issue here. The problem now is Docker. Truncated error message follows:
fatal: [X.X.X.X]: FAILED! => {
"changed": false,
"invocation": {
"module_args": {
...
"dependencies": true,
"docker_host": "unix://var/run/docker.sock",
"env_file": null,
...
}
},
"msg": "Error connecting: Error while fetching server API version: ('Connection aborted.', FileNotFoundError(2, 'No such file or directory'))"
}
The actual error is a little bit longer but contains mostly traceback. The
problem is with the docker_host
attribute. With a Docker rootless, the
actual socket is located in the user space, for instance at
unix:///run/user/1000/docker.sock
, specified by either CLI parameter or
the DOCKER_HOST environmental variable, more in the
docs.
Wrapping up
Most guides recommend exporting the variable somewhere into .profile
,
.bashrc
or .zshrc
like so:
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
But, since the Ansible opens a non-interactive shell, this variable exactly as it is exported will not be available to us. We have to construct it manually and hope no-one had changed it:
tasks:
- name: Intall docker-compose
become: yes
community.general.pacman:
state: present
name:
- docker-compose
- python-pip
- name: Install pip docker-compose
ansible.builtin.pip:
name: docker-compose
- name: Create and start the service
community.docker.docker_compose:
docker_host: "unix://{{ ansible_env.XDG_RUNTIME_DIR }}/docker.sock"
project_src: path/to/compose/project
Note that for ansible_env
to be available, option gather_facts
has to
be kept enabled. Mention in the
docs.
Enjoy!