Ansible Development using Docker

Date: February 15, 2020 Author: Brian Hooper

TLDR

I have been doing quite a bit of work with Ansible lately across several different projects. This snippet is a simple approach for setting up a development workflow for Ansible using Docker. This is helpful when you are needing to quickly iterate on projects that support different Operating Systems.


Configuration

Here are the steps we will be walking through today:

  • Create new project directory
  • Create docker sub-directory
  • Create Dockerfile + Build Image for Amazon Linux (latest)
  • Create Dockerfile + Build Image for Ubuntu Linux 18.04
  • Create Dockerfile + Build Image for Ubuntu Linux 20.04
  • Running Docker for Ansible Development

Create Project Structure

First we need to create our project:


    mkdir ansible-docker
    cd ansible-docker

    mkdir docker
    cd docker

    mkdir amazon-latest
    mkdir ubuntu-18.04
    mkdir ubuntu-20.04

Ok, you should have the following basic structure:

    /ansible-docker
    |
    |__ /docker
    |   |__ /amazon-latest
    |   |
    |   |__ /ubuntu-18.04
    |   |
    |   |__ /ubuntu-20.04

Dockerfile (Amazon Linux)

Ok, next let’s create a Dockerfile for Amazon Linux at /ansible-docker/docker/amazon-latest/Dockerfile


  FROM amazonlinux:latest

  # ARGUMENTS
  ARG PYTHON_VERSION=3.7.7

  # INSTALL PACKAGES
  RUN yum update -y && yum install -y \
    curl \
    git \
    jq \
    make \
    tar \
    unzip \
    wget \
    which \
    zip

  # INSTALL PYTHON
  RUN mkdir "$HOME/.pyenv" && \
    git clone https://github.com/pyenv/pyenv.git "$HOME/.pyenv" && \
    echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc && \
    echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc && \
    echo 'export PATH="$PYENV_ROOT/shims:$PATH"' >> ~/.bashrc && \
    yum install -y \
    gcc \
    zlib-devel \
    bzip2 \
    bzip2-devel \
    readline-devel \
    sqlite \
    sqlite-devel \
    openssl-devel \
    tk-devel \
    libffi-devel && \
    $HOME/.pyenv/bin/pyenv install $PYTHON_VERSION && \
    $HOME/.pyenv/bin/pyenv global $PYTHON_VERSION && \
    $HOME/.pyenv/shims/pip install --upgrade pip && \
    $HOME/.pyenv/shims/pip install ansible

  # COMMAND
  CMD ["/bin/echo", "Hello Ansible"]

  # ENTRYPOINT
  ENTRYPOINT ["/bin/echo", "Hello Ansible"]

Dockerfile (Ubuntu Linux 18.04)

Ok, next let’s create a Dockerfile for Ubuntu Linux at /ansible-docker/docker/ubuntu-18.04/Dockerfile


  FROM ubuntu:18.04

  # ARGUMENTS
  ARG TIME_ZONE=America/Chicago
  ARG PYTHON_VERSION=3.7.7

  # CONFIGURE TIMEZONE
  RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

  # INSTALL PACKAGES
  RUN apt-get update -y && apt-get install -y \
    curl \
    git \
    jq \
    make \
    unzip \
    wget \
    zip

  # INSTALL PYTHON
  RUN mkdir "$HOME/.pyenv" && \
    git clone https://github.com/pyenv/pyenv.git "$HOME/.pyenv" && \
    echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc && \
    echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc && \
    echo 'export PATH="$PYENV_ROOT/shims:$PATH"' >> ~/.bashrc && \
    apt-get install -y --no-install-recommends \
    build-essential \
    libssl-dev \
    zlib1g-dev \
    libbz2-dev \
    libreadline-dev \
    libsqlite3-dev \ 
    llvm \
    libncurses5-dev \
    xz-utils \
    tk-dev \
    libxml2-dev \
    libxmlsec1-dev \
    libffi-dev \
    liblzma-dev && \
    $HOME/.pyenv/bin/pyenv install $PYTHON_VERSION && \
    $HOME/.pyenv/bin/pyenv global $PYTHON_VERSION && \
    $HOME/.pyenv/shims/pip install --upgrade pip && \
    $HOME/.pyenv/shims/pip install ansible

  # COMMAND
  CMD ["/bin/echo", "Hello Ansible running on Ubuntu 18.04"]

  # ENTRYPOINT
  ENTRYPOINT ["/bin/echo", "Hello Ansible running on Ubuntu 18.04"]

Dockerfile (Ubuntu Linux 20.04)

