|  |  | @@ -0,0 +1,127 @@ | 
    
    |  |  | 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](https://github.com/walokra/docker-ansible-playbook) 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](https://github.com/walokra/docker-ansible-playbook) for more information and examples with Ansible Vault. | 
    
    |  |  | 
 | 
    
    |  |  | 
 | 
    
    |  |  | 
 |