top of page
Search

Node.js REST API with Docker, Kubernetes, Prometheus, and Grafana

  • anithamca
  • Feb 28, 2024
  • 6 min read

Updated: Feb 29, 2024



This project demonstrates how to build and deploy a Node.js-based RESTful API using Docker and Kubernetes. Additionally, it showcases how to monitor and alert using Prometheus and Grafana.

Folder Name : project-2

Solution Approach / Pre-requisite

 

1.Install Node.js and npm

2.Docker & Kubernetes cluster (Used : Docker Desktop)

3. Start node js project

4.Install dependencies, create configuration file and bring up node js app

5.Build and push to docker repo

6 .Install Helm & Deploy node app image to Kubernetes cluster using helm chart

7. Prometheus and Grafana set up in local Kubernetes cluster for monitoring.

 

Solution Approach Steps with screenshot below

 

1.     Installed nodejs and npm on Mac OS

Anithas-MacBook-Pro:~ anithar$ node -v

v20.8.0

Anithas-MacBook-Pro:~ anithar$ npm -v

10.1.0


2.   Docker  - Docker for Desktop with default k8s cluster

 


3. Start the nodejs project

   

Anithas-MacBook-Pro:DevOps-Upgrad anithar$ mkdir project-2

Anithas-MacBook-Pro:DevOps-Upgrad anithar$ cd project-2

Anithas-MacBook-Pro:project-2 anithar$ npm init -y

Wrote to /Users/anithar/Documents/Learning/project-2/package.json:

 

{

  "name": "project-2",

  "version": "1.0.0",

  "description": "",

  "main": "index.js",

  "scripts": {

    "test": "echo \"Error: no test specified\" && exit 1"

  },

  "keywords": [],

  "author": "",

  "license": "ISC"

}

 

This command will initialize a node.js project in your directory and will have package.json file.

 

4.  Install dependencies, create configuration file and bring up node js app

 

 Install Dependencies

 

For this project we need to install dependencies like express.js and prom-client. To install the dependencies run the following command:


Anithas-MacBook-Pro:project-2 anithar$ npm i express prom-client

added 68 packages, and audited 69 packages in 2s

12 packages are looking for funding

  run `npm fund` for details

found 0 vulnerabilities

npm notice

npm notice New minor version of npm available! 10.1.0 -> 10.4.0

npm notice Run npm install -g npm@10.4.0 to update!

npm notice

 

Create Configuration File and Run the node js app

 

We need a configuration file and we need to create index.js file and write the configurations and Run the node index.js to test the application whether it is working properly or not.

 

Index.js file content

 

const express = require('express')

const app = express()

const client = require('prom-client')

const port = 3000

 

const register = new client.Registry();

 

client.collectDefaultMetrics({

    app: 'node-application-monitoring-app',

    prefix: 'node_',

    timeout: 10000,

    gcDurationBuckets: [0.001, 0.01, 0.1, 1, 2, 5],

    register

});

 

// Dummy users

var users = [

    { id: 0, name: 'ani', email: 'anitha.mca@gmail.com, role: 'member' }

    , { id: 1, name: ani2, email: 'anitha.devops.aws@gmail.com', role: 'member' }

    , { id: 2, name: anii, email: anitha.mca+github@gmail.com', role: 'admin' }

];

 

function loadUser(req, res, next) {

    // You would fetch your user from the db

    var user = users[req.params.id];

    if (user) {

      req.user = user;

      next();

    } else {

      next(new Error('Failed to load user ' + req.params.id));

    }

}

 

function andRestrictToSelf(req, res, next) {

    // If our authenticated user is the user we are viewing

    // then everything is fine :)

      next();

    } else {

      // You may want to implement specific exceptions

      // such as UnauthorizedError or similar so that you

      // can handle these can be special-cased in an error handler

      // (view ./examples/pages for this)

      next(new Error('Unauthorized'));

    }

}

 

function andRestrictTo(role) {

    return function(req, res, next) {

      if (req.authenticatedUser.role === role) {

        next();

      } else {

        next(new Error('Unauthorized'));

      }

    }

}

 

  // Middleware for faux authentication

  // you would of course implement something real,

  // but this illustrates how an authenticated user

  // may interact with middleware

 

app.use(function(req, res, next){

    req.authenticatedUser = users[0];

    next();

});

 

app.get('/', function(req, res){

    res.redirect('/user/0');

});

 

app.get('/user/:id', loadUser, function(req, res){

    res.send('Viewing user ' + req.user.name);

});

 

app.get('/user/:id/edit', loadUser, andRestrictToSelf, function(req, res){

    res.send('Editing user ' + req.user.name);

});

 

app.delete('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){

    res.send('Deleted user ' + req.user.name);

});

 

