Introduction

Docker is a containerization engine and platform that encapsulates application code along with all of its dependencies inside a container. Containers are like virtual machines but they are more portable, more lightweight, and more dependent on the host operating system. Please do check out our extensive series of posts on docker. In this article, we will explain how to install docker using an Ansible Playbook.
Ansible at its core is a task execution engine. It exists to provide a method for developers, operators, and engineers to easily define one or more actions to be performed on one or more computers. This capability represents a step beyond just logging into each computer in question and manually typing out the commands. These tasks can target the local system ansible is running on as well as other systems ansible can reach over the network. Arguably the ability to manage remote systems is the most important aspect of ansible. When combined with the ability to express tasks to be performed in the form of simple to read text files, ansible provides a reusable and repeatable system for managing a fleet of infrastructure.
In this post, we will explain how to use a simple ansible playbook to automate the steps described in our earlier post explaining the installation of docker on centos 7.

 

Pre-requisites

In order to use the automated setup to install docker on a host using Ansible, we’ll need the following

An ansible control node: We’ve installed ansible on our centos 7 server. Let’s verify the same with the following command

[root@linuxnix ~]# ansible --version
ansible 2.9.15
config file = /etc/ansible/ansible.cfg
configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python2.7/site-packages/ansible
executable location = /bin/ansible
python version = 2.7.5 (default, Apr 2 2020, 13:16:51) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]
[root@linuxnix ~]#

 Ssh keys exchanged: Ansible needs password-less ssh access to the nodes it’s going to connect to. We’ll be using localhost for the purpose of this demonstration and have exchanged the ssh keys for the root user on the localhost. Please note that using the root user to run ansible playbooks is not the best practice. Ideally, you should have a generic user with elevated privileges configured to run playbooks. To verify that our keys are in place, let’s run an ansible adhoc command using the ping module.

[root@linuxnix ~]# ansible localhost -m ping
localhost | SUCCESS => {
"changed": false,
"ping": "pong"
}

The above output confirms that we are good to use our playbook.

 

The playbook

[root@linuxnix ~]# cat install_docker.yml
---
- name: Install docker on a Centos 7 machine
hosts: localhost

tasks:
- name: Install pre-requisite packages
  yum:
   name: "{{item}}"
   state: latest
  loop:
   - yum-utils
   - device-mapper-persistent-data
   - lvm2

- name: Add Docker CE repo
  get_url:
   url: https://download.docker.com/linux/centos/docker-ce.repo
   dest: /etc/yum.repos.d/docker-ce.repo

- name: Install Docker
  yum: name=docker state=latest

- name: Start and enable the Docker daemon
  service: name=docker state=started enabled=yes

 

What does this playbook do?

In the first section of the playbook, we’ve provided a name for our play and defined localhost as the destination host for the playbook to run on.
We have defined four tasks in the tasks section of our playbook. In the first task, we install the pre-requisite packages required by Docker to run on the system. The package installs are done using yum. In the second task, we add the docker community edition repository to our local yum configuration. The third task in the playbook performs the actual docker install. In the final task of the playbook, we start the docker daemon and enable it on boot.

 

Why use ansible and not a shell script?

We could’ve easily encapsulated the commands used to install docker within a shell script but the ansible playbook written in YAML offers better readability as compared to a bash script. Also, ansible playbooks are idempotent i.e. if we run the same playbook again making no changes to the system, then ansible will detect the host is already configured in the desired state and will not try to make any changes.

 

Running the playbook

To execute the playbook, type ansible-playbook followed by the playbook name as shown below.

[root@linuxnix ~]# ansible-playbook install_docker.yml
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [Install docker on a Centos 7 machine] *****************************************************************************************************************************

TASK [Gathering Facts] **************************************************************************************************************************************************
ok: [localhost]

TASK [Install pre-requisite packages] ***********************************************************************************************************************************
ok: [localhost] => (item=yum-utils)
changed: [localhost] => (item=device-mapper-persistent-data)
changed: [localhost] => (item=lvm2)

TASK [Add Docker CE repo] ***********************************************************************************************************************************************
changed: [localhost]

TASK [Install Docker] ***************************************************************************************************************************************************
changed: [localhost]

