Deploy Node.js application in local minikube Kubernetes cluster

Deploy Node.js application in local minikube Kubernetes cluster

Previously, we have learned the components of Kubernetes and learned how to spin up a mini Kubernetes cluster in our system using minikube. This article will learn how to deploy a Node.js application in the local minikube Kubernetes cluster. We’ll do this demonstration on Windows 10 Pro laptop.

Prerequisites

Follow the below steps to build and deploy a simple Node.js application in the local minikube Kubernetes cluster.

Build Node.js application

Create a folder with the name nodekube in D:\ drive (or any drive of your choice). Open a command prompt and traverse to D:\nodekube and execute the npm init command. This command will ask for information about the application we will build. If you are happy with the default values provided by the wizard, just press Enter, otherwise enter your preferred values. It’ll create a package.json file inside the current directory when wholly executed. This file holds various metadata relevant to the project. This file also contains information that helps npm to identify the project. It also handles the dependencies of the project. For reference, check the snip of the application I created.

npm init
npm, init to generate package.json

We’ll now write a straightforward script. When executed, it’ll render Hello World from Kubernetes! Message. Please copy the below code and save it as index.js inside the nodekube folder.

Now, go back to the command prompt, traverse to the nodekube folder, and issue this command: node index.js. Then, open a browser and hit http://localhost:8080. You will see Hello World from Kubernetes!.

Dockerize nodejs application

Our Node.js application is now ready. But to run it in Kubernetes, we’ll need to create an image. We’ll then deploy this image in the local minikube Kubernetes cluster and run it as a docker container. Let’s write a Dockerfile and save it in the same D:\nodekube folder.

We’ll now launch the command prompt, traverse to D:\nodekube folder and build an image by executing the following command :

Let’s execute the following command to instantiate the image and run the Node.js application locally in a docker container.

Let’s go to the browser and hit http://localhost:8081. We’ll see the same output as before; however, this time from the container that is running locally.

I have written a detailed blog on how to containerize a node.js application with explanation of every tiny detail. Should you have any question on the above steps, you can head there for clarification.

https://iteritory.com/dockerize-nodejs-web-application-step-by-step-guide/

Push the image in docker hub

We’ll use the docker hub as our container registry of the local Kubernetes cluster run by minikube. This means Kubernetes will fetch the docker image from the docker hub container registry and create a Pod to run the container. Hence, We’ll now push the above-created image to the docker hub. Please follow the below steps:

  • If you don’t have a docker id already, head to https://hub.docker.com/ and sign up.
  • Then sign in with your credetial and click on Repositories and then Create Repository.
  • Make the repository name as hello-world-node. You can refer to how I created:
Create repository in docker hub
Create a repository in the docker hub
  • Now, we’ll login to docker hub using the command line. It’ll prompt to enter the docker hub id and password. Key in those information to login.
Docker login command
Docker login command

  • Once logged in successfully, we’ll issue the following command to push the image (that we just created above) to the docker hub.
  • After successful execution, we’ll see the image got pushed to the docker hub. Please refer to the snippet below. This is after I successfully pushed the locally built image to the Docker hub using the above command:
Docker Hub Image Repository
Docker Hub Image Repository

Start the Kubernetes cluster

Now, we’ll start our local Kubernetes cluster run by minikube. Let’s issue the following command to do so:

If you have not already installed minikube or have question about minkube installation process etc, please refer to my blog on how to install minkube in your local system.

https://iteritory.com/install-minikube-in-windows-10-laptop-step-by-step-tutorial/

Deploy the Docker image in the local Kubernetes cluster

At this point, we have developed the application, created an image from this application, pushed the image in the docker hub container registry. Our goal is to instantiate this image build and run it inside a container. In Kubernetes, the container does not run on its own; instead, it’s encapsulated into a Pod. Pods are the smallest object that we can create and manage in Kubernetes.

Kubernetes takes YAML-based configuration files as input and creates different objects like Pods, replicas, services, etc. In the following section, we’ll define a Pod configuration.

Define a YAML configuration for Pod

A Kubernetes object definition contains four root-level essential fields:

apiVerson

