Deploying a MERN Stack App with Kubernetes 🚀
Hello developers 👋, My name is Md taqui imam, i am a Full Stack web developer, today i will tell you "how you can deploy a MERN stack app in Kubernetes".
In today's world, most applications are built using microservices architectures and container technologies like Docker. Kubernetes has emerged as the defacto platform for orchestrating containers at scale across on-premise and cloud infrastructures.
In this detailed post, we will deploy a full-fledged MERN (MongoDB, Express, React, Node.js) stack application on Kubernetes. The MERN stack is one of the most popular techniques for building modern web apps, combining React for the frontend, Express for the API backend and MongoDB for data storage.
Kubernetes provides a powerful platform to run stateful and stateless containers, handle service discovery, load balancing, auto-scaling, upgrades and more - all with declarative configurations and minimal maintenance. Let's delve into setting up a development environment, deploying the services and exploring key Kubernetes concepts.
## Setting Up the Development Environment
The first step is to install the necessary tools on your development machine:
- Docker - Used to build container images for each component of our application. Docker simplifies packaging code and dependencies into portable images.
- Kubectl - The Kubernetes command line client that allows us to interact with the Kubernetes API server and manage the cluster. We will use it to deploy and manage application resources.
- Minikube - A tool that runs a single-node Kubernetes cluster locally for testing and development purposes. It provisions a Virtual Machine, installs Kubernetes and configures the control plane. This gives us an isolated test environment without the overhead of a full production cluster.
To install these, follow the documentation for your operating system. Once installed, we can initialize Minikube:
```
minikube start
```
This will provision a VM, download a Kubernetes version, configure RBAC and pods network etc. To verify it's running correctly:
```
kubectl cluster-info
```
The cluster API endpoint, Kubernetes master location and number of nodes should be displayed.
Now our development environment is set up to deploy containers and experiment with Kubernetes features on a local single-node cluster.
## Containerizing the Application Components
The next step is building Docker images for each component of our MERN application - MongoDB, Express and React. We'll store these images in a Docker registry like Docker Hub for later deployment.
For MongoDB, create a Dockerfile that clones the latest code, sets up configuration and exposes the default port:
```dockerfile
FROM mongo:latest
COPY mongod.conf /etc/mongod.conf
EXPOSE 27017
```
Build and tag the image:
```
docker build -t myuser/mongodb .
docker push myuser/mongodb
```
Similarly, create Dockerfiles and images for the Express API and React front-end services. The API Dockerfile would COPY the code and install dependencies before exposing the app port.
Now we have containerized the application components independently, along with any dependencies or configuration baked in. These images form the base units that Kubernetes will schedule and run as "pods".
## Configuring Secrets and ConfigMaps
When deploying to Kubernetes, we need a secure and flexible way to inject configuration and credentials into containers at runtime. For this, Kubernetes supports Secrets and ConfigMaps.
Secrets hold sensitive data like passwords, keys or tokens in a base64 encoded format. ConfigMaps store non-sensitive configuration data like environment variables or properties files.
For our app, we will define:
- A Secret for the MongoDB username and password
- A ConfigMap for database URL, Node environment variables
The secret.yaml:
```yaml
apiVersion: v1
kind: Secret
metadata:
name: mongo-secret
stringData:
password: supersecurepass
```
The configmap.yaml:
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
MONGO_URI: mongodb://usr:pass@mongodb:27017/app
NODE_ENV: production
```
We can now apply these resources to Kubernetes:
```
kubectl apply -f secret.yaml
kubectl apply -f configmap.yaml
```
This securely stores sensitive values in etcd while exposing non-sensitive data to containers.
## Deploying MongoDB
With container images and configurations ready, it's time to deploy MongoDB as a Kubernetes resource. We'll create a Deployment to launch replicated pods behind a Service for discovery.
The mongodb-deployment.yaml defines:
- mongodb image from prepared Dockerfile
- Pod spec with resources, ports and volumes
- Pulls password from mongo-secret
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mongodb
spec:
replicas: 1
selector:
matchLabels:
app: mongodb
template:
metadata:
labels:
app: mongodb
spec:
containers:
- name: mongodb
image: myuser/mongodb
envFrom:
- secretRef:
name: mongo-secret
ports:
- containerPort: 27017
```
Next, a mongodb-service.yaml exposes port 27017 within the cluster:
```yaml
apiVersion: v1
kind: Service
metadata:
name: mongodb
spec:
type: ClusterIP
ports:
- port: 27017
selector:
app: mongodb
```
Now apply both configs:
```
kubectl apply -f mongodb-deployment.yaml
kubectl apply -f mongodb-service.yaml
```
Kubernetes will create MongoDB pods and make them discoverable via DNS.
## Deploying the Express API
Now for the Express backend API. Create an express-deployment.yaml:
- Node.js image from prepared Dockerfile
- Environment from app-config ConfigMap
- Port 8080 exposed
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: express
spec:
replicas: 1
selector:
matchLabels:
app: express
template:
metadata:
labels:
app: express
spec:
containers:
- name: express
image: myuser/express
envFrom:
- configMapRef:
name: app-config
ports:
- containerPort: 8080
```
An express-service.yaml exposes port 8080 externally:
```yaml
apiVersion: v1
kind: Service
metadata:
name: express
spec:
type: NodePort
ports:
- port: 8080
nodePort: 30001
selector:
app: express
```
Deploy both and the Express API is now available on port 30001!
## Deploying the React Frontend
For the frontend, create a react-deployment.yaml:
- React image from Dockerfile
- Mount volumes for building
- Port 80 exposed
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: react
spec:
selector:
matchLabels:
app: react
template:
metadata:
labels:
app: react
spec:
containers:
- name: react
image: myuser/react
ports:
- containerPort: 80
```
A react-service.yaml exposes port 80 internally:
```yaml
apiVersion: v1
kind: Service
metadata:
name: react
spec:
type: ClusterIP
ports:
- port: 80
selector:
app: react
```
Deploy and the React SPA is now being served from Kubernetes!
## Accessing the Application
Now that all services are deployed, we can test the full MERN stack running on Kubernetes.
By default, the React service is only reachable within the cluster. We can port-forward it locally:
```
kubectl port-forward svc/react 8080:80
```
Now visit http://localhost:8080 to view the app.
The frontend makes API calls to the Express service endpoint, which connects to MongoDB. Even though components are split, Kubernetes handles discovery and routing requests between services.
This is just the beginning - Kubernetes unlocks many more advanced capabilities around auto-scaling, easy updates, reliability guarantees and more. We've learned the basics of deploying modern applications on Kubernetes using declarative configs.
In the future, areas to explore include service meshes, Helm charts for packaging configurations, ingress controllers, monitoring solutions and more to improve the experience of running containers at scale. Kubernetes is a powerful platform for developing and deploying distributed software.