app.get('/metrics', async (req, res) => {

    res.setHeader('Content-Type', register.contentType);

    res.send(await register.metrics());

});

 

app.listen(port, () => {

  console.log(`Example app listening on port ${port}`)

})

 

Anithas-MacBook-Pro:project-2 anithar$ ls

index.js    package-lock.json

node_modules    package.json

 

Anithas-MacBook-Pro:project-2 anithar$  node index.js

Example app listening on port 3000

 

Validate node js app in browser URL : localhost:3000/metrics

 


4.Build and push the app to dockerhub

 

Create Dockerfile with below content

 

Anithas-MacBook-Pro:project-2 anithar$ vi Dockerfile

 

# Use an official Node.js runtime as the base image

FROM node:14

 

# Set the working directory in the container

WORKDIR /app

 

# Copy package.json and package-lock.json to the container

COPY package*.json ./

 

# Install Node.js dependencies

RUN npm install

 

# Copy the rest of your application code to the container

COPY . .

 

# Expose the port your application is running on

EXPOSE 3000

 

# Define the command to start your Node.js application

CMD [ "node", "app.js" ]

 

Anithas-MacBook-Pro:project-2 anithar$ ls

Dockerfile    node_modules    package.json

index.js    package-lock.json

 

Docker Build

 

Anithas-MacBook-Pro:project-2 anithar$ docker build -t anitharajam/crudapp .

[+] Building 5.9s (11/11) FINISHED                                                                                                                         

 => [internal] load build definition from Dockerfile                                                                                                   0.0s

 => => transferring dockerfile: 509B                                                                                                                   0.0s

 => [internal] load .dockerignore                                                                                                                      0.0s

 => => transferring context: 2B                                                                                                                        0.0s

 => [internal] load metadata for docker.io/library/node:14                                                                                             2.6s

 => [auth] library/node:pull token for registry-1.docker.io                                                                                            0.0s

 => [internal] load build context                                                                                                                      0.3s

 => => transferring context: 5.99MB                                                                                                                    0.3s

 => [1/5] FROM docker.io/library/node:14@sha256:a158d3b9b4e3fa813fa6c8c590b8f0a860e015ad4e59bbce5744d2f6fd8461aa                                       0.0s

 => CACHED [2/5] WORKDIR /app                                                                                                                          0.0s

 => [3/5] COPY package*.json ./                                                                                                                        0.0s

 => [4/5] RUN npm install                                                                                                                              2.6s

 => [5/5] COPY . .                                                                                                                                     0.1s

 => exporting to image                                                                                                                                 0.1s

 => => exporting layers                                                                                                                                0.1s

 => => writing image sha256:6a453406d1aff11bbab6cb2032eca5f3ae311b5ac7cc1d2d86651dc06ff8d767                                                           0.0s

 => => naming to docker.io/anitharajam/crudapp                                                                                                          0.0s

                                                                                                                                                           

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them


Docker Push

 

Anithas-MacBook-Pro:project-2 anithar$ docker push anitharajam/crudapp

Using default tag: latest

The push refers to repository [docker.io/anitharajam/crudapp]

1b987aeac8b8: Pushed

133b55b3cd33: Pushed

08dff22c9653: Pushed

89ec37e03735: Mounted from anitharajam/noter-app

4d772e48367e: Mounted from anitharajam/noter-app

