Deploying a Node.js Application with Kustomize on Minikube
Introduction
In this article, we will explore how to deploy a Node.js application using Kustomize on a Minikube cluster. We will cover the project structure, Dockerfile setup, Kubernetes manifests, and how to use Kustomize to manage different configurations for development, staging, and production environments. We will also discuss the advantages of using Kustomize over other alternatives.
Prerequisites
Before we begin, ensure you have the following installed on your machine:
Project Structure
Here is the structure of our project:
profile-app/├── config/│ └── config.json├── k8s/│ ├── base/│ │ ├── deployment.yaml│ │ ├── service.yaml│ │ ├── pvc.yaml│ │ ├── namespace.yaml│ │ └── kustomization.yaml│ └── overlays/│ ├── dev/│ │ ├── config.json│ │ └── kustomization.yaml│ ├── prod/│ │ ├── config.json│ │ └── kustomization.yaml│ └── staging/│ ├── config.json│ └── kustomization.yaml├── app.js├── package.json├── Dockerfile└── docs/└── readme.md
Application Code
app.js
1const express = require("express");2const fs = require("fs");3const app = express();4const PORT = process.env.PORT || 3001;5// Read the mounted config file6const CONFIG_PATH = "./config/config.json";7let config = { name: "Noel Bansikah", role: "DevOps Engineer", nameColor: "black", roleColor: "gray", environment: "development" };8// Check if the config file exists9if (fs.existsSync(CONFIG_PATH)) {10 config = JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));11}12// Define a color map for meaningful colors13const colorMap = {14 black: "#000000",15 gray: "#6c757d",16 blue: "#007BFF",17 yellow: "#FFC107",18 red: "#DC3545",19 green: "#28A745"20};21// Get the colors from the config or default to black and gray22const nameColor = colorMap[config.nameColor] || colorMap.black;23const roleColor = colorMap[config.roleColor] || colorMap.gray;24app.get("/", (req, res) => {25 res.send(`26 <html>27 <body style="background-color: white; text-align: center; padding: 50px;">28 <h1 style="color: ${nameColor};">Hello, I am ${config.name} 🚀</h1>29 <h2 style="color: ${roleColor};">Role: ${config.role}</h2>30 <h3>Environment: ${config.environment}</h3>31 <p>GitHub: <a href="https://github.com/bansikah22">bansikah22</a></p>32 <p>GitLab: <a href="https://gitlab.com/tandapnoelbansikah">bansikah22</a></p>33 <p>LinkedIn: <a href="https://linkedin.com/in/tandapnoelbansikah">bansikah22</a></p>34 </body>35 </html>36 `);37});38app.listen(PORT, () => console.log(`Server running on port ${PORT}...`));
Dockerfile
1# Stage 1: Build stage2FROM node:18-alpine AS build3WORKDIR /app4COPY package.json package-lock.json ./5RUN npm ci --production6COPY app.js ./7# Stage 2: Production stage8FROM node:18-slim9WORKDIR /app10RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*11COPY --from=build /app/node_modules /app/node_modules12COPY --from=build /app/app.js /app/app.js13CMD ["node", "app.js"]14EXPOSE 3001
Building and Pushing the Docker Image
To build and push the Docker image to Docker Hub, follow these steps:
Build the Docker image:
docker build -t <your-dockerhub-username>/profile-app:latest .
2. Push the Docker image to Docker Hub:
docker push <your-dockerhub-username>/profile-app:latest
Replace <your-dockerhub-username> with your actual Docker Hub username.
Kubernetes Manifests
Base Manifests
The base directory contains the common configuration that is shared across all environments. This includes the deployment, service, PVC, and namespace definitions.
k8s/base/deployment.yaml
1apiVersion: apps/v12kind: Deployment3metadata:4 name: profile-app5spec:6 replicas: 17 selector:8 matchLabels:9 app: profile-app10 template:11 metadata:12 labels:13 app: profile-app14 spec:15 containers:16 - name: profile-app17 image: <your-dockerhub-username>/profile-app:latest18 imagePullPolicy: Always19 ports:20 - containerPort: 300121 volumeMounts:22 - name: config-volume23 mountPath: /app/config24 subPath: config.json25 readOnly: false26 volumes:27 - name: config-volume28 configMap:29 name: profile-app-config
k8s/base/service.yaml
1apiVersion: v12kind: Service3metadata:4 name: profile-service5spec:6 selector:7 app: profile-app8 ports:9 - protocol: TCP10 port: 8011 targetPort: 300112 type: ClusterIP
k8s/base/pvc.yaml
1apiVersion: v12kind: PersistentVolumeClaim3metadata:4 name: profile-app-pvc5spec:6 accessModes:7 - ReadWriteOnce8 resources:9 requests:10 storage: 1Gi
k8s/base/namespace.yaml
1apiVersion: v12kind: Namespace3metadata:4 name: profile-app
k8s/base/kustomization.yaml
The kustomization.yaml file in the base directory defines the resources and images used in the base configuration.
1resources:2 - deployment.yaml3 - service.yaml4 - pvc.yaml5 - namespace.yaml67images:8 - name: profile-app9 newName: <your-dockerhub-username>/profile-app10 newTag: latest
Overlays
The overlays directory contains environment-specific configurations. Each environment (dev, prod, staging) has its own directory with a config.json file and a kustomization.yaml file.
k8s/overlays/dev/config.json
1{2 "name": "Noel Bansikah",3 "role": "DevOps Engineer and Software Developer",4 "nameColor": "blue",5 "roleColor": "green",6 "environment": "development"7}
k8s/overlays/dev/kustomization.yaml
1namespace: dev2resources:3 - ../../base45configMapGenerator:6 - name: profile-app-config7 files:8 - config.json=config.json
k8s/overlays/prod/config.json
1{2 "name": "Noel Bansikah",3 "role": "Senior DevOps Engineer",4 "nameColor": "red",5 "roleColor": "yellow",6 "environment": "production"7}
k8s/overlays/prod/kustomization.yaml
1namespace: prod2resources:3 - ../../base45configMapGenerator:6 - name: profile-app-config7 files:8 - config.json=config.json
k8s/overlays/staging/config.json
1{2 "name": "Noel Bansikah",3 "role": "DevOps Engineer",4 "nameColor": "green",5 "roleColor": "blue",6 "environment": "staging"7}
k8s/overlays/staging/kustomization.yaml
1namespace: staging2resources:3 - ../../base45configMapGenerator:6 - name: profile-app-config7 files:8 - config.json=config.json
Using Kustomize
Kustomize is a tool that allows you to customize Kubernetes resource configurations. It provides a way to manage different configurations for different environments without duplicating YAML files. Kustomize is built into kubectl, making it easy to use.
Advantages of Kustomize
Declarative Management: Kustomize allows you to manage Kubernetes resources declaratively.
Environment-Specific Configurations: You can manage different configurations for different environments using overlays.
No Templating: Kustomize does not use templating, making it easier to understand and maintain.
Built into
kubectl: Kustomize is integrated intokubectl, so you don't need to install any additional tools.
Alternatives to Kustomize
Helm: Helm is a package manager for Kubernetes that uses templating to manage configurations. While Helm is powerful and widely used, it can be more complex to manage compared to Kustomize.
Ksonnet: Ksonnet was another tool for managing Kubernetes configurations, but it has been deprecated in favor of Kustomize and Helm.
Why We Prefer Kustomize
We prefer Kustomize for our application because it provides a simple and declarative way to manage configurations for different environments. It is easy to use, integrated into kubectl, and does not require templating, making it easier to maintain.
Deploying the Application
1. Start Minikube
minikube start
2. Deploy to Kubernetes
// title: Bash
// copy: true
// lines: False
// maxLines: 10
kubectl apply -k k8s/overlays/dev
kubectl apply -k k8s/overlays/prod
kubectl apply -k k8s/overlays/staging
3. Test the App
Port Forwarding
// title: Bash
// copy: true
// lines: False
// maxLines: 10
kubectl port-forward -n dev svc/profile-service 8080:80
kubectl port-forward -n staging svc/profile-service 8081:80
kubectl port-forward -n prod svc/profile-service 8082:80
Access the App
Open your browser and navigate to:
Development: http://localhost:8080

