Building an Automated CI/CD Pipeline with Jenkins, SonarQube, Docker, and AWS

Project Overview

This tutorial showcases the implementation of an automated CI/CD pipeline for a web application using Jenkins, SonarQube, Docker, and AWS. The pipeline facilitates automatic building, testing, analysis, and deployment of your project to an AWS EC2 instance every time new code is pushed to your GitHub repository.

Prerequisites:

  • GitHub account

  • Familiarity with EC2 Instances and an active AWS account

  • Understanding of SonarQube, Docker, and Jenkins

Steps to cover:

  1. Provision three EC2 instances and configure a security group to allow all traffic from the internet.

  2. Install Jenkins, SonarQube, and Docker on each EC2 instance.

  3. Establish SSH connections between the Jenkins instance, SonarQube instance, Docker instances, and Jenkins itself.

  4. Generate an SSH key and save the IDs to enable password-less SSH connections.

  5. Install the "SSH2 Easy" plugin on Jenkins and configure the server settings.

  6. Configure server groups and sites for Jenkins, SonarQube, and Docker.

  7. Create a new job in Jenkins and provide the Git link for your repository, specifying the branch you want to build and deploy.

  8. Configure the pipeline by adding build steps that involve copying code from the Jenkins workspace to the SonarQube and Docker instances for analysis and deployment.

Creating EC2 Instances

Generally, there is only one rule present for port 22 for SSH.
We have to add some more rules as:

  • Port 8080, for Jenkins

  • Port 80, for exposing our web application

  • Port 9000, for SonarQube

In this project, we have two options for setting up Jenkins, Ansible, and a web server on the required servers: using command scripts or manually installing the necessary packages on EC2. We will be using the Ubuntu operating system for this purpose to ensure compatibility. Using command scripts can save us time and effort, allowing for a quick and easy setup process.

Navigate to the bottom where you were creating the EC2 instance. Click on "Advanced Details," and at the bottom, you will find a field named "User Data." In this field, add the Jenkins script provided below and then launch the instance.

User data is generally used to run commands or a command script to run when you launch your instance.

Command script for Jenkins


#!/bin/bash

#This script sets up the Jenkins server
#Switch to the superuser
sudo su

#Set the hostname for Jenkins
hostnamectl set-hostname jenkins

#Update the package list
apt update

#Install OpenJDK 11
apt install openjdk-11-jdk -y

#Import the Jenkins GPG key
curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | sudo tee /usr/share/keyrings/jenkins-keyring.asc > /dev/null

#Add the Jenkins repository to the system's list of sources
echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] https://pkg.jenkins.io/debian-stable binary/ | sudo tee /etc/apt/sources.list.d/jenkins.list > /dev/null

#Update the package list again
apt update

#Install Jenkins
apt install jenkins -y

#Start Jenkins and enable it to start on boot
systemctl start jenkins
systemctl enable jenkins

#Allow traffic through port 8080 in the firewall
ufw allow 8080

Command script for Docker

#!/bin/bash

sudo su
hostnamectl set-hostname docker
apt-get update -y
apt-get install -y apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update -y
apt-get install -y docker-ce docker-ce-cli containerd.io
usermod -aG docker ubuntu

Command script for Sonarqube and make sure to user t2.medium instance

#!/bin/bash
apt-get update -y
apt-get install -y openjdk-11-jdk unzip
wget https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-8.9.2.46101.zip
unzip sonarqube-8.9.2.46101.zip -d /opt
mv /opt/sonarqube-8.9.2.46101 /opt/sonarqube
useradd -r sonarqube -s /bin/false
chown -R sonarqube:sonarqube /opt/sonarqube
sed -i 's|#RUN_AS_USER=|RUN_AS_USER=sonarqube|' /opt/sonarqube/bin/linux-x86-64/sonar.sh
/opt/sonarqube/bin/linux-x86-64/sonar.sh start

