Introduction
Continuous Integration and Continuous Deployment (CI/CD) is a game-changer in modern software development. It streamlines code integration, testing, and deployment, ensuring rapid and reliable delivery of applications. In this blog, I’ll share my experience setting up a CI/CD pipeline for MyNotes, a simple application, using Jenkins, GitHub, Docker, and Webhooks.
Why Automate Deployment?
Manually building, testing, and deploying applications can be time-consuming and prone to errors. Automating these steps with a CI/CD pipeline offers:
Faster delivery cycles
Minimized manual intervention
Consistent and reliable deployments
Immediate feedback on code changes
CI/CD Pipeline Overview
The goal is to automate the Build, Test, and Deploy stages every time code is pushed to the GitHub repository. Here’s how the process works:
Setting Up the CI/CD Pipeline
1️⃣ Installing and Configuring Jenkins
Jenkins is the automation server at the heart of this pipeline. I installed Jenkins on my local machine using:
sudo apt update
sudo apt install openjdk-11-jdk
wget -q -O - https://pkg.jenkins.io/debian/jenkins.io.key | sudo apt-key add -
echo "deb http://pkg.jenkins.io/debian-stable binary/" | sudo tee /etc/apt/sources.list.d/jenkins.list
sudo apt update
sudo apt install jenkins
After starting Jenkins, I installed the required plugins: GitHub, Docker Pipeline, and Credentials Binding.
2️⃣ Configuring GitHub Webhook
To automatically trigger Jenkins on a code push, I:
Generated a Personal Access Token with
repo
andadmin:repo_hook
permissions.Configured a Webhook in my GitHub repository with the URL:
http://my-jenkins-server/github-webhook/
.
3️⃣ Writing the Jenkinsfile
The Jenkins pipeline script (Jenkinsfile
) defines the Build, Push to DockerHub, and Deploy stages:
pipeline {
agent { label "agent1" }
stages {
stage('Build') {
steps {
echo "Cloning repository..."
git url: "https://github.com/o/mynotes.git", branch: "main" # Cloning the Repo
echo "Building Docker image..."
sh "docker build -t mynotes:latest ." # Building Docker image
}
}
stage('Push to DockerHub') {
steps {
echo "Pushing image to DockerHub..."
withCredentials([usernamePassword(credentialsId: 'dockerHubCred',
usernameVariable: 'dockerHubUser', passwordVariable: 'dockerHubPass')]) { # Adding Username & Pass with essential security measures.
sh "echo \"$dockerHubPass\" | docker login -u \"$dockerHubUser\" --password-stdin"
sh "docker tag mynotes:latest $dockerHubUser/mynotes:latest"
sh "docker push $dockerHubUser/mynotes:latest" # Push on DockerHub
}
}
}
stage('Deploy') {
steps {
echo "Deploying application..."
sh "docker-compose up -d" # Deploy the appication.
}
}
}
}
4️⃣ Running the Pipeline
Once the Jenkins pipeline was set up, I tested it by committing changes to GitHub. The webhook successfully triggered Jenkins, and the pipeline executed all stages seamlessly.
Pipeline Execution Outputs
Below are some outputs observed during pipeline execution:
1. Build Stage Output:
Cloning repository...
Building Docker image...
Successfully built 3a7e72f1c9c3
Successfully tagged mynotes:latest
2. Push to DockerHub Stage Output:
Pushing image to DockerHub...
Login Succeeded
The push refers to repository [docker.io/yourusername/mynotes]
latest: Pushed
3. Deploy Stage Output:
Deploying application...
Creating network "mynotes_default" with the default driver
Creating mynotes_app_1 ... done
Console output:
Troubleshooting Issues
During the setup, I encountered and resolved the following issues:
GitHub Webhook Not Triggering → Fixed by using ngrok to expose Jenkins publicly.
Jenkins Agent Going Offline → Ensured proper file permissions and stable connectivity.
DockerHub Authentication Failed → Resolved by enabling 2FA and using a Docker Access Token.
Conclusion
With this automated CI/CD pipeline, MyNotes is now continuously integrated and deployed with minimal effort. This setup ensures that each code change is instantly built, tested, and deployed, keeping the application reliable and up-to-date.