Staging: http://localhost:8081
![staging access to app minikube]()
Production: http://localhost:8082
![production access to app minikube]()
4. Verify Deployments
kubectl get pods -n devkubectl get pods -n stagingkubectl get pods -n prodkubectl get services -n devkubectl get services -n stagingkubectl get services -n prod
5. Check Logs to Confirm Colors
kubectl logs -n dev deployment/profile-appkubectl logs -n staging deployment/profile-appkubectl logs -n prod deployment/profile-app
6. Test Persistent Storage
Check the Volume Mount
kubectl exec -n dev -it $(kubectl get pod -n dev -l app=profile-app -o jsonpath='{.items[0].metadata.name}') -- ls /app/config

Restart the Pod & Check if Config Persists
kubectl delete pod -n dev -l app=profile-appkubectl get pods -n dev -w
7. Verify the Updated Config
kubectl exec -n dev -it $(kubectl get pod -n dev -l app=profile-app -o jsonpath='{.items[0].metadata.name}') -- curl profile-service

Open http://localhost:8080 in your browser to see the updated configuration! 🎉
Conclusion
In this article, we explored how to deploy a Node.js application using Kustomize on a Minikube cluster. We covered the project structure, Dockerfile setup, Kubernetes manifests, and how to use Kustomize to manage different configurations for development, staging, and production environments. We also discussed the advantages of using Kustomize over other alternatives. LInk to the Code
If you have any questions or face any challenges, feel free to ask in the comments section below. Happy coding!
References
Happy coding! 🎉

