Kubernetes Deployment: Deploy NodeJS application on the GKE

Niranjan Gawali
Globant
Published in
11 min readApr 10, 2023
Deploy NodeJS application on the GKE
Deploy NodeJS application on the GKE

This is the second blog in a comprehensive five-part series on Kubernetes deployments. The previous and remaining parts are outlined below.

In Part I of our Kubernetes Deployment series, we explained how to deploy the MySQL database on the GKE. In this second installment (Part II), we will discuss deploying a NodeJS application on the GKE and connecting it to the MySQL database we deployed in Part I.

Google Kubernetes Engine(GKE) is a container orchestration platform that enables developers to deploy, scale, and manage containerized applications. By deploying our NodeJS application on Kubernetes, we can take advantage of its features, such as automatic scaling, self-healing, and rolling updates.

Before proceeding with this blog, it is recommended that you have a basic understanding of Docker and Kubernetes, including concepts such as deployments, services, pods, secrets, config maps, and clusters. This understanding will help you follow along with the steps in this blog and better comprehend the reasons behind each step.

You will require a Google Cloud Platform (GCP) account to complete the steps in this blog. If you don't have a GCP account, you can create one for free using this link. At the time of writing, GCP offers a free tier account with $300 worth of free credits for 90 days. This free tier account gives you access to all the GCP services needed to follow along with the steps in this blog, including creating and managing a GKE cluster, creating a service, and deploying a NodeJS on the cluster.

It's not mandatory to deploy MySQL on Kubernetes to follow this blog. If you're using a managed database service like Google's Cloud SQL, you can still proceed with this blog.

Creating a Project, Cluster, and Namespace

In Part 1 of our Kubernetes Deployment series, we established a GCP project, a GKE cluster, and a namespace to better manage Kubernetes resources. To follow along with this tutorial, please ensure that you have completed the steps in Part 1 and proceed with the following steps:

  • Creating the GCP Project and enabling the required APIs
  • Creating the GKE cluster
  • Create a namespace for resource management
  • Create a folder for configuration management: create a book-management-backend folder to hold the NodeJS application configuration files.

Once the abovementioned steps are done, please execute the following commands in the cloud shell to connect with the cluster so we can manage the resources.

# To set the default project
gcloud config set project kubernetes-applications-demo


# To get the cluster credentials
gcloud container clusters get-credentials kubernetes-demo
--region asia-south1 --project kubernetes-applications-demo


# To set the default namespace
kubectl config set-context --current --namespace=development

NodeJS Application

For this blog, you can use any NodeJS application with the ExpressJS framework to establish a connection with the MySQL database. You can also use the provided CRUD API application with NodeJS and ExpressJS; please use this link to access it.

This is a straightforward application that establishes a connection with a MySQL database. As mentioned, you can use any MySQL database you choose, whether deployed on GKE or from any other managed database service. Please refer to the Readme file for more information about this application, including the specific endpoints created.

Setting up the MySQL Database on GCP

This step is optional for those who wish to use the managed MySQL database in conjunction with the NodeJS application. To create the MySQL database in GCP, refer to the documentation. Once the database is created, the existing root user with the password winter will be used. You can customize the credentials as per your preference, but remember to update them when creating specific resources in the cluster.

After creating the database, connect to the MySQL instance and execute the following MySQL queries to add data and structure:

    CREATE DATABASE IF NOT EXISTS`book-management-db`;
