Skip to main content

Command Palette

Search for a command to run...

Deploying a Node.js Application on Kubernetes with Jenkins, Docker, and Kind: A Step-by-Step Guide

Published
8 min read
A

I'm a DevOps Engineer

In this blog post, we will walk through the process of deploying a Node.js application on Kubernetes using Jenkins for CI/CD, Docker for containerization, and Kind (Kubernetes in Docker) for local cluster management. We will cover everything from setting up Jenkins to troubleshooting common errors. Let’s get started!

Prerequisites

Before we dive in, ensure you have the following installed on your machine:

  • Docker

  • Kind

  • kubectl

  • Jenkins

  • A Node.js application (we'll use a sample app from GitHub)

Setting Up Jenkins

Install Required Plugins

After logging in, Jenkins will prompt you to install plugins. Make sure to install the following essential plugins:

  • Git Plugin

  • Docker Pipeline

  • Kubernetes CLI Plugin

Create a Jenkins Pipeline Project

  1. Go to New Item > Select Pipeline > Give your project a name > Click OK.

  2. In the project settings, define a Jenkinsfile, which will contain the pipeline script.

Creating a Kubernetes Cluster with Kind

Create a Kind Cluster

To create a local Kubernetes cluster, run the following command:

kind create cluster --name my-cluster-phontiqe

Verify the Cluster

Check that your cluster is running:

kind get clusters

Configure Jenkins to Connect to Your Kubernetes Cluster

Install kubectl in Jenkins

You need to install kubectl on your Jenkins instance. You can do this by adding a kubectl executable inside Jenkins or using the Kubernetes CLI plugin.

Retrieve kubeconfig from Kind

To access your cluster, you need the kubeconfig file. You can export it using:

kind get kubeconfig --name my-cluster-phontiqe

Add kubeconfig as a Secret File in Jenkins

  1. Go to Manage Jenkins > Manage Credentials.
  1. Click on Add Credentials.

  2. Choose Secret file as the type.

  3. Upload your kubeconfig file and assign it an ID (e.g., kubeconfig).

    ls -la > cd .kube > cat config

Create the Jenkins Pipeline

pipeline {
    agent any

    tools {
        nodejs "nodejs"
    }

    environment {
        DOCKER_IMAGE = "lucky0111/nodejs"
        DOCKER_TAG = "latest"
        KUBE_NAMESPACE = "nodejs"
        KUBE_DEPLOYMENT = "nodejs-deployment"
    }

    stages {
        stage('Checkout Code') {
            steps {
                // Cloning the code from the repository
                git branch: 'main', url: 'https://github.com/mansurianas/node-phontiqe.git'
            }
        }

        stage('Build Docker Image') {
            steps {
                script {
                    // Building the Docker image
                    sh 'docker build -t $DOCKER_IMAGE:$DOCKER_TAG .'
                }
            }
        }

        stage('Docker Login') {
            steps {
                script {
                    // Logging into DockerHub securely using Jenkins credentials
                    withCredentials([usernamePassword(credentialsId: 'dockerhub-credentials', passwordVariable: 'DOCKER_PASSWORD', usernameVariable: 'DOCKER_USERNAME')]) {
                        sh 'echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin'
                    }
                }
            }
        }

        stage('Push Docker Image') {
            steps {
                script {
                    // Pushing the Docker image to DockerHub
                    sh 'docker push $DOCKER_IMAGE:$DOCKER_TAG'
                }
            }
        }

        stage('Deploy to Kubernetes') {
            steps {
                script {
                   // Using the kubeconfig secret file from Jenkins credentials
                    withCredentials([file(credentialsId: 'kube_config_id', variable: 'KUBECONFIG_PATH')]) {
                        // Set the KUBECONFIG environment variable to the path of the secret file
                        sh '''
                        export KUBECONFIG=$KUBECONFIG_PATH

                        kubectl apply -f k8s/namespace.yml
                        kubectl apply -f k8s/deployment.yml
                        kubectl apply -f k8s/service.yml
                        '''
                    }
                }
            }
        }

        stage('Verify Deployment') {
            steps {
                script {
                    // Verifying if the pods are running
                    withCredentials([file(credentialsId: 'kube_config_id', variable: 'KUBECONFIG_PATH')]) {
                        sh '''
                        export KUBECONFIG=$KUBECONFIG_PATH
                        kubectl get pods -n $KUBE_NAMESPACE
                        '''
                    }
                }
            }
        }
    }

    post {
        always {
            // Clean up Docker images to free space
            sh 'docker rmi $DOCKER_IMAGE:$DOCKER_TAG || true'
        }
        success {
            echo 'Deployment completed successfully!'
        }
        failure {
            echo 'Deployment failed!'
        }
    }
}

Access the Application

If your service is of type NodePort, you can access it using the node's IP address and the allocated port:

bashRunCopy code1http://<node-ip>:<node-port>

If your service is of type LoadBalancer, use the external IP provided by Kubernetes.

Port Forwarding (If Required)

If your service is of type ClusterIP, you can use port forwarding:

kubectl port-forward service/nodejs-service -n nodejs 3000:3000 --address=0.0.0.0

Then access your application at:

http://localhost:3000

i can also do kubectl port-forward service/nodejs-service -n nodejs 2000:3000 --address=0.0.0.0

and then access http://localhost:2000

Troubleshooting Common Errors

Error: Authentication Required

If you encounter an authentication error when accessing the Kubernetes API, ensure your kubeconfig file contains the correct credentials. Verify that the user and context are set up properly.

Error: Service Not Found

If your service is not found, double-check your service and deployment YAML files for any misconfigurations.

\=================================================

When to Use Port Forwarding in Kubernetes

Port forwarding is a useful technique in Kubernetes that allows you to access services running inside your cluster from your local machine. Here are scenarios when you might need to use port forwarding:

1. Service Type is ClusterIP

  • ClusterIP is the default service type in Kubernetes, which makes the service accessible only within the cluster. If you want to access a ClusterIP service from outside the cluster (e.g., from your local machine), you need to use port forwarding.

2. Local Development and Testing

  • When developing applications locally, you may want to test your services without exposing them to the internet. Port forwarding allows you to access your application running in the cluster as if it were running locally.

3. No LoadBalancer or Ingress Setup

  • If you do not have a LoadBalancer service configured (common in local setups like Kind or Minikube) or an Ingress controller set up, port forwarding provides a quick way to access your services without additional configuration.

4. Debugging and Troubleshooting

  • Port forwarding can be useful for debugging purposes. If you need to check logs or interact with a service directly, you can forward a port to access it without modifying the service configuration.

How to Use Port Forwarding

To set up port forwarding, you can use the following command:

kubectl port-forward service/nodejs-service -n nodejs 3000:3000 --address=0.0.0.0

Explanation of the Command:

  • service/nodejs-service: This specifies the service you want to forward traffic to.

  • -n nodejs: This specifies the namespace where your service is located.

  • 3000:3000: The first 3000 is the local port on your machine, and the second 3000 is the port on the service inside the cluster. You can change the first port to any available port on your local machine if needed.

  • --address=0.0.0.0: This option allows the port to be accessible from all network interfaces, not just localhost. This is useful if you want to access the service from other devices on the same network.

Accessing the Application

Once you run the port forwarding command, you can access your application in your web browser or via curl:

http://localhost:3000

Or, if you specified a different local port, replace 3000 with that port.

Conclusion

Port forwarding is a powerful feature in Kubernetes that allows you to access services running in your cluster without exposing them to the internet. It is particularly useful for local development, testing, and debugging scenarios. Whenever you have a ClusterIP service and need to access it from outside the cluster, port forwarding is the way to go!

If you configure your Kubernetes service as a NodePort, you typically do not need to use port forwarding to access your application. Here’s a breakdown of why that is the case:

NodePort Service

  1. Direct Access: A NodePort service exposes your application on a specific port on each node in the cluster. This means that you can access the service directly using the node's IP address and the NodePort.

  2. How It Works: When you create a NodePort service, Kubernetes allocates a port from a predefined range (usually between 30000 and 32767) and makes it accessible on all nodes in the cluster. You can access your application using:

     http://<node-ip>:<node-port>
    

    Here, <node-ip> is the IP address of any node in your cluster, and <node-port> is the port assigned to your service.

Example

If you have a NodePort service defined like this:

kind: Service
apiVersion: v1
metadata:
  name: nodejs-service
  namespace: nodejs
spec:
  selector:
    app: nodejs-app
  ports:
    - protocol: TCP
      targetPort: 3000
      port: 3000
      nodePort: 30001  # This is the NodePort
  type: NodePort

You can access your application directly via:

http://<node-ip>:30001

When to Use Port Forwarding

  • ClusterIP Services: If your service is of type ClusterIP, you will need to use port forwarding to access it from outside the cluster.

  • Local Development: If you want to quickly test a service without changing its type or if you are working in a local environment where you want to avoid exposing services unnecessarily.

  • Debugging: If you need to troubleshoot or debug a service without modifying its configuration.

To see the IP addresses of the nodes in your Kubernetes cluster, you can use the kubectl get nodes command. This command will provide you with a list of all nodes along with their details, including their internal IP addresses. Here’s how to do it:

Open Your Terminal

Make sure you have access to your terminal where kubectl is installed and configured to communicate with your Kubernetes cluster.

Step 2: Run the Command

Execute the following command:

bashRunCopy code1kubectl get nodes -o wide

Explanation of the Command

  • kubectl get nodes: This command retrieves information about the nodes in your cluster.

  • -o wide: This option provides additional details, including the internal IP addresses of the nodes.

Example Output

The output will look something like this:

1NAME                                STATUS   ROLES           AGE   VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE                         KERNEL-VERSION
2my-cluster-phontiqe-control-plane   Ready    control-plane   76m   v1.27.3   172.21.0.5    <none>        Debian GNU/Linux 11 (bullseye)   5.15.167.4-microsoft-standard-WSL2

Key Columns

  • NAME: The name of the node.

  • STATUS: The current status of the node (e.g., Ready).

  • ROLES: The roles assigned to the node (e.g., control-plane).

  • INTERNAL-IP: The internal IP address of the node, which you can use to access services exposed via NodePort.

  • EXTERNAL-IP: The external IP address of the node, if applicable.

Accessing the Node IP

Once you have the internal IP address from the output, you can use it to access your NodePort service. For example, if the internal IP is 172.21.0.5 and your NodePort is 30001, you would access your application at:

http://172.21.0.5:30001

Conclusion

Congratulations! You have successfully deployed a Node.js application on Kubernetes using Jenkins, Docker, and Kind. This setup allows for efficient CI/CD practices, enabling you to automate your deployment process seamlessly. If you have any questions or need further assistance, feel free to reach out!