Building an End-to-End CI/CD Pipeline with AWS, Jenkins, Docker, SonarQube, ArgoCD and Kubernetes
Table of contents
- 1. Create an EC2 instance in AWS:
- 2. Install and configure Jenkins:
- 3. Fork the GitHub repository and create a webhook:
- 4. Build a new item in Jenkins:
- 5. Installing plugins:
- 6. Install SonarQube in Ubuntu and configure it in Jenkins:
- 7. Adding Credentials in Jenkins
- 8. Install Docker and configure it in Jenkins:
- 9. Writing Jenkinsfile and manifest file:
- 10. Build the pipeline:
- Now let’s perform continuous deployment using ArgoCD in Kubernetes in the local system
- 1. Install Dependencies:
- 2. Install kubectl and minikube and start minikube
- 3. Installing and configuring Argo CD
- 4. Setting up Argo CD for deployment in K8s
- 5. Testing the deployment from the browser:
- Clean up
This tutorial guides you through setting up a comprehensive CI/CD pipeline using AWS, Jenkins, Docker, SonarQube, ArgoCD and Kubernetes. It covers creating an EC2 instance, installing/configuring Jenkins and SonarQube, adding credentials, installing Docker, building the pipeline, deploying with ArgoCD in Kubernetes, and performing cleanup. By following this tutorial, you'll gain hands-on experience in automating the build, test, and deployment processes of your applications.
Prerequisites:
An AWS account with appropriate permissions to create EC2 instances and open ports.
Basic familiarity with Linux commands and AWS EC2.
Access to the GitHub repository and appropriate permissions to create webhooks.
A DockerHub account for pushing Docker images.
Let's go through the process:
1. Create an EC2 instance in AWS:
Log in to the AWS Management Console and navigate to the EC2 service.
Click on "Launch Instance" and select the Ubuntu t2.large instance type.
Launch the instance and make a note of the public IP address.
Allocate the elastic IP address and associate it with the newly created EC2 instance.
Edit inbound rules:
Port 22: for SSH
Port 80 and 443: for HTTP and HTTPS
Port 8080: for Jenkins
Port 9000: for SonarQube
2. Install and configure Jenkins:
SSH into the EC2 instance using a terminal or SSH client.
Update the system packages: sudo apt update
Install Jenkins by following the official Jenkins documentation for Ubuntu: https://www.jenkins.io/doc/book/installing/linux/
or my previous blog: https://rupaks.hashnode.dev/jenkins-cicd
Once installed, access Jenkins by navigating to http://<public-ip>:8080 in a web browser.
Follow the on-screen instructions to complete the Jenkins setup, including installing suggested plugins.
3. Fork the GitHub repository and create a webhook:
Fork the repository at https://github.com/Rupak-Shrestha/Jenkins-Zero-To-Hero to your GitHub account.
In your forked repository, go to "Settings" > "Webhooks" > "Add webhook".
Set the Payload URL to http://<public-ip>:8080/github-webhook/
Select "Just the push event" under "Which events would you like to trigger this webhook?".
Save the webhook configuration.
4. Build a new item in Jenkins:
5. Installing plugins:
Install the docker pipeline plugin:
Install SonarQube Scanner plugin:
6. Install SonarQube in Ubuntu and configure it in Jenkins:
SSH into the EC2 instance.
Follow the official SonarQube documentation for Ubuntu installation: https://docs.sonarqube.org/latest/setup/install-server/
# Install the 'unzip' package
apt install unzip
# Create a new user named 'sonarqube'
adduser sonarqube
# Switch to the 'sonarqube' user
sudo su - sonarqube
# Download SonarQube zip package
wget https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-9.4.0.54424.zip
# Unzip the downloaded package
unzip *.zip
# Set appropriate permissions for the SonarQube directory
chmod -R 755 /home/sonarqube/sonarqube-9.4.0.54424
# Change ownership of the SonarQube directory to the 'sonarqube' user
chown -R sonarqube:sonarqube /home/sonarqube/sonarqube-9.4.0.54424
# Change the current directory to the SonarQube bin directory
cd sonarqube-9.4.0.54424/bin/linux-x86-64/
# Start SonarQube
./sonar.sh start
Once SonarQube is installed and running, access it through
http://<public-ip>:9000
Username and Password both: admin
7. Adding Credentials in Jenkins
Connecting Jenkins and SonarQube:
Generate token in SonarQube:
Copy the token.
Create credentials in jenkins:
Paste the token in secret.
Adding credentials for DockerHub in Jenkins:
Adding credentials for github in Jenkins:
Generate a token:
Copy the token and paste on secret:
Restart Jenkins.
8. Install Docker and configure it in Jenkins:
SSH into the EC2 instance.
Follow the official Docker documentation for Ubuntu installation: https://docs.docker.com/engine/install/ubuntu
# Update the package lists
sudo apt update
# Install Docker
sudo apt install docker.io
# Switch to the root user
sudo su -
# Add the 'jenkins' user to the 'docker' group
usermod -aG docker jenkins
# Add the 'ubuntu' user to the 'docker' group
usermod -aG docker ubuntu
# Grant permissions to the Docker socket
sudo chmod 666 /var/run/docker.sock
# Restart the Docker service
systemctl restart docker
9. Writing Jenkinsfile and manifest file:
Jenjinsfile:
pipeline {
agent {
docker {
image 'abhishekf5/maven-abhishek-docker-agent:v1' // Docker image to use for the pipeline
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' // Print a message
// git branch: 'main', url: 'https://github.com/iam-veeramalla/Jenkins-Zero-To-Hero.git' // Git checkout command
}
}
stage('Build and Test') {
steps {
sh 'ls -ltr' // List files and directories in the current directory
// 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://3.98.176.181:9000" // SonarQube server URL
}
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}' // Run SonarQube analysis
}
}
}
stage('Build and Push Docker Image') {
environment {
DOCKER_IMAGE = "rupaks/ultimate-cicd:${BUILD_NUMBER}" // Docker image name with the build number
// DOCKERFILE_LOCATION = "java-maven-sonar-argocd-helm-k8s/spring-boot-app/Dockerfile" // Dockerfile location
REGISTRY_CREDENTIALS = credentials('docker-cred') // Docker registry credentials
}
steps {
script {
sh 'cd java-maven-sonar-argocd-helm-k8s/spring-boot-app && docker build -t ${DOCKER_IMAGE} .' // Build Docker image
def dockerImage = docker.image("${DOCKER_IMAGE}")
docker.withRegistry('https://index.docker.io/v1/', "docker-cred") {
dockerImage.push() // Push Docker image to the registry
}
}
}
}
stage('Update Deployment File') {
environment {
GIT_REPO_NAME = "Jenkins-Zero-To-Hero" // GitHub repository name
GIT_USER_NAME = "Rupak-Shrestha" // GitHub username
}
steps {
withCredentials([string(credentialsId: 'github', variable: 'GITHUB_TOKEN')]) {
sh '''
git config user.email "rpksht35@gmail.com"
git config user.name "Rupak Shrestha"
BUILD_NUMBER=${BUILD_NUMBER}
sed -i "s/replaceImageTag/${BUILD_NUMBER}/g" java-maven-sonar-argocd-helm-k8s/spring-boot-app-manifests/deployment.yml // Replace image tag in the deployment file
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 // Push changes to GitHub repository
'''
}
}
}
}
}
Changes to be made in file: java-maven-sonar-argocd-helm-k8s/spring-boot-app/JenkinsFile
Manifest: deployment.yml file
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-boot-app
labels:
app: spring-boot-app
spec:
replicas: 2 # Number of replicas for the deployment
selector:
matchLabels:
app: spring-boot-app
template:
metadata:
labels:
app: spring-boot-app
spec:
containers:
- name: spring-boot-app
image: rupaks/ultimate-cicd:replaceImageTag # Docker image for the container
ports:
- containerPort: 8080 # Port on which the container listens
Changes to be made in file:
java-maven-sonar-argocd-helm-k8s/spring-boot-app-manifests/deployment.yml
10. Build the pipeline:
Create a repository in your dockerhub: ultimate-cicd
or as per the changes you made in your groovy script.
Then, build your pipeline.
After the build is successful, an image is pushed to dockerhub on build and push docker image stage. We can check it from dockerhub:
Now let’s perform continuous deployment using ArgoCD in Kubernetes in the local system
To install and use Minikube on Ubuntu, you can follow these steps:
1. Install Dependencies:
- Open a terminal on your Ubuntu machine.
# Update the package list:
sudo apt update
# Install the necessary dependencies:
sudo apt install curl virtualbox
2. Install kubectl and minikube and start minikube
kubectl is the command-line tool used to interact with Kubernetes clusters. Install it by running:
sudo snap install kubectl --classic
Download the Minikube binary using curl:
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
Make the downloaded binary executable:
sudo install minikube-linux-amd64 /usr/local/bin/minikube
Start Minikube with the default settings by running the following command:
minikube start --memory=4098 --driver=virtualbox
This command will start Minikube with the specified memory allocation using the VirtualBox driver as the virtualization solution on Ubuntu.
To access the Kubernetes dashboard, run:
minikube dashboard
This will open the dashboard in your default web browser.
3. Installing and configuring Argo CD
Goto: https://operatorhub.io/operator/argocd-operator
# Install Operator Lifecycle Manager (OLM)
curl -sL https://github.com/operator-framework/operator-lifecycle-manager/releases/download/v0.24.0/install.sh | bash -s v0.24.0
# Create Argo CD Operator using the provided YAML file
kubectl create -f https://operatorhub.io/install/argocd-operator.yaml
# Get the pods in the 'operators' namespace
kubectl get pods -n operators
Create: 1 file (eg: argocd.yml) and paste the following code into the file:
From: https://argocd-operator.readthedocs.io/en/latest/usage/basics/
apiVersion: argoproj.io/v1alpha1
kind: ArgoCD
metadata:
name: example-argocd # Name of the Argo CD instance
labels:
example: basic # Labels for identification or categorization
spec: {} # Empty specification, no additional configuration provided
In terminal:
# Apply the configuration in the argocd.yml file
kubectl apply -f argocd.yml
# Get the pods in the cluster
kubectl get pods
Edit argocd-server so that the ArgoCD dashboard can be accessible from the browser.
# Get the list of services in the cluster
kubectl get svc
# Edit the example-argocd-server service
kubectl edit svc example-argocd-server
Change type: from ClusterIP to NodePort
# Get the list of services in the cluster
kubectl get svc
# List the services exposed by minikube
minikube service list
You'll get the URL from where you can access the ArgoCD dashboard.
Username: admin
for password:
# Get the list of secrets in the cluster
kubectl get secret
# Edit the example-argocd-cluster secret
kubectl edit secret example-argocd-cluster
copy admin.password
The password is base64 encrypted, so in terminal:
# Decode the base64-encoded string
echo T21vcWJTS1VJOHd6WkdjanAzYUZDRUIxSm5OUjJ2bDQ= | base64 -d
Copy the output and use it as a password.
4. Setting up Argo CD for deployment in K8s
Application deployed successfully:
Checking from terminal:
Two pods are running as the replica is set to 2 in deployment.yml file.
5. Testing the deployment from the browser:
# kubectl describe pod <pod_name>
# Describe the details of the pod with the name spring-boot-app-5878ccfc4-gbzfj
kubectl describe pod spring-boot-app-5878ccfc4-gbzfj
# kubectl port-forward pod/<pod_name> <local_port>:<application_port>
# Forward the local port 8010 to the port 8080 of the pod spring-boot-app-5878ccfc4-gbzfj
kubectl port-forward pod/spring-boot-app-5878ccfc4-gbzfj 8010:8080
Checking the application from the browser:
Clean up
Delete the instance
Disassociate and release elastic ip address
Stop and Delete Minikube Cluster:
When you're finished working with Minikube, you can stop and delete the cluster by running:
# Stop the minikube cluster minikube stop # Delete the minikube cluster minikube delete
This will stop the cluster and delete its resources.
To summarize, this tutorial offers a detailed and hands-on walkthrough for establishing a resilient CI/CD pipeline utilizing AWS, Jenkins, Docker, SonarQube, and ArgoCD. By following the step-by-step instructions, you can successfully automate the build, test, and deployment processes of your applications. This tutorial equips you with the knowledge and hands-on experience necessary to streamline your software development workflow, increase efficiency, and ensure consistent delivery of high-quality software. Embracing CI/CD practices can significantly enhance your development process and enable you to adapt to the ever-changing demands of the software industry.
Thank you for reading and Happy Learning! 🎉