Building the Ultimate CI/CD Pipeline using Jenkins along with Maven, SonarQube and Docker to build the Image and Deploy in Kubernetes using ArgoCd.

Introduction

Here in this Project, we will deploy the simple JAVA application on Kubernetes following the CI/CD practices. We will use Jenkins as our Continuous Integration tool and we use ArgoCd as a CD part and deploy our app in Kubernetes using it. We use tools like Maven for building the app, sonarqube for the static code analysis, use Docker to build the images and push them to the docker hub. We also use scripting to automate the image tag in the deployment yaml file.

Objective

To Deploy the simple JAVA application on k8s using the CI/CD pipeline.

Continuous integration(CI)

The Steps followed in this part of the project are as follows:

AWS

We first Create the AWS EC2 Instance ( t2.large) where we will install all our tools.

Now after creating the AWS EC2 instance the next work is to connect this instance from our terminal through SSH.

ssh -i <path_to_private_key_file> @<public_dns_name_or_ip_address>

Installing Jenkins

Now we should install Java as a prerequisite for Installing Jenkins

sudo apt update

sudo apt install openjdk-11-jre

Check if Java is installed

java -version

Now we can install the Jenkins

curl -fsSL https://pkg.jenkins.io/debian/jenkins.io-2023.key | sudo tee \ /usr/share/keyrings/jenkins-keyring.asc > /dev/null

echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \ https://pkg.jenkins.io/debian binary/ | sudo tee \ /etc/apt/sources.list.d/jenkins.list > /dev/null

sudo apt-get update

sudo apt-get install jenkins

Jenkins Is Installed in the EC2 instance.

Jenkins can be accessed in default port 8080. But for accessing Jenkins we first add the Inbound rule giving access to the port 8080.

We can check the Jenkins using this command in the terminal

ps -ef | grep jenkins

We create the pipeline project in Jenkins.

We provide the Jenkinsfile to the Jenkins through SCM i.e. Git Hub.

The Link to the source code is here.

Here is the jenkinfile that is used in the pipeline process:

pipeline {
  agent {
    docker {
      image 'abhishekf5/maven-abhishek-docker-agent:v1'
      args '--user root -v /var/run/docker.sock:/var/run/docker.sock' // mount Docker socket to access the host's Docker daemon
    }
  }
  stages {
    stage('Checkout') {
      steps {
        sh 'echo passed'

      }
    }
    stage('Build and Test') {
      steps {
        sh 'ls -ltr'
        // build the project and create a JAR file
        sh 'cd java-maven-sonar-argocd-helm-k8s/spring-boot-app && mvn clean package'
      }
    }
    stage('Static Code Analysis') {
      environment {
        SONAR_URL = "http://15.206.122.238:9000"
      }
      steps {
        withCredentials([string(credentialsId: 'sonarqube', variable: 'SONAR_AUTH_TOKEN')]) {
          sh 'cd java-maven-sonar-argocd-helm-k8s/spring-boot-app && mvn sonar:sonar -Dsonar.login=$SONAR_AUTH_TOKEN -Dsonar.host.url=${SONAR_URL}'
        }
      }
    }
    stage('Build and Push Docker Image') {
      environment {
        DOCKER_IMAGE = "amritpoudel/ultimate-cicd:${BUILD_NUMBER}"
        // DOCKERFILE_LOCATION = "java-maven-sonar-argocd-helm-k8s/spring-boot-app/Dockerfile"
        REGISTRY_CREDENTIALS = credentials('docker-cred')
      }
      steps {
        script {
            sh 'cd java-maven-sonar-argocd-helm-k8s/spring-boot-app && docker build -t ${DOCKER_IMAGE} .'
            def dockerImage = docker.image("${DOCKER_IMAGE}")
            docker.withRegistry('https://index.docker.io/v1/', "docker-cred") {
                dockerImage.push()
            }
        }
      }
    }
    stage('Update Deployment File') {
        environment {
            GIT_REPO_NAME = "Jenkins-Zero-To-Hero"
            GIT_USER_NAME = "PoudelAmrit123"
        }
        steps {
            withCredentials([string(credentialsId: 'github', variable: 'GITHUB_TOKEN')]) {
                sh '''
                    git config user.email "amritpoudel433@gmail.com"
                    git config user.name "Amrit Poudel"
                    BUILD_NUMBER=${BUILD_NUMBER}
                    sed -i "s/replaceImageTag/${BUILD_NUMBER}/g" java-maven-sonar-argocd-helm-k8s/spring-boot-app-manifests/deployment.yml
                    git add java-maven-sonar-argocd-helm-k8s/spring-boot-app-manifests/deployment.yml
                    git commit -m "Update deployment image to version ${BUILD_NUMBER}"
                    git push https://${GITHUB_TOKEN}@github.com/${GIT_USER_NAME}/${GIT_REPO_NAME} HEAD:main
                '''
            }
        }
    }
  }
}

