In this article, we are going to deploy a simple rest API with ExpressJS and expose it using a service and ingress. After reading the article you would have learned how to deploy your personal projects on Kubernetes and understood the YAML configuration object for deployments, services and ingresses. You would have also learned how to Dockerize/Containerize a NodeJS application using Docker.
If you are new to coding and are thinking ‘what is Kubernetes?,’ here is a list of prerequisites before we get started.
To set up the application in this article, we will follow these steps:
In this step, we will containerize our application using docker.
In the project directory, create a file with the name Dockerfile. In the Dockerfile, add this code:
FROM node:15-alpine
WORKDIR /usr/app
COPY . .
RUN yarn install
EXPOSE 3000
ENTRYPOINT ["node", "index.js"]
In the Dockerfile, we specify a base image to use for our container and a build context, copy our application files into the image, install npm modules, set 3000 as the port to listen to when connecting to our container, and then specify then start command for the container.
Create a file with the name .dockerignore in the root directory and add a single line of text to it—node_modules—to prevent it from being added to the image during the build.
Build the docker image for the application on your terminal with the following command: docker build -t simple-express-app.
Run the docker images command to list the images you have.
Run the docker image—docker run -d -p 4100:3000
We then publish a port for the container to be accessed on the host machine.
Check that the container is running.
Now let us test our running application. Instead of using 3000 as our port, we will use 4100.
In this stage, we will push the image we have built to the docker repository so that we can access it from our deployment.
On your terminal, log in to docker hub docker login -u username -p password. You will get login succeeded as a response.
Run the command docker tag simple-express-app davidshare/simple-express-app.
Run the command docker push davidshare/simple-express-app where simple-express-app is the name of the docker image we created.
When we check dockerhub, we see that our image has been pushed.
In this step, we will deploy our application to a Kubernetes cluster.
For this tutorial, I am going to use Katakoda They offer you a playground to try out Kubernetes or run tests. You will need to login to access the full features that it offers.
The picture below is of a YAML definition for a deployment in Kubernetes.
apiVersion: apps/v1
kind: Deployment
metadata:
name: simple-express-app
labels:
apps: simple-express-app
spec:
selector:
matchLabels:
app: simple-express-app
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
replicas: 3
template:
metadata:
labels:
app: simple-express-app
spec:
containers:
- name: simple-express-app
image: davidshare/simple-express-app
ports:
- containerPort: 3000
The code for this article is in the repository you cloned before.
To create the deployment we will use the kubectl command and a link to the deployment file in the repository.
In Kubernetes, services are used to enable network access to pods. Each pod managed by the deployment has its own unique IP address and name. This IP address can be used to communicate with the pod within the cluster but cannot be accessed externally.
Each time a deployment is updated or a pod is deleted, new ones are created to replace the old. If you are connecting directly to the pods, it means that you have to find a way to keep track of the changing IP addresses. This is why we need services. Services provide a point of access to a group of pods. The service keeps track of these pods using labels. Kubernetes assigns cluster IPs to these services, so all the pods mapped to the service can be accessed using that IP.
apiVersion: v1
kind: Service
metadata:
name: simple-express-app
spec:
type: NodePort
ports:
- port: 80
targetPort: 3000
protocol: TCP
selector:
app: simple-express-app
There are different kinds of services. The most popular are ClusterIP, which is the default, the NodePort and the Loadbalancer.
controlplane $ kubectl apply -f https://raw.githubusercontent.com/davidshare/simple-express-app/master/kubernetes/service.yaml
service/simple-express-app created
An ingress is used to allow access to the Kubernetes service from outside the cluster. It uses rules that determine where traffic will be directed to. An ingress needs an ingress controller for it to function properly.
kind: Ingress
apiVersion: extensions/vlbetal
metadata:
name:simple-express-app
annotations:
kubernetes.io/ingress.class: 'nginx'
spec:
rules:
- host: simple-express-app.com
http:
paths:
- path: /
backend:
serviceName: simple-express-app
servicePort: 80
The code above is a YAML description of an ingress with a rule to direct network traffic coming to the root path of a url to the simple-express-app service. We can specify as many rules as we want. In the screenshot below, we specify two rules, and direct traffic coming to the /app2 path to the simple-express-app2 service.
kind: Ingress
apiVersion: extensions/vlbetal
metadata:
name: simple-express-app
annotations:
kubernetes.io/ingress.class: 'nginx'
spec:
rules:
- host: simple-express-app.com
http:
paths:
- path: /
backend:
serviceName: simple-express-app
servicePort: 80
- path: /app2
backend:
serviceName: simple-express-app2
servicePort: 80
As a way of practising what you have learned here, you can:
In this article, we learned how to build a simple express application, containerize it and deploy it to a Kubernetes cluster. I hope this was helpful and I look forward to hearing exciting stories of how you applied what you learned here.
Cover Photo by Christopher Gower on Unsplash