How to Deploy a Node Application with Kubernetes

Ali Balbars
5 min readNov 23, 2023

--

In this article I will show you how to dockerize a Node application and how to deploy it via Kubernetes in local environment.

Let’s start with creating a Node application. I will simply expose an endpoint with Express. You can write any other types of application that exposes the application with a URL.

package.json
index.js

To dockerize the app we need to create a Dockerfile

Dockerfile is a text file that contains a set of instructions for building a Docker image.

Dockerfile

Then let’s create a .dockerignore file.

.dockerignore

.dockerignore file is meant to exclude files or directories from the build context. This helps avoid sending unwanted files and directories to the builder, improving build speed, especially when using a remote builder.

Build docker image

By default the docker build command will look for a Dockerfile at the root of the build context. So you can omit it in this case. The -f, --file, option lets you specify the path to an alternative file to use instead.

So far so good! We created our image. To create a K8s cluster in local we will use Minikube. For MacOS it can be installed easily via Homebrew.

Minikube is a tool that helps you run K8s on your local machine. It allows you to set up a small, single-node Kubernetes cluster, which is useful for development, testing, and learning purposes.

Install minikube

After installing minikube we can start our cluster by minikube start command.

Start minikube

After initializing the local cluster we need to pass our image to our cluster. Minikube has a command to do that.

Load image to minikube cluster

Note that you should indicate the image name which you builded previously.

Minikube allows us to list images within the cluster by:

Creating the Deployment

Now we have our image in cluster. Next, we should create a K8s deployment to deploy the image.

deployment.yaml
Application of deployment.yaml

To explain it line by line:

apiVersion: apps/v1: Specifies the Kubernetes API version for the resource. In this case, it's using the "apps" API version, version 1.

kind: Deployment: Defines the type of Kubernetes resource.

metadata: Contains metadata about the Deployment, including the name and namespace.

name: express-app: Name of the Deployment.

namespace: default: Namespace in which the Deployment will be created. In this case, it's the default namespace.

spec: Describes the desired state for the Deployment.

replicas: 1: Desired number of replicas (instances) for the application. You can scale the number of replicas up or down based on your requirements.

selector: Defines how the Deployment identifies which Pods to manage.

matchLabels: Specifies the labels that the Deployment uses to match with Pods. In this case, it's looking for Pods with the label "app: express-app."

template: Describes the Pod template used to create new Pods when scaling or replacing existing ones. Basically, this field is for desired state of the Pods.

metadata: Contains labels that are applied to Pods created from this template.

labels: Sets the label "app: express-app" for Pods created from this template.

spec: Specification for Pods.

containers: Describes the containers within the Pod.

name: express-app: Name of the container.

image: docker.io/alibalbars/express-app:latest: Docker image to use for the container.

imagePullPolicy: Never: Sets the image pull policy to "Never," meaning Kubernetes will not attempt to pull the image from the registry if it's not already present locally. In our case because we have our image in cluster so we should set it as “Never”. We don’t need to pull any images from a remote registery.

ports: Specifies the ports that should be opened on the container.

containerPort: 3000: Exposes port 3000 on the container. This is the port on which the application inside the container is expected to be listening. This should be our express application’s expose port, which is 3000.

Creating the Service

The deployment lives inside the cluster and doesn’t have any connections from outside the cluster. To expose the deployment to outside we need to create a K8s Service:

service.yaml
Application of service.yaml

apiVersion: v1: Specifies the API version of the Kubernetes object.

kind: Service: Type of Kubernetes resource.

metadata: Contains metadata about the Service, including its name and namespace.

name: express-service: Name of the Service.

namespace: default: Namespace in which the Service is created.

spec: Describes the desired state for the Service.

type: NodePort: Specifies the type of Service. NodePort exposes the Service on each Node's IP at a static port (the NodePort). It makes the Service accessible from outside the cluster using the <NodeIP>:<NodePort> combination.

selector: Defines how the Service selects which Pods to target. In our case, it selects Pods with the label app: express-app.

ports: Specifies the ports that the Service will use.

port: 3000: The port on which the Service will be exposed. In this case, it's port 3000. Which means we will access our application by writing this port to browser.

targetPort: 3000: Specifies the port on the Pod to which the traffic will be forwarded. This is the port on which your application inside the Pod is listening. So this should be the port our containers exposed.

The Last Step

To see our app in browser, we should use minikube tunnelcommand.

minikube tunnel is a command that bridges the gap between your local environment and the services running inside the Minikube cluster. By creating a network tunnel, it allows external access to these services.

Tunnel connection

Happy ending :) After tunneling we can access our application on browser.

Browser

What is next?

In the next chapter I will explain the essential components in K8s in more detail. Happy orchestrating!

--

--