"Starbucks Clone Reimagined: Automated & Secure Deployment with Jenkins, Docker, SonarQube and GitHub"

SAI PRASAD ANNAMSAI PRASAD ANNAM
10 min read

This blog post will guide you through setting up a CI/CD pipeline for a (Starbucks) Nodejs application, streamlining the build, deployment, and integration process. Using Jenkins, Docker, and GitHub, we'll automate every step, ensuring efficient and reliable software delivery.

Repository for this Project:

Project Overview

This project involves creating a complete CI/CD pipeline that automates the deployment of a (starbucks) Nodejs application. Here are the steps we will follow:

  1. Create AWS EC2 Instances to host Jenkins and Docker.

  2. Set up Jenkins to automate the CI/CD pipeline.

  3. Containerize the Nodej application using Docker.

  4. Create a Jenkinsfile for automated builds and deployment.

  5. Techstack used in this project :

    • GitHub

    • SonarQube

    • Trivy

    • Docker

    • Email

Steps to Implement the Project

Step 1: Create An AWS EC2 Instance

We'll start by setting up an instance:

  1. Log in to AWS:

    • Go to the AWS Console and log in.
  2. Launch an EC2 Instance (Jenkins-server):

    • Go to the EC2 Dashboard and click on Launch Instance.

    • Select the Ubuntu 24.04 LTS AMI.

    • Choose t2.medium for the Jenkins Master instance.

    • Configure Security Group:

      • SSH (port 22) for remote access.

      • HTTP (port 80) to access Jenkins through the browser.

    • Click Review and Launch.

Step 2: assign an Elastic IP (EIP) to a Jenkins server on AWS,

follow these steps:

Allocate an Elastic IP

  • Go to the EC2️ Dashboard in AWS Management Console.

  • Under Network & Security, click Elastic IPs.

  • Click Allocate new address and choose your region.

    Associate Elastic IP with Jenkins Server:

  • o In the Elastic IPs section, select the allocated EIP.

  • o Click Actions, then Associate Elastic IP address.

  • o Select the Jenkins server instance (EC2 instance) and click Associate.

    Verify Association:

  • Check if the EIP is now associated with the Jenkins EC2 instance.

  • Ensure Jenkins is accessible via the new IP.

    Benefits:

  • Static IP: Provides a fixed IP address that won't change even if you stop/start your EC2 instance. •

  • Easy Access: Direct and consistent access to Jenkins from external networks.

  • High Availability: In case of instance failure, the EIP can be reassigned to a new instance quickly.

    Assocated ip to server

Step 3 : Install the required Tools

SSH into instance using below command:

Update EC2 Instance

Ensure Ec2 instance are up to date by running:

ssh -i <your-key>.pem ubuntu@<your-ec2-public-ip>

sudo apt update && sudo apt upgrade -y

Install Java on EC2 Instance

Jenkins requires Java, so install OpenJDK 17 on each instance:

apt install openjdk-17-jre -y
java -version

Install Jenkins

we are going to build and deploy the application on jenkin-server, so no need to install the jenkins server on Ec2 machine

create a bash script with jenkins.sh and provide the executable permisssions to it