TASK [Start and enable the Docker daemon] *******************************************************************************************************************************
changed: [localhost]

PLAY RECAP **************************************************************************************************************************************************************
localhost : ok=5 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

 

Let’s check if docker has been installed.

[root@linuxnix ~]# systemctl status docker
● docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)
Active: active (running) since Fri 2020-12-18 08:57:35 UTC; 3min 44s ago
Docs: http://docs.docker.com
Main PID: 3859 (dockerd-current)
CGroup: /system.slice/docker.service
├─3859 /usr/bin/dockerd-current --add-runtime docker-runc=/usr/libexec/docker/docker-runc-current --default-runtime=docker-runc --exec-opt native.cgroupdri...
└─3867 /usr/bin/docker-containerd-current -l unix:///var/run/docker/libcontainerd/docker-containerd.sock --metrics-interval=0 --start-timeout 2m --state-di...

Dec 18 08:57:35 linuxnix.example.com dockerd-current[3859]: time="2020-12-18T08:57:35.280330882Z" level=warning msg="overlay2: the backing xfs filesystem is f...
Dec 18 08:57:35 linuxnix.example.com dockerd-current[3859]: time="2020-12-18T08:57:35.428811756Z" level=info msg="Graph migration to content-addressab...seconds"
Dec 18 08:57:35 linuxnix.example.com dockerd-current[3859]: time="2020-12-18T08:57:35.430088365Z" level=info msg="Loading containers: start."
Dec 18 08:57:35 linuxnix.example.com dockerd-current[3859]: time="2020-12-18T08:57:35.541895571Z" level=info msg="Firewalld running: false"
Dec 18 08:57:35 linuxnix.example.com dockerd-current[3859]: time="2020-12-18T08:57:35.801792654Z" level=info msg="Default bridge (docker0) is assigned...address"
Dec 18 08:57:35 linuxnix.example.com dockerd-current[3859]: time="2020-12-18T08:57:35.863455849Z" level=info msg="Loading containers: done."
Dec 18 08:57:35 linuxnix.example.com dockerd-current[3859]: time="2020-12-18T08:57:35.889618029Z" level=info msg="Daemon has completed initialization"
Dec 18 08:57:35 linuxnix.example.com dockerd-current[3859]: time="2020-12-18T08:57:35.889762006Z" level=info msg="Docker daemon" commit="0be3e21/1.13....n=1.13.1
Dec 18 08:57:35 linuxnix.example.com dockerd-current[3859]: time="2020-12-18T08:57:35.904562229Z" level=info msg="API listen on /var/run/docker.sock"
Dec 18 08:57:35 linuxnix.example.com systemd[1]: Started Docker Application Container Engine.
Hint: Some lines were ellipsized, use -l to show in full.
[root@linuxnix ~]#

To validate that docker has been installed correctly, let’s run a hello-world container.

[root@linuxnix~]# docker run hello-world
Unable to find image 'hello-world:latest' locally
Trying to pull repository docker.io/library/hello-world ...
latest: Pulling from docker.io/library/hello-world
0e03bdcc26d7: Pull complete
Digest: sha256:1a523af650137b8accdaed439c17d684df61ee4d74feac151b5b337bd29e7eec
Status: Downloaded newer image for docker.io/hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/

For more examples and ideas, visit:
https://docs.docker.com/get-started/

[root@linuxnix~]#

Conclusion

Using ansible for automating our infrastructure setup ensures streamlined and standardized configurations across our server fleet.
This not only saves a lot of time but also significantly reduces human errors resulting in misconfigured systems. In this article, we demonstrated how to write a simple ansible playbook to automate the installation of docker on our localhost. We could easily modify this playbook to perform the install on a fleet of systems in case we intend to use docker in swarm mode. We hope you found this article to be useful and we look forward to your suggestions and feedback.

The following two tabs change content below.

Sahil Suri

He started his career in IT in 2011 as a system administrator. He has since worked with HP-UX, Solaris and Linux operating systems along with exposure to high availability and virtualization solutions. He has a keen interest in shell, Python and Perl scripting and is learning the ropes on AWS cloud, DevOps tools, and methodologies. He enjoys sharing the knowledge he's gained over the years with the rest of the community.