Full-stack Microservice App (React Frontend + Node.js Backend)
With Docker, Docker Compose, Docker Swarm, Jenkins CI/CD, Docker Networking, Parallelism
π οΈ Project Idea (Simple & Clean)
Frontend: React App (shows "Hello from Backend")
Backend: Node.js API (returns "Hello from Backend API")
Goal: Use all DevOps concepts to build, deploy, and orchestrate the app
STEP 1οΈβ£ β Setup project structure
my-devops-project/
βββ backend/
β βββ Dockerfile
β βββ server.js
β βββ package.json
βββ frontend/
β βββ Dockerfile
β βββ (React app)
βββ docker-compose.yml
βββ Jenkinsfile
- First make a main folder i.e my-devops-project.
- Inside, make two sub folders i.e backend and frontend.
- Now, inside backend folder, make Dockerfile and server.js.
- Inside frontend folder, make Dockerfile.(In further steps we will setup react project by commands)
- Outside frontend and backend folder, make Jenkinsfile (Keep J capital) and docker-compose.yml.
- Now follow below steps.
STEP 2οΈβ£ β Backend: Node.js app
We will run following commands to build a simple backend project setup.
cd backend
npm init -y
npm install express
In server.js, write below code:
const express = require('express');
const app = express();
const PORT = 5000;
app.get('/', (req, res) => {
res.send('Hello from Backend API');
});
app.listen(PORT, () => {
console.log(`Backend running on port ${PORT}`);
});
STEP 3οΈβ£ β Frontend: React app
npx create-react-app .
Now, in App.js, replace code with below code:
import { useEffect, useState } from 'react';
function App() {
const [message, setMessage] = useState('');
useEffect(() => {
fetch('http://backend:5000')
.then(res => res.text())
.then(data => setMessage(data));
}, []);
return <h1>{message}</h1>;
}
export default App;
STEP 4οΈβ£ β Dockerize Backend + Frontend
β
Backend Dockerfile
FROM node:18
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 5000
CMD ["node", "server.js"]
β
Frontend Dockerfile
# Stage 1: Build the React app
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Stage 2: Serve the React app with Nginx
FROM nginx:alpine
# Copy the build folder from the first stage to Nginx's default HTML directory
COPY --from=build /app/build /usr/share/nginx/html
# Expose port 80 for the Nginx server
EXPOSE 80
# Start Nginx
CMD ["nginx", "-g", "daemon off;"]
Note: While making frontend using react,angular or any other framework, we prefer to provide build folder to container because it's clean, optimized, small, fast, and production-ready β no unnecessary dev code.
Why we prefer this?
Without build folder | With build folder (preferred) |
---|---|
Heavy container (with Node.js) | Lightweight container (Nginx/alpine) |
Slower load (dev bundle) | Optimized, fast-loading |
Exposes dev dependencies/tools | Only static files exposed |
More resource usage | Less CPU/RAM |
STEP 5οΈβ£ β Docker Compose (Networking + Storage)
Now, let's write docker-compose.yml file according to docker swarm. Generally, we write build step in compose file, but while embedding docker swarm, we will directly provide image because docker swarm demands image not do building process.
version: '3.8'
services:
backend:
image: aryasingh55/backend:v1
ports:
- "5002:5002"
networks:
- appnet
volumes:
- backend-data:/app
frontend:
image: aryasingh55/frontend:v1
ports:
- "5000:80"
networks:
- appnet
volumes:
- frontend-data:/app
networks:
appnet:
external: true
volumes:
backend-data:
frontend-data:
Note: appnet is custom network name. You can provide any name of your choice.
Note: image name for backend should be : your-dockerhub-username/backend-folder-name:v1
Note: image name for frontend should be : your-dockerhub-username/frontend-folder-name:v1
Note: As reference, I have written my credentials above.
Note: In networks -> appnet -> external , it is true. It means we have created swarm network through CLI command. So, run this command:
docker network create --driver overlay appnet
STEP 6οΈβ£ β Docker Hub operations
Create Docker Hub account: https://hub.docker.com
- Login Docker CLI using below command:
docker login
- Tag + Push backend image
docker build -t your-custom-name/backend:v1 backend/
docker tag your-custom-name/backend:v1 your-dockerhub-username/backend:v1
docker push your-dockerhub-username/backend:v1
- Tag + Push frontend image
docker build -t your-custom-name/frontend:v1 frontend/
docker tag your-custom-name/frontend:v1 your-dockerhub-username/frontend:v1
docker push your-dockerhub-username/frontend:v1
STEP 7οΈβ£ β Docker Swarm Cluster
- Open bash, run following commands.
docker swarm init --advertise-addr <your-wsl-ip-address>
# to check wsl-ip-address, run below command.
ip addr
Deploy services using compose (in swarm mode)
docker stack deploy -c docker-compose.yml devopsapp
Check services
docker service ls
docker stack ps devopsapp
docker service logs <service-name>
STEP 8οΈβ£ β Jenkins (port 8080 UI)
Make sure, you have setup you jenkins before and it is running on port 8080.
Access Jenkins: http://localhost:8080
Now, you will get asked for login with your username and password.
After successful login, you'll see dashboard. Click new item. Name your project,select pipeline as option and click save.
Now do below configurations.
1οΈβ£ Jenkins UI β New Item β Pipeline Job
2οΈβ£ Source Code Mgmt (SCM) β Git repo URL (https://github.com/your-repo-url.git)
3οΈβ£ Pipeline β Definition: Pipeline script from SCM
β SCM: Git
β Repo URL aur branch
Note: No need to do step 3 if you have made repo public on github.
β Change */master -> */main
β Script Path = Jenkinsfile
STEP 9οΈβ£ β Jenkinsfile (CI/CD Pipeline)
π Parallelism + Docker build + Push + Deploy
pipeline {
agent any
environment {
DOCKERHUB_CREDENTIALS = 'dockerhub-creds'
IMAGE_BACKEND = 'aryasingh55/backend:v1'
IMAGE_FRONTEND = 'aryasingh55/frontend:v1'
STACK_NAME = 'devopsapp'
COMPOSE_FILE = 'docker-compose.yml'
}
stages {
stage('Clone Repository') {
steps {
git branch: 'main', url: 'https://github.com/Aryagithubk/devops_prac1.git'
}
}
stage('Build docker images (Parallel)') {
parallel {
stage('Build backend') {
steps {
script {
docker.build("${IMAGE_BACKEND}", 'backend/')
}
}
}
stage('Build frontend') {
steps {
script {
docker.build("${IMAGE_FRONTEND}", 'frontend/')
}
}
}
}
}
stage('Push docker images (Parallel)') {
parallel {
stage('Push Backend') {
steps {
script {
docker.withRegistry('https://index.docker.io/v1/', DOCKERHUB_CREDENTIALS) {
docker.image(IMAGE_BACKEND).push()
}
}
}
}
stage('Push Frontend') {
steps {
script {
docker.withRegistry('https://index.docker.io/v1/', DOCKERHUB_CREDENTIALS) {
docker.image(IMAGE_FRONTEND).push()
}
}
}
}
}
}
stage('Deploy to docker swarm') {
steps {
script {
sh 'docker network inspect appnet || docker network create --driver overlay appnet'
sh "docker stack deploy -c ${COMPOSE_FILE} ${STACK_NAME}"
}
}
}
}
post {
success {
echo 'successful deployment'
}
failure {
echo 'deployment failed'
}
}
}
Note: In IMAGE_BACKEND and IMAGE_FRONTEND, write your own image names which you have written in above steps.
β¨ The value of DOCKERHUB_CREDENTIALS should be the ID of the Jenkins credentials where you save your DockerHub username + access token pair.
Step-by-step (what you have to do):
1οΈβ£ Go to Jenkins β Manage Jenkins β Manage Credentials
2οΈβ£ Choose (or create) a domain (Global is fine) β Add Credentials
3οΈβ£ Credential Type β Username with password
Username β your DockerHub username
Password β your DockerHub access token (from DockerHub > Account Settings > Security > Access Tokens)
4οΈβ£ ID β Set dockerhub-creds (since your pipeline uses this name)
(Or use any ID you want β but update the pipeline env var to match)
5οΈβ£ Save
STEP π β Test your app
Visit http://localhost:3000 β Frontend React app
Backend API works β through Docker network
Flow of this project:
GitHub push β Jenkins Trigger
1οΈβ£ Clone repo
2οΈβ£ [Parallel] Backend & Frontend docker images build
3οΈβ£ [Parallel] Backend & Frontend docker images DockerHub pe push
4οΈβ£ appnet network check/create β Docker Swarm deploy (stack)
Result β Inside Swarm cluster,services running + Networking setup automatic
Note: We have not applied webhooks yet, to apply automation using jenkins.
Checkout next article for this.
Bye,Bye.
Top comments (0)