Building an End-to-End CI/CD Pipeline for Node.js Applications - Part 1: Jenkins Basic Setup

The first part of our Node.js CI/CD pipeline series, focusing on setting up a basic pipeline with Jenkins and Docker.

Building an End-to-End CI/CD Pipeline for Node.js Applications - Part 1: Jenkins Basic Setup

Table of Contents

Introduction

In this tutorial, we will learn how to set up a Continuous Integration and Continuous Deployment (CI/CD) pipeline for a Node.js application using Jenkins. We will build a basic pipeline that includes code checkout, dependency installation, security scanning, and deployment with Docker.

Code Repository

The sample code for this project is available at Clone Website GitHub Repository.

Prerequisites

Before starting this project, make sure you have the following prerequisites:

  1. Jenkins: Ensure Jenkins is installed and running on a server.
  2. Docker: Install Docker on your server.
  3. Node.js: Install Node.js on your server.
  4. npm: Install npm on your server.
  5. EC2 Instance: AWS EC2 instance of type t2.large (or equivalent from another cloud provider) to ensure enough resources.

Setting Up Jenkins

Let’s start by setting up Jenkins on an EC2 instance:

  1. SSH into the EC2 instance and first install Java:
sudo apt update -y
sudo apt install openjdk-11-jdk -y
java -version
  1. Download and install Jenkins:
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
sudo apt-get install jenkins

Remember to open port 8080 in your security group to access Jenkins.

  1. Access Jenkins in your browser using http://<your-ec2-ip>:8080

  2. Retrieve the initial admin password:

sudo cat /var/lib/jenkins/secrets/initialAdminPassword
  1. Install the suggested plugins when prompted.

  2. Create an admin user with your credentials.

Installing Required Jenkins Plugins

You need to install the following plugins:

  1. NodeJS Plugin
  2. SonarQube Scanner
  3. OWASP Dependency Check
  4. Docker Pipeline
  5. Docker Commons
  6. Docker API

To install these plugins:

  1. Go to Manage Jenkins → Manage Plugins → Available
  2. Search for each plugin and install it
  3. Restart Jenkins after installation

Configuring Jenkins Tools

Configure the global tools by going to Manage Jenkins → Global Tool Configuration:

  1. NodeJS: Add NodeJS → Name it as “node-18.16.0” → Install automatically → Select version 18.16.0

  2. SonarQube Scanner: Add SonarQube Scanner → Name it as “sonarqube” → Install automatically

  3. OWASP Dependency Check: Add OWASP Dependency Check → Name it as “DP” → Install automatically

Setting Up Docker

Install Docker on the Jenkins server:

sudo apt-get update
sudo apt-get install docker.io -y
sudo systemctl start docker
sudo docker run hello-world
sudo systemctl enable docker
docker --version
sudo usermod -a -G docker jenkins
sudo usermod -a -G docker $(whoami)
newgrp docker
sudo systemctl restart jenkins

Setting Up SonarQube

Install SonarQube using Docker:

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

Access SonarQube at http://<your-ec2-ip>:9000 with the default login credentials:

  • Username: admin
  • Password: admin

Generate a token for Jenkins integration:

  1. Log in to SonarQube
  2. Go to Administration → Security → Users
  3. Click on the update token option for the admin user
  4. Generate a token named “jenkins”
  5. Save the token for later use

Add the token to Jenkins:

  1. Go to Manage Jenkins → Manage Credentials → Global
  2. Add Credentials
  3. Choose “Secret text” as the kind
  4. In the Secret field, paste your SonarQube token
  5. Set ID as “sonar” and description as “sonar”
  6. Click OK

Creating a Jenkins Pipeline for Node.js

Now, let’s create a Jenkins pipeline:

  1. Go to Dashboard → New Item → Enter “nodejs-app-pipeline” as the name → Select Pipeline → OK
  2. Under “General”, check “Discard old builds” and set “Max # of builds to keep” to 2
  3. Under “Pipeline”, select “Pipeline script” and use the following script:
