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
const express = require("express");
const fs = require("fs");
const app = express();
const PORT = process.env.PORT || 3001;
// Read the mounted config file
const CONFIG_PATH = "./config/config.json";
let config = { name: "Noel Bansikah", role: "DevOps Engineer", nameColor: "black", roleColor: "gray", environment: "development" };
// Check if the config file exists
if (fs.existsSync(CONFIG_PATH)) {
config = JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));
}
// Define a color map for meaningful colors
const colorMap = {
black: "#000000",
gray: "#6c757d",
blue: "#007BFF",
yellow: "#FFC107",
red: "#DC3545",
green: "#28A745"
};
// Get the colors from the config or default to black and gray
const nameColor = colorMap[config.nameColor] || colorMap.black;
const roleColor = colorMap[config.roleColor] || colorMap.gray;
app.get("/", (req, res) => {
res.send(`
${nameColor};">Hello, I am ${config.name} 🚀
${roleColor};">Role: ${config.role}
Environment: ${config.environment}
GitHub: bansikah22
GitLab: bansikah22
LinkedIn: bansikah22
`);
});
app.listen(PORT, () => console.log(`Server running on port ${PORT}...`));
Enter fullscreen mode
Exit fullscreen mode
Dockerfile
# Stage 1: Build stage
FROM node:18-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --production
COPY app.js ./
# Stage 2: Production stage
FROM node:18-slim
WORKDIR /app
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
COPY --from=build /app/node_modules /app/node_modules
COPY --from=build /app/app.js /app/app.js
CMD ["node", "app.js"]
EXPOSE 3001
Enter fullscreen mode
Exit fullscreen mode
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 /profile-app:latest .
Enter fullscreen mode
Exit fullscreen mode
Push the Docker image to Docker Hub:
docker push /profile-app:latest
Enter fullscreen mode
Exit fullscreen mode
Replace 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
apiVersion: apps/v1
kind: Deployment
metadata:
name: profile-app
spec:
replicas: 1
selector:
matchLabels:
app: profile-app
template:
metadata:
labels:
app: profile-app
spec:
containers:
- name: profile-app
image: /profile-app:latest
imagePullPolicy: Always
ports:
- containerPort: 3001
volumeMounts:
- name: config-volume
mountPath: /app/config
subPath: config.json
readOnly: false
volumes:
- name: config-volume
configMap:
name: profile-app-config
Enter fullscreen mode
Exit fullscreen mode
k8s/base/service.yaml
apiVersion: v1
kind: Service
metadata:
name: profile-service
spec:
selector:
app: profile-app
ports:
- protocol: TCP
port: 80
targetPort: 3001
type: ClusterIP
Enter fullscreen mode
Exit fullscreen mode
k8s/base/pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: profile-app-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
Enter fullscreen mode
Exit fullscreen mode
k8s/base/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: profile-app
Enter fullscreen mode
Exit fullscreen mode
k8s/base/kustomization.yaml
The kustomization.yaml file in the base directory defines the resources and images used in the base configuration.
resources:
- deployment.yaml
- service.yaml
- pvc.yaml
- namespace.yaml
images:
- name: profile-app
newName: /profile-app
newTag: latest
Enter fullscreen mode
Exit fullscreen mode
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
{
"name": "Noel Bansikah",
"role": "DevOps Engineer and Software Developer",
"nameColor": "blue",
"roleColor": "green",
"environment": "development"
}
Enter fullscreen mode
Exit fullscreen mode
k8s/overlays/dev/kustomization.yaml
namespace: dev
resources:
- ../../base
configMapGenerator:
- name: profile-app-config
files:
- config.json=config.json
Enter fullscreen mode
Exit fullscreen mode
k8s/overlays/prod/config.json
{
"name": "Noel Bansikah",
"role": "Senior DevOps Engineer",
"nameColor": "red",
"roleColor": "yellow",
"environment": "production"
}
Enter fullscreen mode
Exit fullscreen mode
k8s/overlays/prod/kustomization.yaml
namespace: prod
resources:
- ../../base
configMapGenerator:
- name: profile-app-config
files:
- config.json=config.json
Enter fullscreen mode
Exit fullscreen mode
k8s/overlays/staging/config.json
{
"name": "Noel Bansikah",
"role": "DevOps Engineer",
"nameColor": "green",
"roleColor": "blue",
"environment": "staging"
}
Enter fullscreen mode
Exit fullscreen mode
k8s/overlays/staging/kustomization.yaml
namespace: staging
resources:
- ../../base
configMapGenerator:
- name: profile-app-config
files:
- config.json=config.json
Enter fullscreen mode
Exit fullscreen mode
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 into kubectl, 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
Enter fullscreen mode
Exit fullscreen mode
2️⃣ Deploy to Kubernetes
kubectl apply -k k8s/overlays/dev
kubectl apply -k k8s/overlays/prod
kubectl apply -k k8s/overlays/staging
Enter fullscreen mode
Exit fullscreen mode
3️⃣ Test the App
Port Forwarding
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
Enter fullscreen mode
Exit fullscreen mode
Access the App
Open your browser and navigate to:
Development: http://localhost:8080
Staging: http://localhost:8081
Production: http://localhost:8082
4️⃣ Verify Deployments
kubectl get pods -n dev
kubectl get pods -n staging
kubectl get pods -n prod
kubectl get services -n dev
kubectl get services -n staging
kubectl get services -n prod
Enter fullscreen mode
Exit fullscreen mode
5️⃣ Check Logs to Confirm Colors
kubectl logs -n dev deployment/profile-app
kubectl logs -n staging deployment/profile-app
kubectl logs -n prod deployment/profile-app
Enter fullscreen mode
Exit fullscreen mode
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
Enter fullscreen mode
Exit fullscreen mode
Restart the Pod & Check if Config Persists
kubectl delete pod -n dev -l app=profile-app
kubectl get pods -n dev -w
Enter fullscreen mode
Exit fullscreen mode
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
Enter fullscreen mode
Exit fullscreen mode
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 CodeIf you have any questions or face any challenges, feel free to ask in the comments section below. Happy coding!
References
Kustomize Documentation
Minikube Documentation
Kubernetes Documentation
Happy coding! 🎉