Automating the Deployment of a Full-Stack Application with Jenkins and Docker

Introduction

In today's fast-paced development environment, automating deployment pipelines is crucial for efficiency and reliability. In this blog, we will walk through setting up a Jenkins CI/CD pipeline for deploying a full-stack application — a React.js frontend and a Node.js backend — while ensuring that each component runs on different ports. We will also incorporate Docker for containerization, making it easier to deploy and manage the application.

The ultimate goal is to create an automated pipeline that can:

  • Build the backend and frontend on code changes.

  • Deploy them in isolated environments with different ports.

  • Enable easy testing and deployment for continuous delivery.


Prerequisites

Before we dive into the pipeline setup, ensure that you have the following tools installed:

  1. Jenkins – A tool to automate tasks like building, testing, and deploying applications.

  2. Docker – For containerizing applications for easy deployment.

  3. Git – For version control.

  4. Node.js & npm – For managing the backend and frontend dependencies.


Step-by-Step Guide

1. Setting Up Jenkins Pipeline

To automate the deployment process, we create a Jenkins pipeline that will handle:

  • Checkout of the code from Git.

  • Install dependencies for both frontend and backend.

  • Run both the frontend and backend servers simultaneously on different ports.

2. Dockerizing the Application

We use Docker to ensure that both the frontend and backend applications are easily deployable across environments. By using Docker containers, we can isolate the environments for each application and ensure consistency across development, testing, and production.


Jenkins Pipeline Configuration

In Jenkins, we create a Pipeline that follows these stages:

  1. Checkout Code: Clone the Git repository.

  2. Install Backend: Install dependencies and test the backend.

  3. Install Frontend: Install dependencies and build the frontend.

  4. Run Application: Start both the frontend and backend, each on its respective port.

Here’s the complete pipeline script:

pipeline {
    agent any
    environment {
        NODEJS_HOME = tool name: 'NodeJS 16', type: 'NodeJS' // Specify NodeJS version
        PATH = "${NODEJS_HOME}/bin:${env.PATH}"
    }
    stages {
        stage('Checkout Code') {
            steps {
                git branch: "${env.BRANCH_NAME ?: 'main'}", url: 'https://github.com/your-repo.git' // Replace with your Git repo URL
            }
        }
        stage('Install and Build Backend') {
            steps {
                dir('backend') {
                    sh 'npm install'
                    sh 'npm test' // Run backend tests if any
                }
            }
        }
        stage('Install and Build Frontend') {
            steps {
                dir('frontend/expense-tracker') {
                    sh 'npm install'
                    sh 'npm run build'
                }
            }
        }
        stage('Run Application') {
            steps {
                parallel (
                    backend: {
                        dir('backend') {
                            sh 'PORT=3000 npm start &' // Backend runs on port 3000
                        }
                    },
                    frontend: {
                        dir('frontend/expense-tracker') {
                            sh 'PORT=3001 npm start &' // Frontend runs on port 3001
                        }
                    }
                )
            }
        }
    }
    post {
        always {
            echo 'Pipeline execution completed!'
        }
        failure {
            echo 'Pipeline failed!'
        }
    }
}

Dockerizing the Application

To make deployment even easier, we will Dockerize both the frontend and backend. Below is a brief overview of the Dockerization process.

Backend Dockerfile (Node.js)

# Backend Dockerfile
FROM node:16

WORKDIR /app

COPY backend/package.json /app/
RUN npm install

COPY backend /app/

EXPOSE 3000
CMD ["npm", "start"]

Frontend Dockerfile (React.js)

# Frontend Dockerfile
FROM node:16

WORKDIR /app

COPY frontend/package.json /app/
RUN npm install

COPY frontend /app/

RUN npm run build

EXPOSE 3001
CMD ["npx", "serve", "-s", "build", "-l", "3001"]

Docker Compose (optional)

To run both containers together, we can use Docker Compose. Here’s a basic docker-compose.yml file:

version: '3'
services:
  backend:
    build:
      context: .
      dockerfile: backend/Dockerfile
    ports:
      - "3000:3000"
  frontend:
    build:
      context: .
      dockerfile: frontend/Dockerfile
    ports:
      - "3001:3001"

CI/CD Flow: From Code Commit to Production

  1. Code Push: Developers push changes to the Git repository (e.g., on GitHub).

  2. Jenkins Trigger: Jenkins automatically triggers the pipeline on code changes.

  3. Build Process:

    • Jenkins checks out the code.

    • Installs the required dependencies for both the frontend and backend.

    • Builds and runs the applications on their respective ports (3000 for backend, 3001 for frontend).

  4. Docker Deployment: Once the apps are built, Docker containers are created and can be deployed on any server.

  5. Testing: Once deployed, tests are run (if included in the pipeline) to verify the correctness of the application.

  6. Production Deployment: Once everything passes, the app is deployed to the production server.


Architecture Diagram

Here is a simple architecture diagram for the CI/CD pipeline:

+-------------------+        +------------------+       +-------------------+
|   Git Repository  | -----> | Jenkins Pipeline | ----> |  Docker Containers |
|  (GitHub/GitLab)  |        |  (Build & Test)   |       | (Backend & Frontend)|
+-------------------+        +------------------+       +-------------------+
                             |       |                 |
                             |       |                 |
                        +----v---+  +----v---+   +------v-------+
                        | Backend|  |Frontend|   |  Docker Swarm  |
                        | (Node) |  | (React)|   | Or Kubernetes   |
                        +--------+  +--------+   +----------------+

Conclusion

In this blog, we've covered how to set up a Jenkins CI/CD pipeline to automate the deployment of a full-stack application. We’ve also demonstrated how to Dockerize the application for better scalability and deployment flexibility.

With this setup, you can easily manage your full-stack applications, test changes automatically, and deploy them seamlessly to your production environment.

By integrating Jenkins with Docker, you ensure that your applications run consistently across different environments, making the DevOps process smoother and more efficient.