This key refers to the version of Kubernetes API that Kubernetes will use to create an object. Depending on what object we want to build, Kubernetes has different versions of APIs. In this case, to create a Pod object, we’ll use its value as v1.

kind

This key informs Kubernetes what type of object we are going to create. In this case, we are creating a Pod object. Hence, we’ll use its value as Pod. Other possible values are Pod, Job, ReplicaSet, etc.

metadata

The value of the metadata field is a dictionary, unlike apiVersion and kind where we supplied string value. This field describes some data about the object to be created. It also provides a means to identify it uniquely. In our case, we’ll define the following:

  • name A name that uniquely identifies the pod. In our case, we’ll name it as helloworld-pod
  • labels: These are custom key/value pairs that are assigned to an object as its property. We can use labels to organize the objects and also to group a similar type of pods in a cluster. It can help us identify and query the related objects. For example, in a retail enterprise, we can have multiple different modules like order-management, inventory-management etc. We can assign a label of inventory-management to a group of pods that run the inventory related services. There is no limitation on the number of labels we can assign. In our scenario, let’s add two labels for demonstration purpose:
    • release: beta
    • type: poc

spec

Depending on the kind of object we create, the content of this field describes the specification of that object. Kubernetes understands the desired state of the object through the configurations in the spec section. While defining the pod object, we use this section to tell Kubernetes what containers will be part of the Pod, their memory, storage requirement, etc. In this POC, our need is simple. We have only one container to run in the Pod that we’ll define. Spec is a dictionary. Let’s take a look at the values we will use:

  • replicas: This field defines how many instances of this Pod we want to run. For this POC, let’s create just one Pod to start with. So, the value will be 1.
  • containers: This segemnt is a YAML list/array because it can define more than one container if needed. The properties of each item in this list is again a dictionary. We’ll use following properties:
    • name: Each container in a pod must have a unique name and it can’t be updated. In our case, we’ll keep the value as helloworld-container.
    • image: In this field, we’ll mention the image infromation which in our case is msadrud/hello-world-node:v1. This is the image we have pushed into docker hub just a while back.
    • ports: This segment is a YAML list/array that defines the list of ports to be exposed from the container. Exposing a port here gives the system additional information about the network connections a container uses. We’ll use only following property in the ports dictionary:
      • containerPort: The value tells kubernetes the port number to expose on the Pod’s IP address. This must be a valid port number, 0 < x < 65536. So, here we’ll use port number 8080 which is the port, we have exposed from our code.

Now, putting all these elements together, the following is how our Pod configuration looks like.

Define a YAML configuration for Deployment

We can use the above configuration snippet to create Pods manually easily. But this way, the management (e.g., upgrade, update, rollback, scale-up/down, etc.) of these Pods will be manual. Instead, we can use a Deployment object that defines a Pod’s desired state and let Kubernetes take care of the Pod management in the cluster. We’ll learn how to use this k8s object as part of this POC.

Let’s create a Deployment configuration file with the name hello-world-deployment-def.yaml in the D:\nodekube folder. As it is for any other k8s definition file, this one too has four main sections:

  • apiVersion: We’ll use the value apps/v1
  • kind: This value will be Deployment
  • metadata:
    • name: We’ll name it as helloworld-deployment
    • labels: We’ll create following two custom labels
      • release: beta
      • type: poc
  • spec:
    • replicas: This field defines how many instances of this Pod we want to run. For this POC, let’s create just one Pod to start with. So, the value will be 3.
    • selectors: The value of this section helps us to group which all Pods fall under it. The value of selector needs to be written as matchLabels. The matchLabels seclector matches labels written under it with the label of the Pods. In our Poc, we’ll use the followings as as its value:
      • release: beta
      • type: poc
    • template: In this section, we need to provide the Pod template. We have already created a Pod defintion above. WE’ll use that configuration except for the first two lines.

Combining all these, let’s take a look at what the complete Deployment definition looks like:

Create Deployment in kubernetes cluster

We will open a command prompt and execute the following command to create a deployment in the locally run k8s cluster.

Upon successful execution of the command, we’ll see the following:

