Akhil Sukhnani
11 min readSep 2, 2020

--

In this article we would be automating the complete deployment of a web server via CI/CD pipeline and then deployment of the web app, and the server for the app would be launched inside the docker container, and all of this would be automated by jenkins that would also be launched inside a container and will control the tasks on the base OS via ssh.

Here’s the detailed and step by step description of the task:-

  1. Create a container image that has jenkins installed using Dockerfile.
  2. When we launch any container from this image, it should automatically start jenkins service in the container.
  3. Create a job chain of job1, job2, job3, and job4 using build pipeline plugin in jenkins.
  4. Job 1 Pull the Github repo automatically whenever the developer push repo to Github.
  5. Job2 — By looking the type of files in the repo (php, html etc.) jenkins should automatically start the respective server and deploy that code on that server.
  6. Job 3 — It should test that the Web App or Code is deployed on the server or not.
  7. Job 4 — If the code is not deployed on the server, it should send an email to the developer.
  8. Job 5 — It should keep monitoring our container which has the server running of our website or Web app and if by any chance, the container fails or stops, it should automatically start the container again.

Prerequisites:-

  1. Rhel7/8 or centos 7/8 installed
  2. Docker configured

And in the end we will put all the job in a single BUILD Pipeline which would look something like this:-

So now our first goal is to launch a container that has Jenkins installed in it, and for that we have make some changes in the existing docker images and for the sake of automation we also can’t go inside the container of a base OS and make changes, so we have to use Dockerfile , it is the official scripting language by Docker to make customized images according to the need,so with the help of Dockerfile we can achieve our goal that is jenkins installed inside a image and Jenkins service automatically starts upon launching of the container.

Dockerfile

Here’s the code to the dockerfile and explanation to every line of the dockerfile.


FROM centos
#this means our base os would be centos above which we would be making the changes
ENV USER=root
#the user to which ssh has to be done ,by default its it is root from our side in case user doesn't provide us the user, we have given this because because we would like to do ssh from inside the conatainer to the host os or any of the OS , because we don't want to launch the webserver in the same container
ENV DEST_IP $DEST_IP
#this would be taken at the run time when we go for docker run command,it is the ip of the host os or the machine upon which you want to launch the webserver upon(with the help of docker)
RUN yum install wget -y
#to download anything from the internet we need this command , and we have to download jenkins so we need this software also
RUN yum install openssh-server openssh-clients -y
#installing ssh so that we can get the access of the machine from where we have to execute the docker engine
COPY script.sh /
#copying the script to root directory of the container that is going to be launched , this script conatins ssh commands to get the co0ntrol of the host os and they all needed to be executed in a single run.
RUN wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat/jenkins.repo
RUN rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key
#to install jenkins, as mentioned on the jenkins official site
RUN yum install java jenkins git sudo -y
#installing java because jenkins require java background to run and install jenkins package that we have downloaded ,and installing git because our code (code for the webserver)would be downloaded inside the same conatiner
RUN echo -e "jenkins ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
#to give jenkins user sudo power to do root level tasks and run commands as well
EXPOSE 8080
#because jenkins by default runs on the port 8080
CMD bash script.sh
#running our script inside the container which we copied , because we can only run CMD command inside the dockerfile only once.

And this is the script.sh that we copied inside the container.

Script.sh‘
ssh-keygen
#generating keys
ssh-copy-id $USER@$DEST_IP
#copying the key in the system which is running the file or whose ip is mentioned at the execution of docker command for the passwordless and secure connection
/usr/bin/ssh
#to start ssh because we haven't installed package that can run systemctl command
java -jar /usr/lib/jenkins/jenkins.war
#this cmd is to start jenkins service
#all of these commands are written inside one file because CMD can only be used one time and in that one time we will run this complete file which in return will run the complete commandsBecause we have to make some permission changes in ssh so that our jenkins container wont’t find any error wile doing ssh connectivity with our base os

And now after this we have to make some changes in the ssh_config file because or jenkins container will make ssh connection to the base OS.

systemctl restart sshd

Now we need to start sshd because , if we make changes in the ssh file and then to apply the changes we need to restart this service.

Now its time to build the image, from the dockerfile that we have written.

docker build -t dockjenkins:v1 .

This command will build the image from the Dockerfile

As you can see our image has been built, now its time to run our jenkins container but before that we need to know the ip address of our base OS for the purpose of password less ssh.

Finally running the container from the image that we made

let me explain the command syntax to you in detail:-

docker run -it --name <name of the container> -v <path of the directory you have to mount>:/root/.jenkins/ -p <port you want to PAT at>:8080 -e USER=<username of the system to which you will be doing ssh from the container> -e DEST_IP=<ip of the system to which you will be doing ssh from the container> <image_name:version>

Now it will ask for the ssh keygen you have to press enter 3–4 times, then it will ask for the password of the machine whose IP you have given in the command , because it will copy the ssh key inside that machine to accomplish password less ssh and it has been possible because we have made changes above in the sshd file otherwise it would have given error.

this means that successfully ssh keys have been added to the given location

here you will find the initial password for jenkins also , using that you can login on the domain and port use the port 8082 as we have given ,syntax would be like

http://<ip_of_the_base_os>:8082/

Enter initial password and change the password ,then you will land on this screen

Now its time to build the jobs:-

JOB1: Pull the Github repo automatically whenever the developer push repo to Github.

