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.
Git Project Folder Path >> https://github.com/AnithaRajamuthu/AniWebsiteContents.git
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 Changelog: https://github.com/npm/cli/releases/tag/v10.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 :)
if (req.authenticatedUser.id === req.user.id) {
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.
==> Downloading https://ghcr.io/v2/homebrew/core/helm/manifests/3.14.2
##################################################################################################################################################### 100.0%
==> Fetching helm
==> Downloading https://ghcr.io/v2/homebrew/core/helm/blobs/sha256:4874bbf7bc5edb5f7f55b4a180861f420bc659255201f7aa92a1b3af0dd2853e
##################################################################################################################################################### 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