This is a tutorial post for my own notes, in this post we will walk through the steps to install docker on two ubuntu servers using ansible.

DISCLAIMER: This is my first time using ansible, so if you are looking for best practices, look elsewhere.

Installing "ansible" on a mac

brew install ansible
NOTE: you will need homebrew installed - https://brew.sh/

Creating Inventory file

In order for ansible to execute in parallel on multiple hosts, it needs the hosts setup in inventory. For this walk through, you will need to spin up a few EC2 instances in aws and add them to your inventory file hosts.

You can group these files in the inventory file like so:

[testservers]
ec2-XXX.us-west-1.compute.amazonaws.com	ansible_user=ubuntu ansible_ssh_private_key_file=/path/to/key
ec2-XXX.us-west-1.compute.amazonaws.com ansible_user=ubuntu ansible_ssh_private_key_file=/path/to/key

In your terminal create a new folder docker-playbook and create a new file in that folder called hosts and add your servers ip addresses or dns names, along with the user and private key info.

Now that we have our inventory file setup, we can test our connectivity:

ansible all -i hosts -m ping

This command will connect to each server and run a ping program and should result with some output like the following:

ec2-xxxxx.us-west-1.compute.amazonaws.com | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}
ec2-xxxxxx.us-west-1.compute.amazonaws.com | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

๐ŸŽ‰ Congrats! ๐ŸŽ‰ You have successfully connected to your servers using ansible!

To run for our specific inventory group, we replace all with the name of our group.

ansible testservers -i hosts -m ping

Creating a playbook

Now that we have ansible installed and able to connect to our hosts, we need to create a playbook.

In a working directory, create a new file called playbook.yml, open the file in an editor. This is where we will place all of our tasks to be applied to our hosts.

A task is a yaml object with specific properties that instruct ansible to perform instructions on the host machines in parallel.

In order to install docker we would normally run the following instructions on the command-line of a host machine:

apt update -y
sudo apt update -y
sudo apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io -y

With ansible, we will create a playbook that can safely execute the same functionality as above using a list of tasks defined in the playbook yml file.

playbook.yml

The first thing we do is apply the three dashes `---` then use comments to give the file a title.

---
# Install docker on ubuntu 

- hosts: testservers
  become: true
  

Next in the playbook, we specify the hosts we would like to target in the hosts file and that we want to run this playbook as root user, using the become statement.

Next we add a list of tasks:

  ...
  tasks:
  - name: Install aptitude using apt
    apt: name=aptitude state=latest update_cache=yes force_apt_get=yes

The first tasks installs aptitude, because anisble is setup to use aptitude for scripts.

  - name: Install required system packages
    apt: name={{ item }} state=latest update_cache=yes
    loop: ['apt-transport-https', 'ca-certificates', 'curl', 'software-properties-common']

This task is similar to the first using the apt property, but it also contains a variable handler in the apt statement. {{ item }} this item variable is set by the loop property, which will iterate through each item in the list and execute the apt command. This will install the following packages: apt-transport-https, ca-certificates, curl and software-properties-common.

  - name: Add Docker GPG apt key
    apt_key:
      url: https://download.docker.com/linux/ubuntu/gpg
      state: present

In this task, we install the gpg key for docker, using the apt_key command. Which takes a url, and state. The state setting is present, which means, if it already exists, keep it.

  - name: Add Docker Repository
    apt_repository:
      repo: deb https://download.docker.com/linux/ubuntu focal stable
      state: present

In this task, we add the docker repository to the apt get sources.list.

  - name: Update apt and install docker-ce, docker-ce-cli, containerd.io
    apt: update_cache=yes name={{ item }} state=latest
    loop: ['docker-ce', 'docker-ce-cli', 'containerd.io']
    

This tasks installs docker using apt.

Run an ansible playbook

Lets run this playbook against our hosts:

ansible-playbook -i hosts playbook.yml

๐ŸŽ‰ Awesome! ๐ŸŽ‰ You have created an ansible playbook to install docker on an ubuntu 20 system.

- name: Install Python
  apt: name={{ item }} state=latest update_cache=yes
  loop: ['python3-pip', 'virtualenv', 'python3-setuptools']

- name: Install Docker Module for Python
  pip:
    name: docker

Pulling a docker image

In order to use docker with ansible, we need to install the python SDK

Now we add the task to pull the image

- name: Pull image
  docker_image:
    name: "nginx"
    source: pull

Running a container

- name: create container
  docker_container:
    container_default_behavior: compatibility
  	name: "nginx"
    image: "nginx"
    ports:
      - "8080:80"
    state: 'started'
    restart_policy: 'always'
    
Documentation: https://docs.ansible.com/ansible/2.5/modules/docker_container_module.html

Summary

There is a lot to learn about ansible, but the documentation is solid and easy to search for, also there are plenty of examples and cheatsheets published, here is one:

Ansible quickstart cheatsheet
A quick guide to getting started with your first Ansible playbook. ยท One-page guide to Ansible quickstart

Here is the full playbook.yml

---
- hosts: all
  become: true

  tasks:
    - name: Install aptitude using apt
      apt: name=aptitude state=latest update_cache=yes force_apt_get=yes

    - name: Install required system packages
      apt: name={{ item }} state=latest update_cache=yes
      loop: [ 'apt-transport-https', 'ca-certificates', 'curl', 'software-properties-common' ]

    - name: Add Docker GPG apt Key
      apt_key:
        url: https://download.docker.com/linux/ubuntu/gpg
        state: present

    - name: Add Docker Repository
      apt_repository:
        repo: deb https://download.docker.com/linux/ubuntu focal stable
        state: present

    - name: Update apt and install docker-ce
      apt: update_cache=yes name=docker-ce state=latest

    - name: Install Python
      apt: name={{ item }} state=latest update_cache=yes
      loop: ['python3-pip', 'virtualenv', 'python3-setuptools']

    - name: Install Docker Module for Python
      pip:
        name: docker

    - name: Pull image
      docker_image:
        name: "nginx"
        source: pull

    - name: Create container
      docker_container:
        container_default_behavior: compatibility
        name: "nginx"
        image: "nginx"
        ports: 
          - "8080:80"
        state: 'started' 
        restart_policy: 'always'