A fullstack MERN project which has Reactjs as frontend, Nodejs as backend, and MongoDB as a Database.
Clone this repository from the GitHub URL.
git clone https://github.com/MdShimulMahmud/goal-projects.git
cd goal-projects
code .
1.1 Open Backend
cd backend
1.2 Install Dependencies
npm install
1.3 Run Backend
npm start
1.4 Check Application running on: http://localhost:5000/goals
2.1 Open Frontend
cd frontend
2.2 Install Dependencies
npm install
2.3 Run Frontend
npm start
2.4 Check Application running on: http://localhost:3000
FROM node:16-alpine
WORKDIR /app
COPY package.json .
COPY . .
RUN npm install
EXPOSE 5000
CMD ["npm", "start"]
docker build -t <image name>:<image tag> <dockerfile path>
Best Practice:
docker build -t your-dockerhub-username/backend:tag -f backend/Dockerfile .
For example:
docker build -t shimulmahmud/backend:v1.0.4 .
docker run -d -p <host-port>:<container-port> <image name>:<image tag>
For example:
docker run -d -p 5000:5000 shimulmahmud/backend:v1.0.4
docker login
docker push your-dockerhub-username/backend:tag
For example:
docker push shimulmahmud/backend:v1.0.4
FROM node:16-alpine AS build-stage
WORKDIR /app
COPY package.json .
RUN npm install
COPY . .
RUN npm run build
FROM nginx:latest
COPY --from=build-stage /app/build /usr/share/nginx/html
COPY ./conf/nginx.conf /etc/nginx/conf.d/default.conf
CMD ["nginx", "-g", "daemon off;"]
Here we used nginx as a web server which provides static web content and listens on port 80 by default.
docker build -t <image name>:<image tag> <dockerfile path>
Best Practice:
docker build -t your-dockerhub-username/frontend:tag -f frontend/Dockerfile .
For example:
docker build -t shimulmahmud/frontend:v1.0.4 .
docker run -d -p <host-port>:<container-port> <image name>:<image tag>
For example:
docker run -d -p 3000:80 shimulmahmud/frontend:v1.0.4
docker login
docker push your-dockerhub-username/frontend:tag
For example:
docker push shimulmahmud/frontend:v1.0.4
To set up Nginx as a reverse proxy for your MERN stack application, you can configure Nginx to route traffic to both your frontend (React) and backend (Node.js/Express) applications.
Here’s how you can configure Nginx to act as a reverse proxy in a Dockerized environment:
You’ll need to create a custom Nginx configuration file (default.conf
) to set up the reverse proxy.
default.conf
upstream frontend {
server frontend:3000;
}
upstream backend {
server backend:5000;
}
server {
listen 80;
location / {
proxy_pass http://frontend;
}
location /api {
rewrite /api/(.*) /$1 break;
proxy_pass http://backend;
}
}
In this configuration:
- Frontend (React) is served from http://frontend:3000.
- Backend (Node.js/Express API) is served from http://backend:5000.
- The location / route handles requests for the frontend.
- The location /api route handles API requests that should be forwarded to the backend.
To use Nginx as a reverse proxy in a Dockerized environment, you'll need to create a Dockerfile for Nginx and replace its default configuration.
FROM nginx:alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY default.conf /etc/nginx/conf.d/default.conf
To understand how a reverse proxy works, you can Visit Here
To set up a Docker Compose configuration for your MERN stack application (MongoDB, Express, React, Node.js) along with Nginx as a reverse proxy, here’s a structured example.
/goal-projects
├── backend/
│ ├── Dockerfile
│ └── ...
├── frontend/
│ ├── Dockerfile
│ └── ...
├── nginx/
│ ├── Dockerfile
│ └── nginx.conf
├── docker-compose.yml
└── ...
version: "3.8"
services:
backend:
build:
context: ./backend
dockerfile: Dockerfile
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
depends_on:
- backend
nginx:
build:
context: ./nginx
dockerfile: Dockerfile
ports:
- "80:80"
depends_on:
- backend
- frontend
Run docker-compose : docker-compose up --build
- Docker Compose manages multiple containers for the MERN stack, including frontend, backend, and nginx.
- The Nginx container acts as a reverse proxy to route traffic to the frontend (/) and backend (/api).
- Dockerfiles are defined for the frontend, backend, and Nginx.
- Deployment: Describes the pods that should run for your frontend, backend, and MongoDB services.
- Service: Exposes each component (frontend, backend, MongoDB) to the network within the Kubernetes cluster.
- Ingress: Manages external access to your services, routing traffic to the frontend and backend.
- Secrets: Stores sensitive information such as MongoDB credentials.
- ConfigMap: Stores configuration data that can be accessed by the application pods.
- PersistentVolumeClaim (PVC): Allows MongoDB to store data persistently.
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend-deployment
labels:
app: frontend
type: frontend
spec:
replicas: 1
selector:
matchLabels:
app: frontend
type: frontend
template:
metadata:
labels:
app: frontend
type: frontend
spec:
containers:
- name: frontend-container
image: shimulmahmud/frontend:v1.0.4
ports:
- containerPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-deployment
labels:
app: backend
type: backend
spec:
replicas: 1
selector:
matchLabels:
app: backend
type: backend
template:
metadata:
labels:
app: backend
type: backend
spec:
containers:
- name: backend-conatiner
image: shimulmahmud/backend:v1.0.4
ports:
- containerPort: 5000
volumeMounts:
- mountPath: /app/logs/
name: access-log-volume
# subPath: access.log
env:
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: database-secret
key: password
- name: DATABASE_USERNAME
valueFrom:
secretKeyRef:
name: database-secret
key: username
- name: DATABASE_URL
valueFrom:
configMapKeyRef:
name: configmap
key: database
volumes:
- name: access-log-volume
persistentVolumeClaim:
claimName: test-claim
apiVersion: v1
kind: Service
metadata:
name: client-service
spec:
selector:
app: frontend
type: frontend
ports:
- protocol: TCP
port: 3000
targetPort: 3000
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
selector:
app: backend
type: backend
ports:
- protocol: TCP
port: 5000
targetPort: 5000
type: ClusterIP
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minimal-ingress
annotations:
kubernetes.io/ingress.class: 'nginx'
nginx.ingress.kubernetes.io/use-regex: 'true'
# nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: client-service
port:
number: 3000
- path: /goals
pathType: Prefix
backend:
service:
name: backend-service
port:
number: 5000
apiVersion: v1
kind: Secret
metadata:
name: database-secret
type: Opaque
data:
password: <base64 your mongodb password>
username: <base64 your mongodb username>
apiVersion: v1
kind: ConfigMap
metadata:
name: configmap
data:
database: "cluster1.uktxj.mongodb.net/?retryWrites=true&w=majority&appName=Cluster1"
To set up nfs-server for persistent volume you can read this .
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-claim
spec:
storageClassName: nfs-client
accessModes:
- ReadWriteMany
resources:
requests:
storage: 100Mi
To apply and create corresponding services, run the command in the terminal cd k8s
and
kubectl apply -f .
- Push: Triggers on push events to the
master
branch, excluding changes tok8s/deployment.yaml
. - Pull Request: Triggers on pull requests to the
master
branch, excluding changes tok8s/deployment.yaml
.
- Runs on:
ubuntu-latest
- Steps:
- Checkout code
- Run Approval Action
- Store the approval output as an environment variable
- Get the output
- Runs on:
ubuntu-latest
- Needs:
test
job - Condition: Runs if the
test
job's approval output istrue
- Steps:
- Checkout code
- Set up Node.js
- Install dependencies
- Debug Run Number
- Check current version
- Runs on:
ubuntu-latest
- Needs:
backend
job - Steps:
- Checkout code
- Set up Node.js
- Install dependencies
- Debug Run Number
- Check current version
- Runs on:
ubuntu-latest
- Needs:
frontend
andbackend
jobs - Steps:
- Checkout code
- Extract current version
- Read previous version from
deployment.yaml
- Check if current version differs from previous
- Runs on:
ubuntu-latest
- Needs:
frontend
,backend
, andversion_check
jobs - Condition: Runs if the
version_check
job's version_diff output istrue
- Steps:
- Checkout code
- Login to Docker Hub
- Set up Docker Buildx
- Build and push frontend image
- Build and push backend image
- Runs on:
ubuntu-latest
- Needs:
docker
andversion_check
jobs - Condition: Runs if the
version_check
job's version_diff output istrue
- Steps:
- Checkout code
- Set the new image version
- Update Kubernetes
deployment.yaml
image tags - Commit and push changes
This Jenkins pipeline is designed to automate the build and deployment process for the Goals project. The pipeline consists of three main stages: Backend, Frontend, and Docker. It also includes environment variables for GitHub and Docker credentials.
Your jenkins server running on http://localhost:8080 or your_server_ip_address:8080
GH_TOKEN
: GitHub token for accessing private repositories.DOCKER_USERNAME
: Docker Hub username for logging in and pushing images.DOCKER_PASSWORD
: Docker Hub access token for authentication.
- Agent: Any available agent.
- Steps:
- Change directory to
backend
. - Install npm dependencies.
- Print the current build number.
- Read and print the current version from the
VERSION
file.
- Change directory to
- Agent: Any available agent.
- Steps:
- Change directory to
frontend
. - Install npm dependencies.
- Build the frontend application.
- Print the current build number.
- Read and print the current version from the
VERSION
file.
- Change directory to
- Agent: Any available agent.
- Steps:
- Read the current version from the
VERSION
file. - Log in to Docker Hub using the provided credentials.
- Build and push Docker images for both frontend and backend using the current version as the tag.
- Handle any exceptions that occur during the Docker build or push process.
- Read the current version from the
This project contains a Jenkins pipeline stage to trigger the update-k8s-manifests
job by passing the current version as an IMAGE_TAG
parameter.
The Trigger Update Manifest Pipeline
stage performs the following tasks:
- Reads the current version from a
VERSION
file. - Triggers the
update-k8s-manifests
pipeline with the current version as a parameter.
stage('Trigger Update Manifest Pipeline') {
agent any
steps {
script {
def current_version = sh(script: "cat VERSION", returnStdout: true).trim()
build job: 'update-k8s-manifests', parameters: [
string(name: 'IMAGE_TAG', value: "${current_version}")
]
}
}
}
In Infra Repository, you will find all the manifests used in this project. Repo url
- Always: Print a message indicating that the pipeline has completed.
- Failure: Print a message indicating that the pipeline has failed and suggest checking the logs for errors.
- Success: Print a message indicating that the pipeline has succeeded.
To use this Jenkinsfile, place it in the root directory of your project and configure your Jenkins job to use it. Ensure that the necessary credentials (github-token
, docker-username
, and docker-password
) are configured in Jenkins.
Hit your jenkins url to checkout either it's working or not!
Happy Coding!!!