So this job will download the code and will check the code of there exists any html or php file.

This is the workflow of how our job will be executed

this proves that first files were copied in the directory which we mounted and then after fulfilling our condition they were copied to our specified location

This is the screenshot of the error that i was getting during this job , as i didn’t knew where exactly in the mounted directory the content that jenkins downloaded for the job exists.

troubleshooting error
gving the link to my gihub repo

we have used POLL SCM trigger so that it would check every minute that if there’s any changes been made to github repo.

and shell code syntax for job1

ssh root@<ip_of_the_VM> '
path="/root/mounted_directory/workspace/task2_job1/"
files=$(sudo ls $path | grep -e php -e html | wc -l)
if [[ $files > 0 ]]
then
sudo cp -rvf $path* /root/final
else
echo "No php or HTML files received"
exit 1
fi
'

ssh root command will connect our container to the base os or specified os and the commands , this code will check the if there exists any php or html files in the given location so it would copy all the files to some other location from where we would use them in our webserver.

JOB2

By looking the type of files in the repo (php, html etc.) jenkins should automatically start the respective server and deploy that code on that server.

So now that we have our code for website we would like to use it and deploy on a webserver and we have to launch webserver also in such a way that only that webserver would be launched whose code is there in our files.

So we would use grep keyword for this purpose to look for keyword and wc keyword for the word count so that we would know that how many times ,keywords like html or php were used.

our container has been launched successfully
HTML Page Deployed
PHP code deployed
ssh root@10.148.0.2 'html_code=$(sudo ls /root/final/ | grep html | wc -l)
php_code=$(sudo ls /root/final/ | grep php | wc -l)
if sudo docker ps -a | grep -e phpserver -e httpdsever
then
sudo docker rm -f phpserver httpdserver
else
echo "GO ahead"
fi
if [[ $html_code > 0 && $php_code > 0 ]]
then
sudo docker run -dit -v /root/final/:/var/www/html --name phpserver -p 8600:80 vimal13/apache-webserver-php
elif [[ $php_code> 0 ]]
then
sudo docker run -dit -v /root/final/:/var/www/html --name phpserver -p 8600:80 vimal13/apache-webserver-php
elif [[ $php_code> 0 ]]
then
sudo docker run -dit -v /root/final/:/usr/local/apache2/htdocs --name httpdserver -p 8601:80 httpd
fi
'

so here in this code we did do ssh again to the remote OS and looked at the location where we copied the code , that what type of file existed there and accordingly that type of webserver was launched and as soon as we found a match we turned of previously working servers and restarted because there might be possibility running inside the site would not be the same that we wanted to deploy.

Job 3 — It should test that the Web App or Code is deployed on the server or not.

job3

So here in this job we would like to check if the website is running successfully or not.

And easiest way to do that is

curl -o /dev/null -s -w “%{http_code}” <website_address>

if this returns 200 this means that website deployed successfully and running efficiently and if the return value is not equal to 200 there might be some issues

ssh root@10.148.0.2  '
website_status=$(sudo curl -o /dev/null -s -w "%{http_code}" http://35.240.141.3:8600/ )
if [[ $website_status == 200 ]]
then
echo "Website is UP and running fine"
else
echo "Something went wrong"
exit 1
fi
'

Job 4 — If the code is not deployed on the server, it should send an email to the developer.

Now the question is how would we know if the previous job or any job which we are looking for ran successfully or not.

So here’s an API of jenkins which gives us detailed description about the job

And here’s how to get that information for any job running inside the jenkins.

if you enter url in the browser in similar pattern

http://<uname:password>@i<ip_of _jenkins>/job/<job_name>/lastBiuild/api/xml

This is the way you will get output and success is the keyword which will tell us if the job was successful or not.

Now using grep keyword we will fetch the information from this page which is useful to us and that information is to determine the success of previous task that was that task successfully completed or not.

status=$(curl -s  http://admin:redhat@35.240.141.3:8082/job/task2_job3/lastBuild/api/xml | grep SUCCESS | wc -l)
if [[ $status == 1 ]]
then
echo "App running successfully"
else
echo "App not working properly, Email sent to Developer"
exit 0
fi

and if the status is not equal to 200 we have to send a mail to the developer so upon every failure of this job a mail would be sent to the developer and for that you have to setup smtp mail server in the jenkins and then you can use it send POST-BUILD notifications like this

And for the purpose of testing , i intentionally made some mistakes in the code of website and the mail that i received at the other end was like this:-

Job 5 — It should keep monitoring our container which has the server running of our website or Web app and if by any chance, the container fails or stops, it should automatically start the container again.

The simplest way to monitor was to go to the base os wherever the container ,

and monitor there in every minute that if the container we wished for is running or not.

doing poll scm so that we can check every minute

Shell code for job5:-

ssh root@10.148.0.2 '
running=$(sudo docker ps | grep phpserver | wc -l)
all=$(sudo docker ps -a | grep phpserver | wc -l)
if [[ $running==0 && $all==1 ]]
then
sudo docker start phpserver
fi
'

And finally add them in build pipeline view after making one job dependent over other.So our all the tasks will run in a single go.

Thank you for reading the article , I hope you liked it, and understood the core concepts behind this task , if you find any difficulty in understanding any of the concept you can comment down below.

And i am also attaching the githubrepo link to this task:- https://github.com/akhilsukhnani/Automating_CI_CD_PIPELINE.git

--

--