Skip to main content

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:

dockerfile
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:

bash
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:

bash
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:

bash
# 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:

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:

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:

bash
kubectl apply -f express-deployment.yaml

Verify your pods are running:

bash
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:

bash
kubectl apply -f express-service.yaml

Check that the service is running:

bash
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:

bash
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:

yaml
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:

bash
kubectl apply -f express-configmap.yaml

Then update your deployment to use this ConfigMap:

yaml
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:

bash
kubectl apply -f express-deployment.yaml

Scaling Your Application

One of Kubernetes' key benefits is easy scaling. To scale your Express application:

bash
kubectl scale deployment express-app --replicas=5

Verify the scaling:

bash
kubectl get pods
# You should now see 5 pods running

Rolling Updates

When you have a new version of your Express application:

  1. Build and push the new Docker image with a new tag:
bash
docker build -t yourusername/my-express-app:1.1.0 .
docker push yourusername/my-express-app:1.1.0
  1. Update the image in your deployment file:
yaml
spec:
containers:
- name: express-app
image: yourusername/my-express-app:1.1.0
  1. Apply the update:
bash
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:

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:

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:

yaml
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:

bash
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:

bash
# 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:

bash
kubectl exec -it express-app-7d8f7d8f7d-2k8mj -- /bin/sh

Checking Resource Usage

To see how your Express app is utilizing resources:

bash
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

Exercises

  1. Add a health check endpoint to your Express app and configure Kubernetes liveness and readiness probes
  2. Implement auto-scaling based on CPU usage using the Horizontal Pod Autoscaler
  3. Set up an Ingress controller and configure routing rules for your Express app
  4. Create separate development and production Kubernetes configurations for your Express application
  5. 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! :)