Connecting to Jenkins, SonarQube, and Docker through SSH

We have to set the SSH connection from Jenkins to Jenkins, Jenkins to SonarQube, and Jenkins to Docker for further processing. Also, we have to make these connections smooth by generating an ssh key and saving their Ids.

Follow these commands to do so:

sudo su
#this will eneter you into root environment

vi /etc/ssh/sshd_config
#you have to edit and make some changes in ssh config file to make ssh connection successful

To save these changes and exit, press ":wq!"

systemctl restart sshd
#this will restart sshd services

exit
#this will make exit from root environment

Now, you have to set a password to your instance for authentication.

sudo passwd root

You have to repeat the above process in all the three instances.

After completing the above process in all three instances, connect to your Jenkins instance in your terminal and run the following command to set up a connection from Jenkins to Jenkins, SonarQube, and Docker.

ssh-keygen
#this will generate a ssh key in which we will going to save our ssh Ids of other instances to make them passwordless.

Now, this below process has to be repeated again for all three Public IPs of Jenkins, SonarQube and Docker in Jenkins instance only.

I'm showing this with Docker Public IP, but you have to do it with all three.

After successful authentication, you will get to login to your instance from Jenkins to that particular instance whose Public IP you have entered above.

After repeating this above process with remaining two Public IPs of Jenkins and SonarQube, head over to AWS Instances dashboard on your browser.

Integrating SonarQube For Jenkins

Copy the Public IP of SonarQube and paste it on the browser and put port 9000 after it in such format <public IP>:9000
For ex. - 65.0.3.87:9000

Now, login with username - admin & password - admin

After login and changing your password, you'll land on the SonarQube dashboard.

Click on Add a Project.

Click on this icon.

Give a name to your Project and provide the name of the branch you want to analyze.

Now, we have many options to select for Continuous Integration tools. For now, we will move forward with Jenkins.

On the next page, we'll choose GitHub for the DevOps platform.

Now, the system will prompt you for configuration details. Proceed by clicking "Continue" until you reach the third step. In the third step, choose the "Other" option as we intend to deploy our HTML application. Make sure to copy the code provided in this step and save it securely, as we will need it later on.

Click on Finish this tutorial to save it, and after saving it move to My Accounts>Security>Generate Tokens

Now, create a Token for integrating with Jenkins. Copy the token and save it somewhere safe, as we are going to use it later.

Now, head over to Jenkins Dashboard, by pasting the Public IP of Jenkins and put port 8080 after it in such fromat <Public IP>:8080

Manage Plugins

We have to install some plugins on Jenkins for setting up SonarQube and Docker.

SonarQube Scanner

On Jenkins Dashboard, click on Manage Jenkins>>Manage Plugins>>Available Plugins

Now, in Available Plugins, search for SonarQube Scanner and SSH2 Easy.
After selecting them, click on "Install without restart".

To proceed, we need to set up several plugins and servers to enable connectivity among all three instances.

  1. Access the Jenkins Dashboard and navigate to "Manage Jenkins" >> "Global Tool Configuration."

  2. In the tool configuration page, search for "SonarQube Scanner."

  3. Click on "Add SonarQube Scanner" and provide an appropriate name for it.

  4. Enable the "Install automatically" option to facilitate the scanner's installation.

  5. Finally, save the configuration to apply the changes.

Now, on Manage Jenkins>>Configure system, search for SonarQube servers and click on "Add SonarQube"

Name it accordingly, copy the Public IP of SonarQube and paste it, and put port 9000 after it in such format http://<Public IP>:9000 . Apply & save it.

SSH2 Easy

On the Jenkins dashboard, click on Manage Jenkins>>Configure System, search for "Server Groups Center" and then click on "Add" under "Server Group List"

We have to add two Server Groups here, one for Jenkins and one for Docker. Give them names Jenkins-Server and Docker-Server respectively.

