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.
Sahil Suri
Latest posts by Sahil Suri (see all)
- Google Cloud basics: Activate Cloud Shell - May 19, 2021
- Create persistent swap partition on Azure Linux VM - May 18, 2021
- DNF, YUM and RPM package manager comparison - May 17, 2021
- Introduction to the aptitude package manager for Ubuntu - March 26, 2021
- zypper package management tool examples for managing packages on SUSE Linux - March 26, 2021