CI/CD Pipeline for Node.js App Deployment on AWS ECS

bhaktraj - Feb 26 - - Dev Community

Architecture
Overview:
This project implements a fully automated CI/CD pipeline using Jenkins to streamline the deployment of a Node.js application on AWS ECS. The pipeline ensures code quality, security, and high availability by integrating SonarQube, OWASP Dependency-Check, Docker, AWS ECR, and ECS. Additionally, Elastic Load Balancer (ELB) is used to distribute traffic efficiently.

Key Features & Workflow:
✅ Code Fetching: Jenkins pulls the latest source code from GitHub.
✅ Code Quality Analysis: SonarQube scans the code for vulnerabilities & maintainability issues.
✅ Security Scan: OWASP Dependency-Check detects security vulnerabilities in dependencies.
✅ Build & Packaging: Dependencies are installed using npm.
Docker image is built and tagged with a unique build number.
✅ Docker Image Push: The image is securely pushed to AWS Elastic Container Registry (ECR).
✅ AWS ECS Deployment: The application is deployed as a container on AWS ECS (Fargate/EC2).The service is restarted using force-new-deployment to fetch the latest image.
✅ Traffic Management: The ECS service is connected to an Elastic Load Balancer (ELB) for high availability & traffic distribution.
✅ Automated Rollouts: If a deployment fails at any stage, Jenkins stops the pipeline to prevent bad code from reaching production.

Steps:
Step 1 : Launch 2 instance on AWS EC2 of t2medium type

  • one for Jenkins
  • another for SonarQube

Step 2 : install docker in both of them

#!/bin/bash
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
sudo usermod -aG docker $USER && newgrp docker

Enter fullscreen mode Exit fullscreen mode

or

sudo apt update
sudo apt install docker.io -y
sudo usermod -aG docker $USER && newgrp docker

Enter fullscreen mode Exit fullscreen mode

Step 3 : Install Jenkins in one Inststance
for installing Jenkins
https://dev.to/bhaktraj/how-to-install-jenkins-in-ubuntu-2735

or

#!/bin/bash
sudo apt update 
sudo apt install openjdk-21-jre-headless -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
sudo apt-get install jenkins -y
sudo systemctl enable jenkins
sudo systemctl start jenkins

Enter fullscreen mode Exit fullscreen mode

Once Jenkins is installed, you will need to go to your AWS EC2 Security Group and open Inbound Port 8080, since Jenkins works on Port 8080.

Now, grab your Public IP Address
url = http://ec2_public_IPaddress:8080

for password

sudo cat /var/lib/jenkins/secrets/initialAdminPassword
Enter fullscreen mode Exit fullscreen mode

after that set the jenkins

Step 4 : Run SonarQube container on another Instance

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

Enter fullscreen mode Exit fullscreen mode

once SonarQube is running you will need to allow inbound rule for port 9000 in EC2 security group, since SonarQube running on 9000 port

url = http://ec2_public_IPaddress:9000

username = admin
password = admin

Step 5 : Install Plugins like JDK, Sonarqube Scanner, NodeJs, OWASP Dependency Check, docker AWS on Jenkins
Goto Manage Jenkins →Plugins → Available Plugins →

Install Plugin

  • SonarQube Scanner
  • NodeJs Plugin
  • Install OWASP Dependency Check
  • Docker
  • Docker Commons
  • Docker Pipeline
  • Docker API
  • docker-build-step
  • AWS Steps
  • AWS Credentials
  • Amazon Web Services SDK :: All

5.1 Configure Java and Nodejs in Global Tool Configuration

  • Goto Manage Jenkins → Tools → Install JDK(17) and NodeJs(16)→ Click on Apply and Save

Step 6 : Configure Sonar Server in Manage Jenkins

Grab the Public IP Address of your EC2 Instance, Sonarqube works on Port 9000, so :9000. Goto your Sonarqube Server. Click on Administration → Security → Users → Click on Tokens and Update Token → Give it a name → and click on Generate Token

click on update Token

Create a token with a name and generate

copy Token

Goto Jenkins Dashboard → Manage Jenkins → Credentials → Add Secret Text.

Now, go to Dashboard → Manage Jenkins → System and Add Server url , name and and authentication credation

The Configure System option is used in Jenkins to configure different server

Global Tool Configuration is used to configure different tools that we install using Plugins

We will install a sonar scanner in the tools.

Step 7 : we had to configure the Tool OWASP Dependency Check

Goto Dashboard → Manage Jenkins → Tools →

Step 8 : Now go configure → Pipeline and add this stage to your pipeline and build.