We use docker as an agent which will help us achieve a reduced footprint of build agents, minimizing resource consumption such as memory and CPU.

We use Maven for building the Java application.

We use Sonarqube for the static code testing as well as Docker to build the images of the application and push it on the docker hub.

Setting Up SonarQube

We create the new user named sonarqube in our EC2 instances from the root user.

adduser sonarqube

Switching to the sonarqube user we can now install the sonarqube following the following steps:

wget https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-9.4.0.54424.zip

unzip *

chmod -R 755 /home/sonarqube/sonarqube-9.4.0.54424

chown -R sonarqube:sonarqube /home/sonarqube/sonarqube-9.4.0.54424 cd sonarqube-9.4.0.54424/bin/linux-x86-64/

./sonar.sh start

Now we can access the sonar Qube in port 9000.

Installing Docker

Now we install the docker in our root user following the command:

sudo apt update

sudo apt install docker.io

Grant Jenkins user and Ubuntu user permission to docker daemon.

sudo su -

usermod -aG docker jenkins

usermod -aG docker ubuntu

systemctl restart docker

We restart our Jenkins as Restarting Jenkins after downloading and installing plugins is necessary to ensure that the new plugins are fully loaded and integrated into the Jenkins environment.

Pipeline

The Jenkinsfile we provide to Jenkins is used for automating our tasks. The Maven used for build and test of our application looks out for pom.xml file for installing the required dependencies for building the app.

We configure the required credential of Sonarqube, docker, and GitHub.

After configuring we again restarted the Jenkins server.

We build the pipeline and wait for the pipeline to be passed successfully. we Passed the pipeline-building process on 2nd attempt.

As per the Jenkinsfile ( mentioned above), docker is used as an agent to build the container required to run the steps.

The first stage is for checking if Jenkins is able to clone the GitHub file or not.

In the second stage, Jenkins tries to build the file using the maven.

In The Third stage sonarqube is used for the static code test and Jenkins sends the response back to the sonarqube server.

fig: Sonarqube server

In the fourth stage, we build the image of the application and push it to the docker hub.

fig: DockerHub Dashboard.

And in the final stage, we update our deployment yml file using scripting.

In This way we automate the building, testing, building the images, and pushing them to the docker hub.

Continuous Deployment(CD)

As the next part, we should implement the CD part where we use ArgoCd to deploy our app images in Kubernetes.

We can use our local terminal and with the help of Minikube, we can achieve the required process.

We use the Kubernetes operator to install the Kubernetes Controller. We will install ArgoCd using the Kubernetes operator.

We follow this step to install our Operator. Now using this operator we install our required controller i.e. ArgoCd

we use the most minimal valid manifest to create a new Argo CD cluster with the default configuration.

apiVersion: argoproj.io/v1alpha1
kind: ArgoCD
metadata:
  name: example-argocd
  labels:
    example: basic
spec: {}

We name this argocd-basic.yml

We build this using the command

kubectl apply -f argocd-basic.yml

We can list out the services using the command:

kubectl get svc

We edit the argocd-server type as Nodeport from clusterIP

Minikube provides us with a special command which it will generate the URL using which we can access in our browser.

minikube service argocd-server argocd-server is the name of the svc.

It will provide us the URL through which we can access the ArgoCd.The username is admin and the password is stored in the secret.

After logging we create the new app and enter the required field as necessary. And it will take the deployment file we wrote and pushed in the Git Hub and deploy the images in Kubernetes.

ArgoCd

In This way, we deploy our App in Kubernetes following the CI/CD practices.

Conclusion

In conclusion, implementing a CI/CD pipeline using SonarQube, Maven, and Docker helps us to achieve reliable software development. Jenkins is used to implement the CI part and Argocd is used for the CD part. At last, I will like to thank Abhishek Veeramalla for the nice tutorial which is beneficial for a DevOps learner like me who is relatively new in the field of DevOps.