MERN Stack with Kubernetes

In this article, I will cover how you can setup MERN stack application on Kubernetes.

Prerequisites

It is assumed that you have an understanding of the following tools/concepts and have them installed on your system:

  • Docker
  • Minikube
  • NodeJS

Introduction

The purpose of this article is to familiarize you with the deployment process of MERN stack on Kubernetes. We have minimal requirements on our side:

  • Deployment of the server (NodeJS)
  • Deployment of the client (ReactJS)
  • Initiation of services (MongoDB)

In addition to that, we will get hands-on experience to:

  • Inspect status of the deployments, pods, services.
  • Start/Stop the deployments, pods, services whenever we need to.
  • Inspect the logs and descriptions of individual deployments, pods, and services.

Lets Dive In Coding

We start by creating our base-directory mern-stack.

Within that directory, we will create the following files and directories:-

  • Client/Dockerfile
  • Server/Dockerfile
  • server-deployment.yaml
  • client-deployment.yaml

For simplicity, I created 2 folders for separation of concern, Client folder includes the React app as front-end, while Server folder includes the NodeJS app as back-end. As we need two different docker images for the Kubernetes deployment, Client and Server folders, each would have their own Dockerfile. Having docker images to be ready, is a precursor to the deployment of Kubernetes. In addition to that, server-deployment.yaml is used to build deployment for NodeJS app (back-end) on Kubernetes, while client-deployment.yaml is used for ReactJS app (front-end) on Kubernetes.
Now let’s start coding, paste the following lines in Client/Dockerfile to dockerize the front-end of the app.

WORKDIR /usr/src/app/client
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]

We have our file ready to build the docker image, now move on to paste the following lines in Server/Dockerfile to dockerize the back-end.

WORKDIR /usr/src/app/server
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3010
CMD ["npm", "start"]

We have both front-end and back-end docker files ready. Now let’s move on to the Kubernetes setup files.
Now paste the following lines in server-deployment.yaml placed in the root directory.

kind: Deployment
metadata:
  name: mongo
spec:
  selector:
    matchLabels:
      app: mern-stack-app-server
  replicas: 1
  template:
    metadata:
      labels:
        app: mern-stack-app-server
    spec:
      containers:
        - name: mongo
          image: mongo:latest
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 27017
          volumeMounts:
            - name: data
              mountPath: /data
              readOnly: false
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: mongo-data

---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mongo-data
  labels:
    app: mern-stack-app-server
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
      
---

apiVersion: v1
kind: Service
metadata:
  name: mongo
  labels:
    app: mern-stack-app-server
spec:
  ports:
    - name: mongo
      port: 27017
      targetPort: 27017
  type: NodePort
  selector:
    app: mern-stack-app-server
    
---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mern-stack-app-server
spec:
  selector:
    matchLabels:
      app: mern-stack-app-server
  replicas: 1
  template:
    metadata:
      labels:
        app: mern-stack-app-server
    spec:
      containers:
        - name: mern-stack-app-server
          image: mern-stack-app-server:1.0.0
          imagePullPolicy: Never
          ports:
            - containerPort: 3010
          volumeMounts:
            - name: data
              mountPath: /data
              readOnly: false
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: mongo-data

Above code, will create the following:-

  • 1 deployment of Server app
  • 1 deployment of MongoDB
  • Persistent volume claim for MongoDB and attachment to server app for data persistence
  • MongoDB service for MongoDB database communication with server app
    Now, paste the following lines of code in client-deployment.yaml in the root directory.
kind: Deployment
metadata:
  name: mern-stack-app-client
spec:
  selector:
    matchLabels:
      app: mern-stack-app-client
  replicas: 1
  template:
    metadata:
      labels:
        app: mern-stack-app-client
    spec:
      containers:
        - name: mern-stack-app-client
          image: mern-stack-app-client:1.0.0
          imagePullPolicy: Never
          ports:
            - containerPort: 3000

Above mentioned code, will create the client-side deployment. This is all the coding files we need for Kubernetes deployment. Rest is just the commands for appropriate exposure of components to each other.
In the app, replace the mongo host in mongodb connection string, by mongo, like this:

const connection = "mongodb://mongo:27017/kubernetes-app"

Guide to Build for deployment on Kubernetes

Make sure you are in the root directory of your project. Run the following commands sequentially, in order to deploy on Kubernetes:

this will start the minikube cluster single node.

minikube start

this will make docker inside minikube accessible via cmd.

eval $(minikube docker-env)

build docker server-app image.

docker build -t mern-stack-app-server:1.0.0 ./server -f ./server/Dockerfile --no-cache

build docker client-app image.

docker build -t mern-stack-app-client:1.0.0 ./client -f ./client/Dockerfile --no-cache

deploy server-app on kubernetes using kubectl.

kubectl apply -f server-deployment.yaml

deploy client-app on kubernetes using kubectl.

kubectl apply -f client-deployment.yaml

get the server-app and client-app pod name, and expose it to localhost access.

kubectl get pods
kubectl port-forward <pod name here including 'mern-stack-app-server'> 32111:3010
kubectl port-forward <pod name here including 'mern-stack-app-client'> 32000:3000

This would make the server-app accessible via http://localhost:32111 and the client-app at http://localhost:32000.

Managing MERN Stack On Kubernetes

Now, as your app is up and running on Kubernetes cluster, you must know what and how to perform operations on the running app.

Inspecting Kubernetes Components

kubectl describe pods <pod_name>
kubectl describe deployments <deployment_name>
kubectl describe services <service_name>
kubectl describe pvc <pvc_name>
kubectl describe pv <pv_name>

Inspecting Logs

kubectl logs <pod_name>
kubectl logs <deployment_name>
kubectl logs <service_name>

In order to keep listening to logs, add -f after kubectl logs in the above commands.

Interact with the container inside Pod

kubectl exec -it <pod_name> -- /bin/bash

Stopping/Deleting Kubernetes Components

kubectl delete -f server-deployment.yaml
kubectl delete -f client-deployment.yaml

In conclusion, MERN Stack app can be easily deployed and configured on Kubernetes cluster, if the right procedure and technique is followed, as mentioned in this article.