Change their UserName from root to ubuntu, and give the password for this which you set earlier in the terminal for ssh authentication.

Apply and save it, and again come back here to add server sites.
Now, search for "Server List" under Manage Jenkins>>Configure System>>Server Groups Center and click on "Add" to list the servers.

Here also, you have to add two server sites, one for Jenkins and one for Docker. Give them a name accordingly, and mention the Public IPs of Docker and Jenkins in their respective Server Groups.

Apply and save it. We are done managing plugins.

Finally, we will build our CI-CD Pipeline for our simple HTML project

Create a Jenkins Job for Deploying our Web Application

Give it a name, select freestyle the type of pipeline you want to create, and then click "OK".

Under the configuration setting, select "Git" for Source Code Management. Copy and paste the git link from the GitHub repository.

To automate the pipeline i.e. automatically triggering the build process when new changes got pushed to the GitHub. This will build an automated pipeline between a Programmer and Jenkins Pipeline.

For this, we have to check this Build trigger - "GitHub hook trigger for GITScm polling"

Click apply and save.
Now head over to your GitHub repo and click on settings>>webhooks and then click on "Add webhook".

Give the URL in the following format http://<Public IP of Jenkins>:8080/github-webhook and click on "Add webhook" to save it.

Now, on Jenkins Dashboard Under CI-CD Pipeline click on "Build Now" to start the build process.

If everything works fine, you will see the success status of your build.

Configuring Pipeline for SonarQube

Under the CI-CD Pipeline, click "Configure", search for Build Steps and click on "Add build step" and select "SonarQube Scanner".

Now, paste the code in the Analysis Properties field, which you copied from SonarQube which we kept saved.

Apply and save it, and head over to Jenkins Dashboard>>Manage Plugins>>Configure System and add server authentication token for SonarQube by clicking on "Add", and selecting Jenkins.

Change Kind to secret text and paste the secret token key which we copied earlier and saved it. Give it an ID name accordingly.

Now, for the server authentication token, from the scroll bar select the ID name, the one which you just created.

Now, under the CI-CD Pipeline, click **"Build Now".**You will see-success if all things work fine.

Now, our pipeline takes the code from the user through GitHub and triggers a build in Jenkins which push the code to SonarQube to analyze the code quality.

Configuring Pipeline for Docker

We want to build a docker image of our application and then run it on port 3000 so that it can be accessed and be in a running state.

First, ssh into your Docker instance through the terminal and create a folder website for storing the source code and building the Docker image.

mkdir website

Now, under the CI-CD Pipeline on Jenkins Dashboard click on "Configure", search for Build Steps, and select remote shell.


We are using Remote Shell because we want to run commands in Docker from Jenkins.
Earlier the SSH connection that we set up between Jenkins to Jenkins and Jenkins to Docker is for this only.***

Now first, we target Jenkins Server for copying the source code from the Jenkins workspace to Docker Server inside the website folder that we just created.

cd /var/lib/jenkins/workspace/jenkins-sonarqube-docker/
scp -r ./* root@54.167.51.165:/website/
#this command securely copy all the code from the directory - CI-CD Pipeline and paste it to this following destination - root@54.167.51.165:/website/

Add another remote shell, this time for targeting the Docker server for building and running the image.

cd /website/
docker build -t netflix .
docker run -d -p 80:80 --name NETFLIX netflix

Apply and save it.
If everything works fine, then you'll see success. Here is our live website working on Port 3000 on Docker.

Now, you can also verify the pipeline by pushing some new changes to the GitHub repo, it will automatically trigger a new build process, analyze the code by sending the code to SonarQube and the docker image will re-build and, the changes will reflect on Port 3000 of the Docker instance.

This completes our automated CI-CD Pipeline here.

Conclusion

In this blog, we learned about how to build a CI/CD Pipeline using AWS, Jenkins, SonarQube, and Docker. It helps us to automatically build, test, analyze, and deploy our web applications.