Automating things in software development is more than useful and using Ansible is one way to automate software provisioning, configuration management, and application deployment. Normally you would install Ansible to your control node just like any other application but an alternate strategy is to deploy Ansible inside a standalone Docker image. But why would you do that? This approach has benefits to i.a. operational processes.
Although Ansible does not require installation of any agents within managed nodes, the environment where Ansible is installed is not so simple to setup. In control node it requires specific Python libraries and their system dependencies. So instead of using package manager to install Ansible and it's dependencies we just pull a Docker image.
By creating an Ansible Docker image you get the Ansible version you want and isolate all of the required dependencies from the host machine which potentially might break things in other areas. And to keep things small and clean your image uses Alpine Linux.
The Dockerfile is:
FROM alpine:3.7 ENV ANSIBLE_VERSION 2.5.0 ENV BUILD_PACKAGES \ bash \ curl \ tar \ openssh-client \ sshpass \ git \ python \ py-boto \ py-dateutil \ py-httplib2 \ py-jinja2 \ py-paramiko \ py-pip \ py-yaml \ ca-certificates # If installing ansible@testing #RUN \ # echo "@testing http://nl.alpinelinux.org/alpine/edge/testing" >> #/etc/apk/repositories RUN set -x && \ \ echo "==> Adding build-dependencies..." && \ apk --update add --virtual build-dependencies \ gcc \ musl-dev \ libffi-dev \ openssl-dev \ python-dev && \ \ echo "==> Upgrading apk and system..." && \ apk update && apk upgrade && \ \ echo "==> Adding Python runtime..." && \ apk add --no-cache ${BUILD_PACKAGES} && \ pip install --upgrade pip && \ pip install python-keyczar docker-py && \ \ echo "==> Installing Ansible..." && \ pip install ansible==${ANSIBLE_VERSION} && \ \ echo "==> Cleaning up..." && \ apk del build-dependencies && \ rm -rf /var/cache/apk/* && \ \ echo "==> Adding hosts for convenience..." && \ mkdir -p /etc/ansible /ansible && \ echo "[local]" >> /etc/ansible/hosts && \ echo "localhost" >> /etc/ansible/hosts ENV ANSIBLE_GATHERING smart ENV ANSIBLE_HOST_KEY_CHECKING false ENV ANSIBLE_RETRY_FILES_ENABLED false ENV ANSIBLE_ROLES_PATH /ansible/playbooks/roles ENV ANSIBLE_SSH_PIPELINING True ENV PYTHONPATH /ansible/lib ENV PATH /ansible/bin:$PATH ENV ANSIBLE_LIBRARY /ansible/library WORKDIR /ansible/playbooks ENTRYPOINT ["ansible-playbook"] |
The Dockerfile declares an entrypoint enabling the running container to function as a self-contained executable, working as a proxy to the ansible-playbook command.
Build the image as:
docker build -t walokra/ansible-playbook . |
You can test the ansible-playbook running inside the container, e.g.:
docker run --rm -it -v $(pwd):/ansible/playbooks \ walokra/ansible-playbook --version |
The command for running e.g. site.yml playbook with ansible-playbook from inside the container:
docker run --rm -it -v $(pwd):/ansible/playbooks \ walokra/ansible-playbook site.yml |
If Ansible is interacting with external machines, you'll need to mount an SSH key pair for the duration of the play:
docker run --rm -it \ -v ~/.ssh/id_rsa:/root/.ssh/id_rsa \ -v ~/.ssh/id_rsa.pub:/root/.ssh/id_rsa.pub \ -v $(pwd):/ansible/playbooks \ walokra/ansible-playbook site.yml |
To make things easier you can use shell script named ansible_helper
that wraps a Docker image containing Ansible:
#!/usr/bin/env bash docker run --rm -it \ -v ~/.ssh/id_rsa:/root/.ssh/id_rsa \ -v ~/.ssh/id_rsa.pub:/root/.ssh/id_rsa.pub \ -v $(pwd):/ansible_playbooks \ -v /var/log/ansible/ansible.log \ walokra/ansible-playbook "$@" |
Point the above script to any inventory file so that you can execute any Ansible command on any host, e.g.
./ansible_helper play playbooks/deploy.yml -i inventory/dev -e 'some_var=some_value' |
Now we have dockerized Ansible, isolated it's dependencies and are not restricted to some old version which we get from Linux distribution's package manager. Crafty, isn't it? Check the docker-ansible-playbook repository for more information and examples with Ansible Vault.
This blog post and Dockerfile borrows from Misiowiec's post Running Ansible Inside Docker and his earlier work. If you want to test playbooks it's work checking out his ansible_playbook repository. Since then Alpine Linux has evolved and things could be cleaned a bit more like getting Ansible directly from testing repository.
HI,
i am new to docker and ansible.i tried above example to create ansible container and it worked good.i am trying to create an sdk where it installs all the required tools into host machine.so i have written a playbook.yml file for it ,
- name: Install the base package
yum: name={{ item }} state=installed
with_items:
- locales
- build-essential
- git
- supervisor
- python-pip
- python3-pip
- nginx
i tried with "yum" and "apk" both.but i am getting error.
for yum i am getting :
(item=[u'locales', u'build-essential', u'git', u'supervisor', u'python-pip', u'python3-pip', u'nginx']) => {"changed": false, "item": ["locales", "build-essential", "git", "supervisor", "python-pip", "python3-pip", "nginx"], "msg": "The Python 2 bindings for rpm are needed for this module. If you require Python 3 support use the `dnf` Ansible module instead.. The Python 2 yum module is needed for this module. If you require Python 3 support use the `dnf` Ansible module instead."}
i searched in net and tried few solutions and nothing worked.Can i please get support on this.am i missing any other package isntallation.or yum is not supported in dockerised enviroment.
Hey, you need to use dnf instead of yum
Hi, I'm trying to do the same thing that you did here. but when I ran the cmd 'docker build -t walokra/ansible-playbook .' to build the docker, I get this error
"==> Adding build-dependencies...
+ apk --update add --virtual build-dependencies gcc musl-dev libffi-dev openssl-dev python-dev
fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/main/x86_64/APKINDEX.tar.gz
ERROR: http://dl-cdn.alpinelinux.org/alpine/v3.7/main: temporary error (try again later)
WARNING: Ignoring APKINDEX.70c88391.tar.gz: No such file or directory
fetch http://dl-cdn.alpinelinux.org/alpine/v3.7/community/x86_64/APKINDEX.tar.gz
ERROR: http://dl-cdn.alpinelinux.org/alpine/v3.7/community: temporary error (try again later)
WARNING: Ignoring APKINDEX.5022a8a2.tar.gz: No such file or directory
ERROR: unsatisfiable constraints:
...."
The site http://dl-cdn.alpinelinux.org/alpine/v3.7/community is working .
Can you help me pls ? Thank you !
I think it's a temporary problem with the APKINDEX. I tried it now and it worked.
Maybe are you behind a proxy?
Hi there. Thanks for posting this article. I wondered whether you've had to deal with circumstances in which you're using an encrypted SSH key - it so happens that I've been trying to do what you've done in your article - containerise docker so that I can run different versions with ease. However when I got to the point of running the playbooks I couldn't work out how to share the SSH key, I can mount them as you do but I can't see a way to force ansible to ask for the key's passphrase. Since I am on a Mac, it appears that sharing an SSH Agent session between the host and the container is out, too.
If you are on a Mac your best option might be an SSH agent container and having your other containers that require a passphrase protected key inheriting the SSH_AUTH_SOCK via --volumes-from.
That seems to be the issue with newer 2.5.0 Ansible when using the tarball. With 2.3.0.0 it worked without running make. I will update the Dockerfile regarding that. Thanks for the notice.
Please note that I believe you need to run make in your code after unarchiving your tar ball. Otherwise you have only placed source code in your container. I was not able to use your example until I ran make; make install after the tar command.