Monday, 4 May 2020

Deploy a Node.js app on Kubernetes

In this guide we'll see how to containerize a simple Node.js app and deploy it on Kubernetes.

Create a Node.js project.
Suppose we have this project tree:
File:Node.js logo.svg - Wikimedia Commons
simple-nodejs-app
├── Dockerfile
├── index.js
└── package.json

Let's review the content of all the files.

package.json

{
  "name": "nodejs-app",
  "version": "1.0.0",
  "description": "A very very simple node.js app",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
  }
}


index.js

const http = require('http');
const port = process.env.PORT || 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.end('Hello Node!\n');
});

server.listen(port, () => {
  console.log(`Server running on http://localhost:${port}/`);
});



Dockerfile

FROM node:alpine
ADD . /
USER node
EXPOSE 3000
CMD npm run start


As you can see, we use as source image the image of Node.js with tag alpine, in order to build a small image.
We add to the image all the files in the current directory, and use the (prebuilt) user node instead of root.
We expose the port 3000 to reach the application from outside the container, and specify in the CMD instruction how to launch the app.

In this case we don't have particular npm packages to install, so we can skip the npm i command.

Now we have all ready. Let's go on.

Build Docker image and push it to Kubernetes registry.

cd simple-nodejs-app

docker build /custom_nodejs:1.0.0 .
...
docker push /custom_nodejs:1.0.0



Create Kubernetes resources.

Kubernetes organizes entities on the cluster as objects called resources.

The main resources to create for our app are:

Pod.
A pod is a set of one ore more containers that share a unique ip address and persistent storage.


Deployment.
A Deployment provides declarative updates for Pods, that describe the desired state.

deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nodejs-deployment
  labels:
    app: nodejs
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nodejs
  template:
    metadata:
      labels:
        app: nodejs
    spec:
      containers:
      - name: node
        image: node:13
        ports:
        - containerPort: 3000


Service.
A service is the representation of a set of pods. It consists of an IP address and a DNS name, and can be exposed outside the cluster through an ingress. A service can easily be accessed from other pods of the same namespace thanks to the environment variable <SERVICE>_HOST injected automatically inside them.

service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nodejs-service
spec:
  selector:
    app: nodejs
  ports:
    - protocol: TCP
      port: 80
      targetPort: 3000

Ingress.
Ingress exposes HTTP and HTTPS routes from outside the cluster to services within the cluster.An Ingress may be configured to give Services externally-reachable URLs, load balance traffic, terminate SSL / TLS, and offer name based virtual hosting. An ingress controller is needed for your ingress to work.

ingress.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: nodejs-ingress
spec:
  backend:
    serviceName: nodejs-service
    servicePort: 80

Now that we have created the definitions for all the resources we need, we can create them combining them in a single file gke.yaml and using the command:

kubectl apply -f gke.yaml

Or creating them separately:

kubectl apply -f deployment.yaml service.yaml ingress.yaml

Depending on the cloud-provider or the infrastructure hosting your cluster, you may have to create a DNS entry pointing to the ingress ip, to reach the application.

To get the ingress ip:
kubectl describe ingress nodejs-ingress

Supposing simplenodejs.app.example.com is your DNS entry, this is the result:



That's all!



No comments:

Post a Comment