We can issue the kubectl get pods to deploy command to get the pod and deployment status. It may take a while to bring the pod to running status, so have patience!

At this point, if we visit the Kubernetes dashboard, we’ll see something like the following. It’s interesting to note that k8s created a replicaset in the process (more about it in some other blog).

Expose the nodejs application outside cluster

These Pods that we just created are now running inside our local cluster. They have their own IPs that are accessible only from inside the cluster. The Pods are ephemeral to add to the complexity; their IPs are not static either. When a Pod is recreated, they can get new IPs. We can also have multiple replicas of these Pods running in the cluster spanning more than one node.

With such complexities, the question is, how do we discover and access the application from outside of the cluster familiarly and consistently without having to worry about the complexities we just discussed above? Well, Kubernetes addresses this problem with Services. A service in Kubernetes is an object to expose an application (be enabling network access) running on a group of Pods deployed in a cluster.

In Kubernetes, there are different types of services available to choose from:

  • NodePort
  • ClusterIP
  • LoadBalancer
  • ExternalName

We’ll use the NodePort service type to expose the API in this POC. In some later blogs, I’ll try and cover the other options.

Typically, we use NodePort to expose a service via a static port accessible on each node’s IP. It maps a port on the node to the port on the pod. In the NodePort setup, we deal with three different types of ports:

  • NodePort: A port on the Node. This enables the access to service from external consumers. The range of NodePort values is from 30000 to 32767.
  • port: This is the port on the Service object. It exposes the service within the cluster. Other pods in the cluster can communicate with the service using this port. The port basically redirects the traffic from service to the container port.
  • targetPort: This is the port where the application is running in the container.

Define a YAML configuration for NodePort Service

Let’s create a Service configuration file with the name hello-world-service-def.yaml in the D:\nodekube folder. As it is for any other k8s definition file, this one too has four main sections:

  • apiVersion: We’ll use the value v1
  • kind: This value will be Service
  • metadata:
    • name: We’ll name it as helloworld-service
  • spec:
    • type: This field defines which type of serice we’ll create. We’ll use NodePort.
    • ports: This is where we configure the 3 types of ports that we discussed above. this section is an array. We can have multiple such port mapping in a single service.
      • targetPort: The value of this field will be 8080. This is the port where our nodejs application is running in the Pod. This is an optional field. If not mentioned, the value of port will be assigned for targetPort.
      • port: We’ll set this value as 80. This is a mandatory field.
      • nodePort: We’ll set this value as 30001. The range of valid values are from 30000 to 32767. This is an optional field. If we don’t mention this value in the service defintion file explicitly, an available port will be randomly selected.

Looking at configuration carefully, we have mapped a Service port 80 to a Pod targetPort 8080. But of which Pod? There can be many Pods running different applications on port 8080. We use labels and selectors to bridge this missing link.

  • selectors:
    • The value of this section helps us to link the Pod with the Service. So, we’ll just copy the labels that we mentioned in the Pod definition file.
      • release: beta
      • type: poc

Combining all these, let’s take a look at what the complete Service definition looks like:

Create Service in kubernetes cluster

We will open a command prompt and execute the following command to create a NodePort Service in the locally run k8s cluster.

Upon successful execution of the command, we’ll see the following:

We can issue the kubectl get services command to get the service we just created.

How to access the application running in minikube?

The minikube VM is exposed to the host system via a host-only IP address that we can obtain with the minikube ip command. We can access any services of type NodePort over that IP address on the NodePort.

We can also get the URL by executing the following command:

Now, if we hit this URL in the browser, we’ll see the response from the nodejs application running inside a Pod in our local Kubernetes cluster.

NodeJs application running in Kubernetes.

Well, that’s it!!

GitHub link

You can find the codes written here in the GitHub link: https://github.com/msadrud/kubernetes-nodejs-hello-world.

Conclusion

I hope you learned how to deploy a simple Node.js application in the local minikube Kubernetes cluster and expose it for external access. Stay tuned for more exciting articles around Kubernetes.

References

44

No Responses

Write a response

This site uses Akismet to reduce spam. Learn how your comment data is processed.