pipeline {
    agent any
    tools {
        jdk 'jdk17'
        nodejs 'node16'
    }
    environment {
        scannerhome = tool 'sonarserver'
        imagename = '058264453864.dkr.ecr.us-east-1.amazonaws.com/nodejsapp'
        awscred = 'ecr:us-east-1:awscred'
        registeryurl='https://058264453864.dkr.ecr.us-east-1.amazonaws.com/nodejsapp'
        cluster='nojejsproject'
        service='nodejsprojectservice'
    }
    stages{
        stage('clean workspace'){
            steps{
                cleanWs()
            }
        }
        stage("Fetch Code"){
            steps{
                git url: 'https://github.com/bhaktraj/zomatocicd.git', branch:'main'
            }
        }
        stage("Sonarqube analyse "){
            steps{
                withSonarQubeEnv('sonarserver') {
                   sh '''$scannerhome/bin/sonar-scanner -Dsonar.projectName=zomato \
                    -Dsonar.projectKey=zomato '''
              }
            }
        }
        stage("Quality Gate") {
            steps {
                timeout(time: 1, unit: 'MINUTES') {
                    // Parameter indicates whether to set pipeline to UNSTABLE if Quality Gate fails
                    // true = set pipeline to UNSTABLE, false = don't
                    waitForQualityGate abortPipeline: true
                }
            }
        }
        stage("install Dependences"){
            steps{
                sh 'npm install'
            }
        }
        stage('OWASP Dependency-Check') {
            steps {
                    dependencyCheck additionalArguments: ''' 
                    -o './'
                    -s './'
                    -f 'ALL' 
                    --prettyPrint''', odcInstallation: 'owasp'

                    dependencyCheckPublisher pattern: 'dependency-check-report.xml'
                    }
        }
}
}
Enter fullscreen mode Exit fullscreen mode

Step 9 : Create IAM User on aws cloud and provide permission of awsECSFullaccess
and create access key

Step 10 : Configure AWS Credential

Goto Jenkins Dashboard → Manage Jenkins → Credentials

select aws Credential and then and fill out access key and access id

also install aws cli in server by cmd

sudo apt update
sudo snap install aws-cli --classic
Enter fullscreen mode Exit fullscreen mode

Step 11 : Configure ECR On AWS cloud

AWS ECR

Step 12 : Configure ECS On AWS Cloud

AWS ECS

Step 13 : now set Pipeline or update it

pipeline {
    agent any
    tools {
        jdk 'jdk17'
        nodejs 'node16'
    }
    environment {
        scannerhome = tool 'sonarserver'
        imagename = '058264453864.dkr.ecr.us-east-1.amazonaws.com/nodejsapp'
        awscred = 'ecr:us-east-1:awscred'
        registeryurl='https://058264453864.dkr.ecr.us-east-1.amazonaws.com/nodejsapp'
        cluster='nojejsproject'
        service='nodejsprojectservice'
    }
    stages{
        stage('clean workspace'){
            steps{
                cleanWs()
            }
        }
        stage("Fetch Code"){
            steps{
                git url: 'https://github.com/bhaktraj/zomatocicd.git', branch:'main'
            }
        }
        stage("Sonarqube analyse "){
            steps{
                withSonarQubeEnv('sonarserver') {
                   sh '''$scannerhome/bin/sonar-scanner -Dsonar.projectName=zomato \
                    -Dsonar.projectKey=zomato '''
              }
            }
        }
        stage("Quality Gate") {
            steps {
                timeout(time: 1, unit: 'MINUTES') {
                    // Parameter indicates whether to set pipeline to UNSTABLE if Quality Gate fails
                    // true = set pipeline to UNSTABLE, false = don't
                    waitForQualityGate abortPipeline: true
                }
            }
        }
        stage("install Dependences"){
            steps{
                sh 'npm install'
            }
        }
        stage('OWASP Dependency-Check') {
            steps {
                    dependencyCheck additionalArguments: ''' 
                    -o './'
                    -s './'
                    -f 'ALL' 
                    --prettyPrint''', odcInstallation: 'owasp'

                    dependencyCheckPublisher pattern: 'dependency-check-report.xml'
                    }
        }
        stage('Build docker images'){
            steps{
                script{
                    dockerimage = docker.build(imagename + ":$BUILD_NUMBER", ".")

                }

            }
        }
        stage('Upload to ECR'){
            steps{
            script{
                docker.withRegistry(registeryurl, awscred){
                    dockerimage.push("$BUILD_NUMBER")
                    dockerimage.push("latest")
                }
            }
        }
        }
        stage('deploy to ecs'){
        steps{
            withAWS(credentials:'awscred', region:'us-east-1'){
                sh 'aws ecs update-service --cluster ${cluster} --service ${service} --force-new-deployment'
            }
        }
    }
    }
}
Enter fullscreen mode Exit fullscreen mode

OWASP report
OWASP report

Stage View
Stage View

Server

SonarQube Server

. . . . . . .