Dockerizing all the things: Running Ansible inside Docker container

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.


Posted

in

,

by

Comments

10 responses to “Dockerizing all the things: Running Ansible inside Docker container”

  1. CZ Cheung Avatar

    Can I run more than one processes of Ansible in one pod inside docker at the same time?

  2. Leelavathi Avatar
    Leelavathi

    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.

    1. Joshua Jones Avatar
      Joshua Jones

      Hey, you need to use dnf instead of yum

  3. Abdel Avatar
    Abdel

    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 !

    1. Marko Avatar

      I think it’s a temporary problem with the APKINDEX. I tried it now and it worked.

    2. Rainyel Ramos Avatar

      Maybe are you behind a proxy?

  4. Dan Rough Avatar
    Dan Rough

    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.

    1. dragon788 Avatar
      dragon788

      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.

  5. Marko Avatar

    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.

  6. Steven Avatar

    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.

Leave a Reply

Your email address will not be published. Required fields are marked *