The world today demands the creation and deployment of various services and products as fast as possible. This brings up the necessity of having a fast product development, deployment, and monitoring cycle. To satisfy the requirements of the clients, the teams handling the product development, product deployment, and product monitoring and maintenance can take the help of different DevOps tools available in the industry today. One of these tools is Jenkins.
Jenkins is an opensource automation server, which provides hundreds of plugins to support building, deploying, and automating any project. Using Jenkins, it is possible to automate multiple parts of the product development, deployment, and monitoring cycle, to bring about the concept of Continuous Integration and Continuous Delivery (CI/CD). In this article, I will be giving a small glimpse into how Jenkins can be used in combination with Git and GitHub to automate product deployment.
To do this small project, we need the following requirements:
- A RHEL8 OS (I have used a VM on Oracle Virtual Box)
- Jenkins software installed on the RHEL8 VM
- Docker CE version installed on the RHEL8 VM
Before working on Jenkins itself, we need to modify the permissions assigned to Jenkins. When we run Jenkins Jobs, we need to run commands on the shell of the host system (in our case, the RHEL8 VM). Hence, we modify the
/etc/sudoers/ file in the RHEL8 VM through the command line and add the lines
jenkins ALL=(ALL) NOPASSWD=ALL .
Once this has been finished, we can run shell commands on the Jenkins Jobs. The work can be divided into three Jenkins Jobs:
- Job 1 — Watch the GitHub repository’s master branch, fetch from the master branch, and deploy on a Docker container.
- Job 2 — Watch the GitHub repository’s dev branch, fetch from the dev branch, and deploy on a Docker container.
- Job 3 — If the Quality Assurance Team (QA Team) approves of the dev branch deployment on the Docker container, merge the dev and master branches and run the newly merged content of the master branch on the Docker container.
To simulate the above content, we can take the example of the index.html page as the data or product to be deployed. Firstly, we create a Git repository in the local system (Base OS of your laptop — in my case, Windows 10).
$ git init
$ cat > index.html
Line 1 test.
$ git add .
$ git commit . -m 'commit 1 by master'
In the code snippet above, we first create a local Git repository using
git init . Then we write the index.html file and add the file to the staging area using
git add and finally commit the file using
git commit . Note that when a git repository is created, we are automatically within the master branch. However, we can create other side branches, but only after the first commit to the master branch has been made.
$ git branch dev
$ git remote add origin https://github.com/akshayavb99/Task1_DevOps.git
We now create a new branch named dev. Once the branch is created, we can create a new repository on GitHub and then use
git remote add origin <GitHub repo link> to set up remote versions of branches of the local Git repository in the GitHub repository. Now, we can proceed with the creation of Jenkins Jobs.
To perform operations related to Git, we need to install the required Jenkins Plugins. This can be done by installing the GitHub Plugin using Manage Plugins. Once the plugins are installed, we proceed with the creation of Job 1 as a Freestyle Project (the name given is task1_job1). The contents of Job 1 are shown in the images below.
Job 1 requires the deployment of the contents of the master branch onto a Docker container. In Figure 1, we describe our job’s purpose and give the details of the GitHub repository we will be working with. We also give the details of the branch with which we will be working as shown in Figure 2.
In Figure 3, we have the Build Triggers, which are essential conditions. If these conditions are satisfied then the current job will automatically run. We select two options. The first one is the Poll SCM, which is meant to check for any changes in the GitHub repository in the mentioned time interval (Here the check happens every second. The second one is if the build of another job (Task1_job3) is successful. Here Task1_job3 is called the upstream project of the current task1_job1. If Task1_job3 successfully runs, then automatically, task1_job1 is also set to run.
In Figure 4, we use shell commands to copy the contents of the master branch to a local directory and then deploy the docker container with the required contents. The docker container uses an image of HTTPD from the online Docker registry. We also use the concept of PATing on the docker container to allow it to be accessible outside the RHEL8 OS.
Job 2 is set up similarly, but the main changes are to set different names for the local directories and docker containers along with a different port number for PATing.
Job 3 is set up as a Freestyle project, but its purpose is different from that of Job 1 and Job 2. It is meant to merge the contents of the master and dev branch. To perform merging, we need to give the credentials of the GitHub repository while stating the branch (dev) on which we need to work. There are no specific shell commands, but we now use the feature of Post-Build Actions. Using the Git Publisher, we merge the contents of the master and dev branch. The contents of Job 3 (named as task1_job3) are shown in the images below.
Now all the Jenkins Jobs are set up, and we return to the local Git repository. In the Git repository, we have created the content, but have not pushed it to the GitHub repository.
$ git push -u origin master
$ git checkout dev
$ cat >> index.html
Line 1 by dev.
Line 2 by dev.
Line 3 by dev.
$ git add .
$ git commit . -m 'commit by dev'
$ git push --set-upstream origin dev
We first push the latest commit in the master branch to the GitHub repository. Then we switch to the dev branch, make changes in the index.html file, and push the changes to the remote dev branch. Now, we can see the effects of the actions until now, by checking the Jenkins Job 1 and Job 2. If the containers have been deployed successfully with the content, we will see the following outputs.
Now, if we manually build the Job 3, we see that once Job 3 is built successfully, Job 1 automatically starts to build, and after the successful deployment of the master branch contents, we get the following output.
There are multiple improvements we can do to increase the automation involved. One is the use of hooks for post-commit actions in the Git repository. Using a custom post-commit hook, we can automatically push the contents to the remote branch immediately after the commit command.
The work we have seen in this article is simply a glimpse into how powerful Jenkins is in many of the real-world applications. When combined with other DevOps tools like Prometheus, Grafana etc., we can develop fault-tolerant systems that enable CI/CD for product development, deployment, monitoring, and maintenance.