Categories
Kubernetes Node.js Redis

An example of Redis persistence using Node.js and Kubernetes

In the previous example we have seen how to use Docker Compose to deploy a Node.js microservice to interact with Redis, also showing in a basic way how Redis persistence works.

Now let’s see istead how to use Kubernetes and Docker to do the same, showing also how we can easily scale up our application.

To try this example on your PC you only need to install Docker Desktop and Node.js then follow the described steps.

Create a directory for this example and inside copy the two directories from the previous example, redis-server and webservice.

mkdir node-redis-example-3
cd node-redis-example-3
cp -r ../node-redis-example-2/redis-server/ .
cp -r ../node-redis-example-2/webservice/ .

Enter the redis-server directory and build our redis-server image.

cd redis-server
docker build -t redis-server:1.0.0 -f Dockerfile .

Create a deploy.yml file that we will use to configure on Kubernetes a single Pod with a container running our redis-server image.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-server
  labels:
    application: node-redis-example-3
    component: redis-server
    type: single
spec:
  replicas: 1
  selector:
    matchLabels:
      application: node-redis-example-3
      component: redis-server
      type: single
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
    type: RollingUpdate
  template:
    metadata:
      labels:
        application: node-redis-example-3
        component: redis-server
        type: single
    spec:
      containers:
        - image: redis-server:1.0.0
          name: redis-server
          volumeMounts:
            - mountPath: /redis-data
              name: redis-data
          ports:
            - containerPort: 6379
              name: http
              protocol: TCP
      volumes:
        - name: redis-data
          hostPath:
            path: /run/desktop/mnt/host/c/redis-data
            type: DirectoryOrCreate

As you can see we’ve just configured the bind of the same directory used in the previous example, to store Redis persistence files: dump.rdb for RDB and appendonly.aof for AOF.

containers:
    - image: redis-server:1.0.0
        name: redis-server
        volumeMounts:
        - mountPath: /redis-data
            name: redis-data
        ports:
        - containerPort: 6379
            name: http
            protocol: TCP
    volumes:
    - name: redis-data
        hostPath:
        path: /run/desktop/mnt/host/c/redis-data
        type: DirectoryOrCreate

If you are using Docker Desktop on Windows with WSL 2 you actually have to configure your host directory with a different path respect the previous example even it’s the same directory on your machine.
You can read more on this official GitHub Docker Desktop issue: “Kubernetes Volumes not correctly mounted with WSL2”.

#Previouse example
#Docker Compose using Docker Desktop on Windows with WSL 2 
/mnt/c/redis-data
#This example
#Kubernetes using Docker Desktop on Windows with WSL 2
/run/desktop/mnt/host/c/redis-data

Apply the redis-server deployment configuration.

kubectl apply -f ./deploy.yml

Create a Kubernetes Service configuration file for our redis-server.

apiVersion: v1
kind: Service
metadata:
  name: redis-server
  labels:
    application: node-redis-example-3
    component: redis-server
    type: single
spec:
  ports:
    - port: 6379
      protocol: TCP
      targetPort: 6379
  selector:
    application: node-redis-example-3
    component: redis-server
    type: single

Apply the redis-server service configuration.

kubectl apply -f ./service.yml

Enter the webservice directory and build the microservice image.

cd ../webservice
docker build -t webservice:1.0.0 -f Dockerfile .

Create a deploy.yml file that we will use to configure on Kubernetes a two replicas of our Node.js webservice.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    application: node-redis-example-3
    component: webservice
  name: webservice
spec:
  replicas: 2
  selector:
    matchLabels:
      application: node-redis-example-3
      component: webservice
  strategy:
    rollingUpdate:
      maxSurge: 2
      maxUnavailable: 0
    type: RollingUpdate
  template:
    metadata:
      labels:
        application: node-redis-example-3
        component: webservice
        redeploy: '1'
    spec:
      containers:
        - name: webservice
          image: webservice:1.0.0
          env:
          - name: REDIS_SERVER_IP
            value: "redis-server"
          ports:
          - name: webservice
            containerPort: 3000
            protocol: TCP

Apply the webservice deployment configuration.

kubectl apply -f ./deploy.yml

Create a Kubernetes Service configuration file for our webservice.

apiVersion: v1
kind: Service
metadata:
  name: webservice
  labels:
    application: node-redis-example-3
    component: webservice
spec:
  type: NodePort
  selector:
    application: node-redis-example-3
    component: webservice
  ports:
  - protocol: TCP
    port: 3000
    targetPort: 3000
    nodePort: 30000

Apply the webservice service configuration.

kubectl apply -f ./service.yml

You will then be able to see the pods created and their containers in the Docker Desktop dasbboard.

Docker Desktop dashboard showing pods and containers

Now, as in the previous example, just using Postman we can now try to set a key value pair, we need only to change the port from 3000 to 30000, because the webservice Service type in this case is NodePort.

Set a key pair value using Postman

And then retrieve the saved value through its key.
The webservice Service will distribute the requests to the two replicas.

Get a key pair value using Postman

You can easily scale up the webservice replicas, for example from two pods / containers to four.

kubectl scale deploy webservice --replicas=4

You’ll be able to see the new pods and containers in the Docker Desktop dashboard.

Docker Desktop dashboard showing scaled replicas

You can find the source code on this GitHub repository:
https://github.com/robertobandini/node-redis-example-3
It also includes the Postman collection used and a sw-version.txt file to specify the softwares used for this project and their versions.

How we can do the load testing of this application?
What happens if we have high concurrecy calls trying to set the same key value pair?
We will talk about these arguments in the nexts posts.