Ok, next let’s create a Dockerfile for Ubuntu Linux at /ansible-docker/docker/ubuntu-20.04/Dockerfile


  FROM ubuntu:20.04

  # ARGUMENTS
  ARG TIME_ZONE=America/Chicago
  ARG PYTHON_VERSION=3.7.7

  # CONFIGURE TIMEZONE
  RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

  # INSTALL PACKAGES
  RUN apt-get update -y && apt-get install -y \
    curl \
    git \
    jq \
    make \
    unzip \
    wget \
    zip

  # INSTALL PYTHON
  RUN mkdir "$HOME/.pyenv" && \
    git clone https://github.com/pyenv/pyenv.git "$HOME/.pyenv" && \
    echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc && \
    echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc && \
    echo 'export PATH="$PYENV_ROOT/shims:$PATH"' >> ~/.bashrc && \
    apt-get install -y --no-install-recommends \
    build-essential \
    libssl-dev \
    zlib1g-dev \
    libbz2-dev \
    libreadline-dev \
    libsqlite3-dev \ 
    llvm \
    libncurses5-dev \
    xz-utils \
    tk-dev \
    libxml2-dev \
    libxmlsec1-dev \
    libffi-dev \
    liblzma-dev && \
    $HOME/.pyenv/bin/pyenv install $PYTHON_VERSION && \
    $HOME/.pyenv/bin/pyenv global $PYTHON_VERSION && \
    $HOME/.pyenv/shims/pip install --upgrade pip && \
    $HOME/.pyenv/shims/pip install ansible

  # COMMAND
  CMD ["/bin/echo", "Hello Ansible running on Ubuntu 20.04"]

  # ENTRYPOINT
  ENTRYPOINT ["/bin/echo", "Hello Ansible running on Ubuntu 20.04"]

Building Docker Images

Ok, we should now have the following project structure:


    /ansible-project
    |
    |__ /docker
    |   |__ /amazon-latest
    |   |   |__ Dockerfile
    |   |
    |   |__ /ubuntu-18.04
    |   |   |__ Dockerfile
    |   |
    |   |__ /ubuntu-20.04
    |       |__ Dockerfile

Now that we have our Dockerfiles, let’s build our Docker Container Images to facilitate Ansible Development

Build Amazon Linux Image


    cd ./ansible-docker/docker/amazon-latest
    docker build --tag amazon:latest-ansible .

Build Ubuntu Linux 18.04 Image


    cd ./ansible-docker/docker/ubuntu-18.04
    docker build --tag ubuntu:18.04-ansible .

Build Ubuntu Linux 20.04 Image


    cd ./ansible-docker/docker/ubuntu-20.04
    docker build --tag ubuntu:20.04-ansible .

Running Docker from Ansible Project

Using this basic structure within an Ansible project:

    # Example
    /ansible-project
    |
    |   /ansible
    |   |__ /roles
    |   |   |
    |   |   |__ /sample
    |   |       |__ /files
    |   |       |__ /handlers
    |   |       |__ /meta
    |   |       |__ /tasks
    |   |       |__ /vars
    |   |
    |   |__ /scripts
    |   |   
    |   |__ playbook_sample.yaml    
    |
    |__ /docker
    |   |__ /amazon-latest
    |   |   |__ Dockerfile
    |   |
    |   |__ /ubuntu-18.04
    |   |   |__ Dockerfile
    |   |
    |   |__ /ubuntu-20.04
    |       |__ Dockerfile

Here is an example playbook (playbook_sample.yaml):


    ---
    # SAMPLE PLAYBOOK
    - name: playbook_sample
      hosts: localhost
      connection: local
      become: yes

      roles:
        - sample_role

We can run Docker and mount our working Ansible project directory as a volume:

    # ANSIBLE DIRECTORY
    cd ./ansible

    # AMAZON LINUX 
    docker run --volume="$PWD:/var/ansible" -it --entrypoint /bin/bash amazon:latest-ansible

    # UBUNTU LINUX 18.04
    docker run --volume="$PWD:/var/ansible" -it --entrypoint /bin/bash ubuntu:18.04-ansible

    # UBUNTU LINUX 20.04
    docker run --volume="$PWD:/var/ansible" -it --entrypoint /bin/bash ubuntu:20.04-ansible

In the example above we are mounting the present working directory" $PWD to /var/ansible within the running container. So now, while working within the container running on our local machine, we can run our Ansible playbooks against a specific OS:Version and iterate on our design.


    # USAGE: 
    # Note: While working in the container run the playbook from the local mounted volume
    ansible-playbook -i "localhost," -c local /var/ansible/playbook_<name>.yaml --extra-vars "workspace=<environment>"

    # EXAMPLE
    ansible-playbook -i "localhost," -c local /var/ansible/playbook_sample.yaml --extra-vars "workspace=dev"

If you receive the following error related to python3-apt, you can specify the interpreter to use. I have observed this error when using apt resources in a playbook:

  
    # ERROR
    {"changed": false, "msg": "Could not import python modules: apt, apt_pkg. Please install python3-apt package."}

    # EXAMPLE
    ansible-playbook -i "localhost," -c local /var/ansible/playbook_sample.yaml -e "ansible_python_interpreter=/usr/bin/python3"

Next

I have been using this basic setup to support several different projects and it has definitely helped with speeding up the feedback look when iterating on new designs. For future posts I plan on using this basic structure and setup to share some of the Ansible development that I am working on. More to come!


Resources


© Traveler Theme 2023
Let's Stay Connected

Handcrafted by a @KnownTraveler