pipeline {
    agent any

    tools {
        nodejs 'node-18.16.0'
    }

    stages {
        stage('Git Checkout') {
            steps {
                git url: 'https://github.com/amodh-2002/Clone_Website.git', branch: 'main'
            }
        }

        stage('Install Dependencies') {
            steps {
                sh 'npm install'
            }
        }

        stage('OWASP Dependency Check') {
            steps {
                dependencyCheck additionalArgs: '--scan ./', odcInstallation: 'DP'
                dependencyCheckPublisher pattern: '**/dependency-check-report.html', projectToken: 'dependency-check-report'
            }
        }

        stage('Build Docker Image') {
            steps {
                script {
                    withDockerRegistry([credentialsId: 'docker-hub-credentials', toolName: 'docker']) {
                        sh 'docker build -t amodh-2002/clone_website:1.0.0 .'
                        sh 'docker push amodh-2002/clone_website:1.0.0'
                    }
                }
            }
        }

        stage('Deploy with Docker') {
            steps {
                script {
                    withDockerRegistry([credentialsId: 'docker-hub-credentials', toolName: 'docker']) {
                        sh 'docker stop nodejs-app || true'
                        sh 'docker rm nodejs-app || true'
                        sh 'docker run -d --name nodejs-app -p 80:80 amodh-2002/clone_website:1.0.0'
                    }
                }
            }
        }
    }

    post {
        success {
            echo 'Pipeline completed successfully!'
        }
        failure {
            echo 'Pipeline failed!'
        }
    }
}

Adding Docker Hub Credentials

Before running the pipeline, add Docker Hub credentials to Jenkins:

  1. Go to Manage Jenkins → Manage Credentials → Global
  2. Click on Add Credentials
  3. Choose Username with password
  4. Enter your Docker Hub username and password
  5. Set ID as “docker-hub-credentials” and description as “Docker Hub”
  6. Click OK

Understanding the Pipeline

Let’s break down each stage of our pipeline:

1. Git Checkout

This stage clones the Node.js application repository. The application is a simple website clone that we’ll be building and deploying.

2. Install Dependencies

This stage installs all the npm dependencies required by the application.

3. OWASP Dependency Check

This stage runs a security scan on the application dependencies to identify any known vulnerabilities.

4. Build Docker Image

This stage builds a Docker image for the application and pushes it to Docker Hub.

5. Deploy with Docker

This stage deploys the application by running a Docker container from the image we built.

Best Practices for Node.js CI/CD Pipelines

  1. Automated Testing: Add automated testing stages (unit tests, integration tests) to ensure code quality.

  2. Caching: Implement caching strategies for npm dependencies to speed up builds.

  3. Environment Variables: Use environment variables for sensitive information instead of hardcoding them.

  4. Versioning: Implement proper versioning for your Docker images.

  5. Rollback Strategy: Have a rollback strategy in case a deployment fails.

  6. Monitoring: Add monitoring to your pipeline to get insights into build times and failures.

Common Issues and Troubleshooting

  1. Node.js Version Compatibility: Ensure the Node.js version in Jenkins matches the one your application requires.

  2. Docker Permission Issues: If you encounter permission issues with Docker, make sure the Jenkins user is added to the Docker group.

  3. Network Issues: If the Docker image push fails, check your internet connection and Docker Hub credentials.

  4. Resource Limitations: If the build fails due to resource limitations, consider upgrading your instance or optimizing your build process.

Conclusion

In this tutorial, we’ve set up a basic CI/CD pipeline for a Node.js application using Jenkins and Docker. We’ve covered how to:

  1. Install and configure Jenkins
  2. Set up required plugins and tools
  3. Create a pipeline with multiple stages
  4. Build and deploy a Docker container

This setup provides a solid foundation for automating the deployment of Node.js applications.

In the next part of this series, we’ll explore how to implement a similar pipeline using CircleCI, offering an alternative approach to CI/CD for Node.js applications.

Table of Contents