Docker Containers as Managed Node in Ansible Controller

B Manoj
7 min readDec 14, 2020
Pic : Ansible makes container as its target nodes and then performs the tasks

The assignment is :

Write an Ansible PlayBook that does the following operations in the managed nodes:

🔅 Configure Docker

🔅 Start and enable Docker services

🔅 Pull the httpd server image from the Docker Hub

🔅 Run the httpd container and expose it to the public

🔅 Copy the html code in /var/www/html directory and start the web server

To achieve this assignment, please find my theoretical solution below:

Step1: First I will create two EC2 instances in AWS cloud and will dynamically update the inventory file on Ansible controller. The detailed explanation of this part is given in below article:

The code for this is:

Step2: Now, I will create another play within the same playbook. This play will help me to install the docker in one of the instances and then continue…

The code for this is :

Here in the very beginning of the code you can see that there is a pause module which will help to pause ansible for 2 minutes, so that the “dynamic ip updating in inventory file” will be completed (Because though we tell ansible to update the IP’s , it will take some time to do that. That’s why I compensated that time in pausing playbook execution so that the next modules wont face any problem)

Till now, From Step1 and Step2, I could achieve these two sub tasks:

🔅 Configure Docker

🔅 Start and enable Docker services

Step3: Now, we are still left out to achieve with:

🔅 Pull the httpd server image from the Docker Hub

🔅 Run the httpd container and expose it to the public

🔅 Copy the html code in /var/www/html directory and start the web server.

If we read the above three points clearly, it states that we have to create a httpd container and send some html file to document root directory.

I can achieve this by simply using volumes concept, instead, I am trying to explore one more option:

” That is making container itself as a target node for the ansible controller.”

We all know that if any node (or OS) has to become a target node then it should be installed with SSH service besides running application service like httpd, in our case.

Finally, I can conclude that my container should contain ssh service and httpd service running in it. To achieve this, we can’t use ansible directly, instead we should use dockerfile concept.

Below is my dockerfile which finally achieves ssh + httpd together in one container.

$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

FROM httpd:latest

# this will help me to pull the httpd image and then customize it #

RUN /usr/bin/apt-get update -y # I am trying to update the apt repo of this image #

RUN /usr/bin/apt-get install -y apt-utils

RUN /usr/bin/apt-get install -y iputils-ping

RUN /usr/bin/apt-get install -y net-tools

RUN /usr/bin/apt-get install -y tar

RUN /usr/bin/apt-get install -y wget

#### apt-utils, iputils-ping, net-tools, tar and wget are dependency packages which has to be present in the image for installing SSH in subsequent steps ####

RUN /usr/bin/apt-get install -y python3

#### We all know that ansible is written on python and ansible controller also expects the managed nodes to have python so that it can pass the commands and get them executed. For this I am installing python in them####

RUN mkdir -p /var/run/sshd

###### Creating a folder for sshd #######

RUN /usr/bin/apt-get install -y openssh-server

###### Installing the ssh server in it #######

RUN ssh-keygen -A

###### running ssh key gen commands #######

RUN rm -f /etc/ssh/sshd_config

###### After ssh is installed , it will create it’s own sshd_config file, which is very raw in nature. To customize this and then replace with the new one I will first delete it and then add my own sshd_config file #######

ADD sshd_config /etc/ssh/

##### Whatever the file I wish to present in /etc/ssh folder I am adding it #####

RUN echo root:redhat | chpasswd

##### Creating a ssh user and password for this container so that Ansible controller can communicate with this user #######

ADD start.sh /

##### This is the most challenging part of this container, our aim is “I need to have ssh and httpd service running simultaneously running in container”

For this, I need to trigger those services using some systemctl or service commands. Unfortunately those commands won’t work.

So we need to trigger the program files of both the services for which I prepared a bash script file

I do agree that we may use either “CMD” or “Entrypoint” , but the problem we can use either of them only …. and that too they can be used only once ….. and that too they are used only for launching one service, not multiple things at a time

That’s the reason why I used a bash script to trigger multiple processes

#########

RUN chmod +x /start.sh

####### After adding the start.sh file, we need to shower proper permissions on it so that Dockerfile build command can execute this one ######

EXPOSE 22

EXPOSE 80

####### Exposing both the ssh and httpd ports #########

CMD [“/start.sh”]

####### Executing the script file #########

$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

Now let’s look at the code of sshd_config file which we planned to add in the docker file while the image is building.

$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

Port 22

Protocol 2

HostKey /etc/ssh/ssh_host_rsa_key

HostKey /etc/ssh/ssh_host_dsa_key

HostKey /etc/ssh/ssh_host_ecdsa_key

HostKey /etc/ssh/ssh_host_ed25519_key

LoginGraceTime 120

PermitRootLogin yes

#StrictModes yes

RSAAuthentication yes

PubkeyAuthentication yes

UsePAM yes

$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

Now, let’s see the start.sh script file:

$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

#!/bin/bash

(/usr/sbin/sshd -D) & (/usr/local/apache2/bin/httpd -D FOREGROUND)

$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

(/usr/sbin/sshd -D) — Represents to trigger the sshd process in /usr/sbin/sshd path

Similarly, to trigger the apache or httpd process the command is :

(/usr/local/apache2/bin/httpd -D FOREGROUND).

After looking at Dockerfile, sshd_config and start.sh file, let’s look at our yaml code in AC that how it can help us in acheiving the task:

The ansible code is :

Previously we have seen our playbook code till the point we installed and enabled docker services.

Now let’s copy the packages (Dockerfile, sshd_config & start.sh) from AC to docker host via copy command.

Later, I have used docker_image and docker_build commands to build the image in docker host itself and then launch the container out of it (You can also see that httpd port of container would be 32000 and ssh port of container would be 33000)

With this we have completed the following steps:

🔅 Pull the httpd server image from the Docker Hub

🔅 Run the httpd container and expose it to the public.

Step 4: Now, the next step would be :

🔅 Copy the html code in /var/www/html directory and start the web server.

For this the ansible code is :

Here I am trying to send the hello.html from my AC to destination container via ssh connectivity.

But you may wonder how and when the container is added to our inventory file ? The reason is :

If you see this initial part of code, there is a part of code saying that :

This will help to fetch the private ip of docker host and passed it to my inventory file using lineinfile module with some additional parameters like this :

“{{ y.instances[0].private_ip_address }} ansible_ssh_port=33000 ansible_ssh_user=root ansible_ssh_pass=redhat ansible_connection=ssh”

Here, I have pre-decided that my ssh port would be 33000 and ssh user is root and ssh password would be redhat which I had already configured in container using my Dockerfile

So, if you check my inventory file, it would look something like:

In the very beginning of my playbook execution it should something look like :

[haproxy]

[httpd]

[container]

So that lineinfile can actually update the file by using ‘insertafter’ argument

With this the explanation part is completed. Lets see the execution.

Now, let’s see the execution result:

Now, from the level of my browser I will try to open the ip of webserver and check the hello.html is transferred or not.

You can see that finally we were able to transfer the html file to container using ssh feature of container.

Thank You All , Stay Tuned !!!!

--

--