(Reference URL for commands: https://www.jenkins.io/doc/book/installing/linux/#debianubuntu)
#!/bin/bash
#this Script belong to Cloudaseem Youtube channel #####
# jenkins installation on ubuntu 
sudo apt update -y
sudo apt install fontconfig openjdk-17-jre -y
sudo wget -O /usr/share/keyrings/jenkins-keyring.asc \
  https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key
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
sudo apt-get update -y
sudo apt-get install jenkins -y 
sudo systemctl enable jenkins
sudo systemctl start jenkins

# Verifiy Jenkins installation: jenkins --version

#     Open Port No. 8080 for VM and access Jenkins
#     Setup Jenkins by following the necessary steps

Install Docker

create a bash script with docker.sh and provide the executable permisssions to it

#!/bin/bash
# Script to install Docker on an EC2 instance and configure permissions

# Update the package list
sudo apt-get update -y

# Install Docker
sudo apt-get install docker.io -y

# Add the 'ubuntu' and 'jenkins' users to the 'docker' group to allow running Docker without sudo
sudo usermod -aG docker ubuntu 
sudo usermod -aG docker jenkins 

# Apply the new group settings immediately
newgrp docker

# Set correct permissions for the Docker socket to allow 'docker' group members to access it
sudo chmod 660 /var/run/docker.sock
sudo chown root:docker /var/run/docker.sock

# Restart Docker service to apply changes
sudo systemctl restart docker

# Verify installation
docker -version

Install Trivy

Trivy is an open-source vulnerability scanner for containers and other artifacts, such as Kubernetes manifests. It can detect vulnerabilities in OS packages and application dependencies, ensuring your deployments are secure and compliant.

Why Use Trivy?

Comprehensive Scanning: Scans containers, filesystems, and repositories for vulnerabilities.

Ease of Use: Simple installation and easy-to-understand output.

Integration: Can be integrated into CI/CD pipelines for continuous security.

Install Trivy

#!/bin/bash
# Script to install Trivy on an instance

# Install necessary dependencies
sudo apt-get install wget apt-transport-https gnupg lsb-release -y

# Add the Trivy repository key
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null

# Add the Trivy repository to the sources list
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list

# Update package lists
sudo apt-get update -y

# Install Trivy
sudo apt-get install trivy -y

Step 4: Access the jenkins and do the following configuration and add credentails and add tools for necessary things

Access Jenkins in your browser:

http://<Elastic-ip>:8080

Unlock Jenkins using an administrative password and install the suggested plugins. Retrieve the initial admin password:

sudo cat /var/lib/jenkins/secrets/initialAdminPassword

Create a user click on save and continue.

Jenkins Getting Started Screen.

Follow the setup wizard and install recommended plugins

Install Plugins like JDK, SonarQube Scanner, NodeJs,

Check Goto Manage Jenkins →Plugins → Available Plugins → Install below plugins

1. Eclipse Temurin Installer (Install without restart)

2. SonarQube Scanner (Install without restart)

3. NodeJs Plugin (Install Without restart) – 16.20.2

4. Stage view

5. jdk

Docker plugin

6. Docker

7. Docker Commons

8. Docker Pipeline

9. Docker API

10. docker-build-step

Create a Gmail SMTP App Password

An App Password is a 16-character password that allows third-party applications (like Jenkins) to send emails using Gmail SMTP securely.

Step 1️: Enable 2️-Step Verification

Before generating an App Password, you must enable 2️-Step Verification in your Google Account.

  • Go to Google Account Security: Google My Account

  • Scroll to "Signing in to Google".

  • Click "2️-Step Verification" → Click "Get Started".

  • Follow the steps to set up 2️-Step Verification (via SMS or Authenticator App).

Step 2️: Generate an App Password

  • Go to App Passwords Page: Google App Passwords

  • Sign in with your Google Account.

  • Under "Select app", choose "Mail".

  • Under "Select device", choose "Other (Custom Name)" and enter "Jenkins SMTP".

  • Click "Generate".

  • Copy the 1️6-character App Password (e.g., abcd efgh ijkl mnop).

Add credentials as Username and password in jenkins

Step 3: Configure Gmail SMTP in Jenkins

Go to Jenkins Dashboard → Manage Jenkins → Configure System.

Scroll to "E-mail Notification".

Set the following:

  • SMTP Server: smtp.gmail.com

  • Use SMTP Authentication: Checked

  • User Name: Your Gmail ID (your-email@gmail.com)

  • Password: Paste the App Password

  • SMTP Port: 587

  • Use TLS:

  • Click Save.

scroll Extended Email Notification

Set the following:

SMTP Server: smtp.gmail.com

SMTP Port: 465 (for tls)

Use SSL: Checked

configure tools and credentials:

Set Up Docker Hub Credentials in Jenkins

  1. Go to Manage Jenkins > Security > Credentials > System > Global credentials (unrestricted) and click Add Credentials.

  2. Set Kind to Username with password.

  3. Add your Docker Hub username and password and save , for password generate Personal Access Token on DockerHub.

configure SonarQube Credentials

install sonarqube in another ec2 server and configure with jenkins

create a server with named sonar-server

Install the docker and create a sonarqube container

docker run -d --name sonar -p 9000:9000 sonarqube:lts-community

Access the application with <public-ip:9000>

update the sonarqube password

Tha Dashboard of sonarqube

Create Sonar token in order to connect with Jenkins

Click on Administration → Security → Click on Tokens and Update Token → Give it a name → and click on Generate Token

Add these generated token in jenkins Credentials like below

manage jenkins → credentials → add like below

To connect jenkins with SonarQube → add system configuarations in Jenkins

manage jenkins → system → ADD SonarQube

to connect SonarQube with jenkins → make sure enable webhooks in SonarQube

Administration → Webhooks

Global Tool Configuration:

add Jdk as a global tool as below

as Nodejs

add sonar-qube

Step 5: shared library in jenkins :

A shared library in Jenkins is a collection of Groovy scripts shared between different Jenkins jobs. To run the scripts, they are pulled into a Jenkinsfile.

Each shared library requires users to define a name and a method of retrieving source code. These methods include local files, Git repositories, and Jenkins SCM plugins.

Developers use shared libraries to avoid writing the same code from scratch for multiple projects. Shared libraries share code across development projects, thus optimizing the software development life cycle. This drastically cuts down time spent on coding and helps avoid duplicate code.

to access a shared library from Jenkins male sure to add those GitHub repos into jenkins as shown in below

step 1: create a credentials for GitHub as shown in below

Create a Shared Library Repository

  1. Create a GitHub Repository:

    • Go to GitHub and create a new repository.

    • Name it something like Jenkins-shared-library

    • set it to Public or Private (based on your needs), and click Create Repository.

  2. Clone the Repository:

  • Clone the repository to your local machine:

    
        https://github.com/Saiprasad-1727/Jenkins-shared-library.git
    

    and add the groovy scripts as follows which we are going to use in declarative pipeline

  • clone.groovy

    Handles code cloning from a Git repository:

        def call(String url, String branch){
          git url:url , branch:branch
        }
    

    dockerbuild.groovy

    Builds a Docker image:

        def call(String imageName, String imageTag){
          sh "docker build -t ${imageName}:${imageTag} ."
        }
    

    dockerpush.groovy

    Pushes a Docker image to Docker Hub:

        def call(String credId, String imageName,String imageTag){
          withCredentials([usernamePassword(credentialsId:credId,
                                            usernameVariable:"dockerHubUser",
                                            passwordVariable:"dockerHubPass")]){
                            sh "docker login -u ${env.dockerHubUser} -p ${env.dockerHubPass}"
                            sh "docker image tag ${imageName}:${imageTag} ${env.dockerHubUser}/${imageName}:${imageTag}"
                            sh "docker push ${env.dockerHubUser}/${imageName}:${imageTag}"
                  }
        }
    

    deploy.groovy

    Deploys a Docker container:

        def call(){
          sh "docker-compose down && docker-compose up -d"
        }
    

    Configure the Shared Library in Jenkins

    1. Access Jenkins:

      • Go to Manage Jenkins > Configure System.
    2. Add Global Pipeline Library:

      • Scroll to Global Pipeline Libraries and click Add.

      • Fill in the details:

        • Name: Jenkins-shared-library(matches @Library('Jenkins-shared-library') in your Jenkinsfile).

        • Default Version: main.

        • Retrieval Method: Modern SCM.

        • Source Code Management:

          • Select Git.

          • Add the repository URL:

            Copy

            Copy

                  https://github.com/Saiprasad-1727/Jenkins-shared-library.git
            
          • Add credentials if required (e.g., GitHub token or SSH key).

    3. Save Configuration:

      • Click Save.

step 6: Create the jenkins pipeline for continious integration and deployment

jenkins > new item > Enter an item name as starbucks > select pipeline option and ok

write the description as per your wish

go to pipeline option on left side and start writing declarative pipeline for ci/cd as below

@Library('jenkins_shared_library') _
pipeline {
    agent any
    tools{
        jdk 'jdk'
        nodejs 'node17'
    }
    environment {
        SCANNER_HOME=tool 'sonar-scanner'
    }
    stages {
        stage('clean workspace'){
            steps{
                cleanWs()
            }
        }
        stage("checkout from Git") {
            steps {
                git branch: 'main', url: 'https://github.com/Saiprasad-1727/kubernetes-project-starbucks.app.git'
            }
        }
        stage("Sonarqube Analysis "){
            steps{
                withSonarQubeEnv('SonarQube') {
                    sh ''' $SCANNER_HOME/bin/sonar-scanner -Dsonar.projectName=starbucks \
                    -Dsonar.projectKey=starbucks '''
                }
            }
        }
        stage("quality gate"){
           steps {
                script {
                    waitForQualityGate abortPipeline: false, credentialsId: 'Sonar-token' 
                }
            } 
        }
        stage('Install Dependencies') {
            steps {
                sh "npm install"
            }
        }        
        stage('TRIVY FS SCAN') {
            steps {
                sh "trivy fs . > trivyfs.txt"
            }
        }
        stage('building the image'){
            steps{
                dockerbuild("starbucks","latest")
            }
        }
        stage('push image to hub'){
            steps{
                dockerpush("dockerHubCred","starbucks","latest")
            }
        }
        stage("TRIVY"){
            steps{
                sh "trivy image saiprasad1727/starbucks:latest > trivyimage.txt" 
            }
        }
        stage('App Deploy to Docker container'){
            steps{
                sh 'docker run -d --name starbucks -p 3000:3000 saiprasad1727/starbucks:latest'
            }
        }
    }
    post {
    always {
        emailext attachLog: true,
            subject: "'${currentBuild.result}'",
            body: """
                <html>
                <body>
                    <div style="background-color: #FFA07A; padding: 10px; margin-bottom: 10px;">
                        <p style="color: white; font-weight: bold;">Project: ${env.JOB_NAME}</p>
                    </div>
                    <div style="background-color: #90EE90; padding: 10px; margin-bottom: 10px;">
                        <p style="color: white; font-weight: bold;">Build Number: ${env.BUILD_NUMBER}</p>
                    </div>
                    <div style="background-color: #87CEEB; padding: 10px; margin-bottom: 10px;">
                        <p style="color: white; font-weight: bold;">URL: ${env.BUILD_URL}</p>
                    </div>
                </body>
                </html>
            """,
            to: 'annam.saiprasad2709@gmail.com',
            mimeType: 'text/html',
            attachmentsPattern: 'trivy.txt'
        }
    }
}

Click on Build Now and check the stage view, if any stage got error click on builds and the check the console output and verify the errors and try to correct the pipeline and any configuration.

Final output

Access the application with <public-ip:3000>

http://3.109.147.176:3000

Conclusion

By following these steps, we have successfully set up a CI/CD pipeline to automate the deployment of your Starbucks Application using Jenkins, GitHub, and Docker, shared libraries,etc. This setup not only simplifies the deployment process but also enhances productivity.

Before you leave

If you enjoy the content I share, feel free to connect with me on Sai Prasad Annam there’s a lot more to explore, and I think you’ll find it intriguing!"

And feel free check out my devops projects publication Dashboard - Overview

0
Subscribe to my newsletter

Read articles from SAI PRASAD ANNAM directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

SAI PRASAD ANNAM
SAI PRASAD ANNAM

Hi there! I'm Sai Prasad Annam, an enthusiastic and aspiring DevOps engineer and Cloud engineer with a passion for integrating development and operations to create seamless, efficient, and automated workflows. I'm driven by the challenges of modern software development and am dedicated to continuous learning and improvement in the DevOps field.