b078da4dfde3: Mounted from anitharajam/noter-app

20649f23472c: Mounted from anitharajam/noter-app

476197c298e4: Mounted from anitharajam/noter-app

708291ce0534: Mounted from anitharajam/noter-app

82da4502ad28: Mounted from anitharajam/noter-app

f3e76aaba7cc: Mounted from anitharajam/noter-app

cfec405a23bc: Mounted from anitharajam/noter-app

173621e7addd: Mounted from anitharajam/noter-app

latest: digest: sha256:2d2811de18d99212536d6bc123e2f8cd63ec9cbdf1d8713fcd5c1fa88fc6a3cd size: 3052

 

5.Install helm

 

Anithas-MacBook-Pro:project-2 anithar$ brew install helm

Running `brew update --auto-update`...

==> Auto-updated Homebrew!

Updated 4 taps (hashicorp/tap, homebrew/services, homebrew/core and homebrew/cask).

==> New Formulae

bluez                                  gimmecert                              lsusb-laniksj                          pass-import

==> New Casks

apidog-europe                  deelay                         motion                         segger-ozone                   youlean-loudness-meter

 

You have 40 outdated formulae and 1 outdated cask installed.

 

Warning: Cask helm was renamed to homebrew/core/helm.

##################################################################################################################################################### 100.0%

==> Fetching helm

##################################################################################################################################################### 100.0%

==> Pouring helm--3.14.2.arm64_ventura.bottle.tar.gz

Warning: Cask helm was renamed to homebrew/core/helm.

==> Caveats

Bash completion has been installed to:

  /opt/homebrew/etc/bash_completion.d

==> Summary

🍺  /opt/homebrew/Cellar/helm/3.14.2: 65 files, 48.9MB

==> Running `brew cleanup helm`...

Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.

Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).

 

6.Deploy node app image to Kubernetes cluster as helm chart

 

Manifest file for the application had written and placed under crud/template directory



Anithas-MacBook-Pro:project-2 anithar$ export KUBECONFIG=/Users/anithar/.kube/config


Create helm chart


Anithas-MacBook-Pro:project-2 anithar$ helm install crudapp crud

NAME: crudapp

LAST DEPLOYED: Wed Feb 28 14:29:40 2024

NAMESPACE: default

STATUS: deployed

REVISION: 1

NOTES:

1. Get the application URL by running these commands:

  export NODE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services crudapp)

  export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}")

  echo http://$NODE_IP:$NODE_PORT


This will deploy the application on kubernetes cluster.


7. Prometheus and Grafana set up in local Kubernetes cluster for monitoring.

 

Anithas-MacBook-Pro:project-2 anithar$ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts

"prometheus-community" has been added to your repositories

 

Anithas-MacBook-Pro:project-2 anithar$ helm repo update

Hang tight while we grab the latest from your chart repositories...

...Successfully got an update from the "prometheus-community" chart repository

Update Complete. ⎈Happy Helming!⎈

 

Anithas-MacBook-Pro:project-2 anithar$ helm install my-kube-prometheus-stack prometheus-community/kube-prometheus-stack --version 51.4.0


NAME: my-kube-prometheus-stack

LAST DEPLOYED: Wed Feb 28 14:33:24 2024

NAMESPACE: default

STATUS: deployed

REVISION: 1

NOTES:

kube-prometheus-stack has been installed. Check its status by running:

  kubectl --namespace default get pods -l "release=my-kube-prometheus-stack"


Visit https://github.com/prometheus-operator/kube-prometheus for instructions on how to create & configure Alertmanager and Prometheus instances using the Operator.


We are using kube-prometheus-stack because it comes with prometheus all the dependencies which we need to install.


Now edit the service in using kubectl edit svc command and expose the grafana service.

Login to Grafana and go to Home > Dashboards > Kubernetes/ComputeResources/Pod and select the pods which we have deployed it looks like this:





 
 
 

Comments


Post: Blog2_Post

Subscribe Form

Thanks for submitting!

©2021 by anitharajamuthutechno. Proudly created with Wix.com

bottom of page