USE `book-management-db`;
DROP TABLE IF EXISTS `books`;
CREATE TABLE `books`(
`id`int NOT NULL AUTO_INCREMENT,
`title`varchar(255) NOT NULL,
`author`varchar(255) NOT NULL,
`language`varchar(255) DEFAULT NULL,
`created_at`datetime DEFAULT CURRENT_TIMESTAMP,
`created_by`varchar(255) DEFAULT NULL,
`updated_at`datetime DEFAULT CURRENT_TIMESTAMP,
`updated_by` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
);
insert into books(`title`,`author`,`language`) values('Harry Potter','J.K.Rowling','English');`

To ensure that your Kubernetes cluster can access your MySQL instance, you can use the provided command to connect to the database. The command you can use is:

mysql -h <public-ip-address-dtabase-instance> book-management-db -u root -p

# Add the password that was set while creation of instancemys

If you encounter any issues while connecting, check the Authorized Networks settings for your MySQL instance. To allow access to all networks for testing purposes, you can add 0.0.0.0/0 to the Authorized networks, although this is not recommended. For a more secure and precise approach, refer to the link provided.

Containerizing the Application with Docker

Let's understand the Docker configuration required for creating the Docker images we use in the Kubernetes deployment. This Docker file is present in the root of book-management-api API project, which I shared earlier.

FROM node:16.14.2-alpine
WORKDIR /app
ADD package*.json ./
RUN npm install
COPY . .
EXPOSE 5000 3306
CMD ["npm","run","start"]

This is a Dockerfile, a script used to create a Docker image. The contents of the Dockerfile describe how to build the Docker image. Here is a step-by-step breakdown of what the commands in this Dockerfile do:

  1. FROM node:16.14.2-alpine: This line sets the base image for the Dockerfile. In this case, the base image is node:16.14.2-alpine, a lightweight version of the Node.js runtime environment.
  2. WORKDIR /app: This line sets the working directory to /app, where the application code will be copied.
  3. ADD package*.json ./: This line copies the package.json and package-lock.json files from the host machine to the container's /app directory. The * is used as a wildcard to match any package.json files.
  4. RUN npm install: This line installs the dependencies listed in the package.json file using the npm install command. This step ensures that the dependencies are installed before copying the application code.
  5. COPY . .: This line copies the application code from the host machine to the container's /app directory.
  6. EXPOSE 5000 3306: This line exposes ports 5000 and 3306 from the container to the host machine. This is done so the application can be accessed from outside the container.
  7. CMD ["npm","run","start"]: This line sets the default command to run when the container is started. In this case, it runs the npm run start command, which starts the application. This command is executed in the container's working directory, which is set to /app.

You can use the following commands to create the image and push it to the docker hub.

docker build -t <username>/<image-name>:<tag> . # To create image


docker login --username=<username> --email=<email> # To login wiht docker
Password:


docker push <username>/<image-name>:<tag> # To push the image to the docker hub

If you want to create and test the image instance locally, please use the following command to create the instance locally.

docker run -d -p 5000:5000 --name <image-name> <username>/<image-name>:<tag>

Creating your image is not mandatory; you can use the following image of the same NodeJS application.

niranjang2/book-management-api:node-app

Kubernetes Configuration

Now let's proceed toward creating Kubernetes configuration files that will be used to create the Kubernetes resources.

We are going to create the following configurations.

  • Secret
  • ConfigMap
  • Deployment
  • Service

Secret:

A Secret is an object that contains a small amount of sensitive data, such as passwords or keys. This is the data you don't want to include pod definition or deployment files. While configuring the MySQL deployment, we need the MySQL database password, so we will use a Secret to store the password. You can refer to this link for more information about Secrets.

Below is the configuration file.

apiVersion: v1
kind: Secret
metadata:
name: mysql-credentials
labels:
name: mysql-credentials
app: book-management
namespace: development
type: Opaque
data:
root-password: d2ludGVy

In a Secret, we can store only base64 encoded values. Right now password for the database root user is winter , so we can encode it in the cloud shell using the following command.

echo ‘winter’ | base64
# which returns following response
d2ludGVy

Create the file book-mgmt-secret.yaml and use the following command to create a Secret.

kubectl create -f book-mgmt-secret.yaml

We can check the created secret using the following command, or the same can be verified using the UI.

kubectl describe secret mysql-credentials -n development
GKE Secret
GKE Secret

ConfigMap

ConfigMap is an object that stores the non-confidential data in key and value format; it is usually used to store environment variables utilized by Kubernetes configuration files. In the current configuration, we use config maps to store environment variables about MySQL database connection, node environment, and page size for the GET endpoint. For more details about the config maps, please refer to this link.

Below is the configuration file.

apiVersion: v1
kind: ConfigMap
metadata:
name: database-config
labels:
name: database-config
app: book-management
namespace: development
data:
NODE_ENV: development
MYSQL_HOST: 34.100.172.182
MYSQL_DATABASE: book-management-db
MYSQL_USER: root
PAGE_SIZE: '10'

Here the value of MYSQL_HOST will depend on what database you are using. If you have deployed the MySQL database on the GKE as per Part 1 of our Kubernetes Deployment series, the value here will be the cluster IP address exposed via service. If you use the Cloud SQL database, the value will be the Public IP address of the MySQL instance.

Create the file book-mgmt-config-map.yaml and use the following command to create a config map.

kubectl create -f book-mgmt-config-map.yaml

We can check the created config map using the following command, or the same can be verified using the UI.

kubectl describe configmap database-config -n development
GKE ConfigMap
GKE ConfigMap

Deployment

Deployment is a declarative process for defining and managing the desired state of containerized applications, including Pods and ReplicaSets, with automatic configuration management. It allows you to describe the application's life cycle, define the number of pods, and specify what images should be used to create a Pod. Deployment Controller handles the update of resources defined in the deployment file. For more details about deployment, please refer following links.

The complete deployment configuration file will be looked as below.

apiVersion: apps/v1
kind: Deployment
metadata:
name: book-mgmt-api-deploy
labels:
name: book-mgmt-api-deploy
app: book-management
namespace: development
spec:
replicas: 2
selector:
matchLabels:
name: book-mgmt-api-pod
app: book-management
template:
metadata:
name: book-mgmt-api-pod
labels:
name: book-mgmt-api-pod
app: book-management
spec:
containers:
- name: book-management-api-image
image: niranjang2/book-management-api:node-app
env:
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-credentials
key: root-password
envFrom:
- configMapRef:
name: database-config
ports:
- containerPort: 5000

As we can see in the specifications section for the template, we can see the image.

niranjang2/book-management-api:node-app

As stated earlier, you can use the provided image or create a new one.

Create the file book-mgmt-api-deploy.yaml and execute the following command to create the deployment.

kubectl create -f book-mgmt-api-deploy.yaml

Once the deployment is created, we can check its status using the following command, or it can be verified using UI.

kubectl describe deployment book-mgmt-api-deploy -n development
GKE Deployment
GKE Deployment

Service:

Service is a method for exposing deployed applications running on one or more pods in the cluster. There are three types of services available NodePort, LoadBalancer, and ClusterIP, which is also the default one, selected when no service type is defined. For our task, we will use the LoadBalancer service that will expose the REST APIs to the outside world. For more information about the service, please refer to this link.

Below is the configuration file.

apiVersion: v1
kind: Service
metadata:
name: book-mgmt-api-service
labels:
name: book-mgmt-api-service
app: book-management
namespace: development
spec:
type: LoadBalancer
selector:
name: book-mgmt-api-pod
app: book-management
ports:
- name: http
port: 5000
targetPort: 5000

Create the file book-mgmt-api-service.yamland use the following command to create the service file.

kubectl create -f book-mgmt-api-service.yaml.yaml

Created service can be verified using the following command, or the same can be verified using the UI.

kubectl describe service book-mgmt-api-service -n development
GKE Sevice
GKE Sevice

Once the service is created, we can access the REST APIs using the exposed external endpoint. For example, consider below GET endpoint call.

curl 34.93.238.8:5000/book?pageNo=1 | json_pp

# Returns the following response

% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 254 100 254 0 0 664 0 --:--:-- --:--:-- --:--:-- 664
{
"data" : [
{
"author" : "J.K.Rowling",
"created_at" : "2023-03-11T08:12:59.000Z",
"created_by" : null,
"id" : 1,
"language" : "English",
"title" : "Harray Potter",
"updated_at" : "2023-03-11T08:12:59.000Z",
"updated_by" : null
}
],
"message" : "Success",
"status" : true,
"statusCode" : 200
}

Similarly, we can also call other endpoints; please refer to this link for more information about API calls. You can also simply use the Postman JSON file here.

Removing GKE resources

Suppose you achieve your goal and want to delete resources to reduce the billing. Use the following commands to remove the resources.

#To delete deployments,services,pods,replicasets
kubectl delete all --all -n development
#To delete the configmap database-config
kubectl delete configmap/database-config -n development

#To delete the secret mysql-credentials
kubectl delete secret/mysql-credentials -n development

Conclusion

We have successfully deployed the NodeJS application on the GKE cluster. You can access the source code repository by following this GitHub URL, where you will also find all the necessary Kubernetes configuration files in the designated kubernetes folder.

By deploying the NodeJS application on the GKE cluster, we can benefit from the scalability and flexibility that Kubernetes offers. This allows us to easily manage and scale the application as needed, ensuring reliable and efficient performance. With the Kubernetes configuration files included in the repository, deploying and managing the application on any Kubernetes cluster is straightforward.

The following blog will focus on using GitHub Actions to automate the deployment of a NodeJS application to the Google Kubernetes Engine (GKE). If you have any comments or queries about this, please mention them in the comments section below.

--

--