Express Kubernetes Deployment
Introduction
Kubernetes has become the industry standard for container orchestration, allowing developers to deploy, scale, and manage containerized applications with ease. In this guide, you'll learn how to deploy your Express.js application to a Kubernetes cluster, enabling you to build resilient, scalable web services that can handle production traffic.
By the end of this tutorial, you'll understand:
- How to containerize your Express application
- How to define Kubernetes resources
- How to deploy and expose your Express app in a Kubernetes cluster
- How to scale and update your application
Prerequisites
Before starting this tutorial, ensure you have:
- Basic knowledge of Express.js
- A working Express application
- Docker installed on your machine
- A Kubernetes cluster (local with Minikube, or cloud-based)
kubectl
command-line tool installed and configured
Containerizing Your Express Application
Step 1: Create a Dockerfile
First, we need to package our Express application as a Docker container. Create a Dockerfile
in your project's root directory:
FROM node:16-alpine
# Create app directory
WORKDIR /usr/src/app
# Copy package files and install dependencies
COPY package*.json ./
RUN npm install
# Bundle app source
COPY . .
# Expose the port your app runs on
EXPOSE 3000
# Command to run the application
CMD ["node", "app.js"]
Step 2: Build the Docker Image
From your project directory, build the Docker image:
docker build -t my-express-app:1.0.0 .
Step 3: Test Your Container (Optional)
Before deploying to Kubernetes, test that your container works locally:
docker run -p 3000:3000 my-express-app:1.0.0
Visit http://localhost:3000
to verify your app is running correctly.
Step 4: Push to a Container Registry
For a Kubernetes cluster to pull your image, it needs to be in a container registry. Push your image to Docker Hub or another registry:
# Tag your image (replace 'yourusername' with your Docker Hub username)
docker tag my-express-app:1.0.0 yourusername/my-express-app:1.0.0
# Push to Docker Hub
docker push yourusername/my-express-app:1.0.0
Defining Kubernetes Resources
Step 1: Create a Kubernetes Deployment File
Create a file named express-deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: express-app
labels:
app: express-app
spec:
replicas: 3
selector:
matchLabels:
app: express-app
template:
metadata:
labels:
app: express-app
spec:
containers:
- name: express-app
image: yourusername/my-express-app:1.0.0
ports:
- containerPort: 3000
resources:
limits:
cpu: "0.5"
memory: "512Mi"
requests:
cpu: "0.2"
memory: "256Mi"
env:
- name: NODE_ENV
value: "production"
This YAML file defines a Deployment that:
- Creates 3 replicas (instances) of your Express app
- Uses your Docker image
- Sets resource constraints for CPU and memory
- Sets an environment variable for production mode
Step 2: Create a Service Resource
To expose your Express application to the outside world, create a file named express-service.yaml
:
apiVersion: v1
kind: Service
metadata:
name: express-app-service
spec:
selector:
app: express-app
ports:
- port: 80
targetPort: 3000
type: LoadBalancer
This service:
- Selects all pods with the
app: express-app
label - Maps port 80 on the service to port 3000 on the pods
- Uses a LoadBalancer type to expose the service externally
Deploying to Kubernetes
Step 1: Apply the Deployment
Apply your deployment configuration to your Kubernetes cluster:
kubectl apply -f express-deployment.yaml
Verify your pods are running:
kubectl get pods
# Expected output:
# NAME READY STATUS RESTARTS AGE
# express-app-7d8f7d8f7d-2k8mj 1/1 Running 0 30s
# express-app-7d8f7d8f7d-9j2n8 1/1 Running 0 30s
# express-app-7d8f7d8f7d-xr5nm 1/1 Running 0 30s
Step 2: Apply the Service
Now, apply your service configuration:
kubectl apply -f express-service.yaml
Check that the service is running:
kubectl get services
# Expected output:
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# express-app-service LoadBalancer 10.100.200.30 203.0.113.42 80:32563/TCP 45s
Depending on your Kubernetes environment, the EXTERNAL-IP
might take a moment to be assigned.
Step 3: Access Your Application
Once the external IP is assigned, you can access your Express application at that IP address:
curl http://EXTERNAL-IP
# or open in a browser
Adding a ConfigMap for Application Configuration
For better configuration management, let's use a ConfigMap to store application settings:
apiVersion: v1
kind: ConfigMap
metadata:
name: express-app-config
data:
APP_PORT: "3000"
API_TIMEOUT: "5000"
LOG_LEVEL: "info"
Save this as express-configmap.yaml
and apply it:
kubectl apply -f express-configmap.yaml
Then update your deployment to use this ConfigMap:
apiVersion: apps/v1
kind: Deployment
metadata:
name: express-app
# ...previous metadata
spec:
# ...previous spec
template:
# ...previous template
spec:
containers:
- name: express-app
# ...previous container config
envFrom:
- configMapRef:
name: express-app-config
Apply the updated deployment:
kubectl apply -f express-deployment.yaml
Scaling Your Application
One of Kubernetes' key benefits is easy scaling. To scale your Express application:
kubectl scale deployment express-app --replicas=5
Verify the scaling:
kubectl get pods
# You should now see 5 pods running
Rolling Updates
When you have a new version of your Express application:
- Build and push the new Docker image with a new tag:
docker build -t yourusername/my-express-app:1.1.0 .
docker push yourusername/my-express-app:1.1.0
- Update the image in your deployment file:
spec:
containers:
- name: express-app
image: yourusername/my-express-app:1.1.0
- Apply the update:
kubectl apply -f express-deployment.yaml
Kubernetes will perform a rolling update, replacing pods one by one with the new version, ensuring zero downtime.
Real-World Example: Express API with MongoDB and Redis
Let's see a more complete example of deploying an Express application with database dependencies.
MongoDB StatefulSet and Service
Create mongodb-statefulset.yaml
:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mongodb
spec:
serviceName: mongodb-service
replicas: 1
selector:
matchLabels:
app: mongodb
template:
metadata:
labels:
app: mongodb
spec:
containers:
- name: mongodb
image: mongo:4.4
ports:
- containerPort: 27017
volumeMounts:
- name: mongodb-data
mountPath: /data/db
volumeClaimTemplates:
- metadata:
name: mongodb-data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Service
metadata:
name: mongodb-service
spec:
selector:
app: mongodb
ports:
- port: 27017
targetPort: 27017
clusterIP: None # Headless service for StatefulSet
Redis Deployment and Service
Create redis-deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:6-alpine
ports:
- containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:
name: redis-service
spec:
selector:
app: redis
ports:
- port: 6379
targetPort: 6379
Updated Express Deployment
Now update your Express deployment to connect to these services:
apiVersion: apps/v1
kind: Deployment
metadata:
name: express-app
spec:
# ... other specs
template:
spec:
containers:
- name: express-app
image: yourusername/my-express-app:1.0.0
env:
- name: MONGODB_URI
value: "mongodb://mongodb-service:27017/myapp"
- name: REDIS_HOST
value: "redis-service"
- name: REDIS_PORT
value: "6379"
Apply all configurations:
kubectl apply -f mongodb-statefulset.yaml
kubectl apply -f redis-deployment.yaml
kubectl apply -f express-deployment.yaml
This setup gives you a complete stack with your Express application connected to MongoDB for data storage and Redis for caching or session management.
Troubleshooting
Viewing Logs
To troubleshoot issues with your Express application, check the logs:
# Get pod names
kubectl get pods
# View logs for a specific pod
kubectl logs express-app-7d8f7d8f7d-2k8mj
# Stream logs (follow)
kubectl logs -f express-app-7d8f7d8f7d-2k8mj
Debugging with a Shell
To debug from inside a pod:
kubectl exec -it express-app-7d8f7d8f7d-2k8mj -- /bin/sh
Checking Resource Usage
To see how your Express app is utilizing resources:
kubectl top pods
Summary
In this tutorial, you've learned how to:
- Containerize an Express.js application using Docker
- Define Kubernetes deployments, services, and ConfigMaps
- Deploy your Express application to a Kubernetes cluster
- Scale your application and perform rolling updates
- Set up a complete stack with database dependencies
- Troubleshoot common issues
Deploying Express applications on Kubernetes provides numerous benefits:
- High availability through multiple replicas
- Easy scaling to meet demand
- Rolling updates for zero-downtime deployments
- Consistent environment management
- Self-healing capabilities
Additional Resources
- Official Kubernetes Documentation
- Node.js Kubernetes Deployment Best Practices
- Express.js Production Best Practices
- 12-Factor App Methodology
Exercises
- Add a health check endpoint to your Express app and configure Kubernetes liveness and readiness probes
- Implement auto-scaling based on CPU usage using the Horizontal Pod Autoscaler
- Set up an Ingress controller and configure routing rules for your Express app
- Create separate development and production Kubernetes configurations for your Express application
- Implement monitoring for your Express app using Prometheus and Grafana
By following these practices and continually improving your deployment strategy, you'll have a robust, scalable Express application running reliably in Kubernetes.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)