WittCode💻

Connect a Node App to Prometheus

By

Learn how to connect a Node application to Prometheus using Docker Compose. We will go over the Node prom-client library, how to set up a metrics endpoint, etc.

Table of Contents 📖

Prometheus

Prometheus is desiged to capture metrics by scraping an HTTP endpoint, known as a metrics endpoint. Metrics endpoints are the standard way for an application to expose metrics. Prometheus will periodically scrape a provided HTTP endpoint and store the data. We can then query the data to get an idea of what is going on in the application.

Environment Variables

PROJECT_NAME=PROMETHEUS_DEMO

NODE_CONTAINER_NAME=node-c
NODE_PORT=9001

PROMETHEUS_CONTAINER_NAME=prometheus-c
PROMETHEUS_PORT=9090

SUCCESS: Port 9090 is Prometheus's default port.

Node Project

npm init es6 -y
npm i prom-client
npm i nodemon -D
"main": "./src/server.js",
...
"scripts": {
  "start": "nodemon ."
},

INFO: The prom-client package is a Prometheus client for Node. We will use it to connect to our Prometheus server.

import http from 'http';
import url from 'url';
import client from 'prom-client';

const NODE_CONTAINER_NAME = process.env.NODE_CONTAINER_NAME;
const NODE_PORT = process.env.NODE_PORT;

// The registry where the metrics will be registered.
// A registry is a list of metrics that the Prometheus client is collecting.
const register = new client.Registry();
// Adds a static label to every metric emitted by the registry
register.setDefaultLabels({
  app: 'my-node-app'
});
// There are some default metrics recommended by Prometheus. They can
// be collected by calling collectDefaultMetrics()
client.collectDefaultMetrics({register});

const server = http.createServer(async (req, res) => {
  const route = url.parse(req.url).pathname;
  console.log(`${req.method} ${route}`);

  if (route === '/metrics') {
    res.setHeader('Content-Type', register.contentType)
    // To expose the metrics, respond to Prometheus's scrape requests
    // with the result of register.metrics()
    const metrics = await register.metrics()
    res.end(metrics)
  }
});

server.listen(NODE_PORT, NODE_CONTAINER_NAME, () => {
  console.log(`Server running at http://${NODE_CONTAINER_NAME}:${NODE_PORT}/`);
});

Prometheus Configuration

global:
  scrape_interval: 5s
scrape_configs:
 - job_name: my-node-app
   static_configs:
    - targets:
       - node-c:9001
  • global - Specifies the global configuration. Supplied parameters are valid in all configuration contexts.
  • scrape_interval - How frequently to scrape the targets. Default is 1 minute.
  • scrape_configs - List of scrape configurations.
  • job_name - The job name assigned to the scraped metrics.
  • static_configs - Used to specify a list of targets.
  • targets - List of targets to scrape. Takes the host:port format. Note that this is the name of our Node Docker container in the Docker network.

Dockerizing

FROM prom/prometheus
COPY --chmod=755 entrypoint.sh .
ENTRYPOINT ./entrypoint.sh
/bin/prometheus --config.file=/etc/prometheus/prometheus.yml --storage.tsdb.path=/prometheus --web.console.libraries=/usr/share/prometheus/console_libraries --web.console.templates=/usr/share/prometheus/consoles

WARNING: The above entrypoint.sh script is actually the default entrypoint.sh script for the Prometheus image. We are explicitly defining it here so we can see how to swap out configuration files for different environments i.e. production and development.

FROM node:22-alpine
WORKDIR /server
COPY package*.json .
RUN npm i
CMD ["npm", "start"]

Docker Compose Services

name: ${PROJECT_NAME}

services:

  server:
    pull_policy: build
    image: node-i
    container_name: ${NODE_CONTAINER_NAME}
    env_file: .env
    build:
      context: server
      dockerfile: Dockerfile
    volumes:
      - ./server:/server
      - server-node-modules:/server/node_modules
    ports:
      - ${NODE_PORT}:${NODE_PORT}

  prometheus:
    pull_policy: build
    image: prometheus-i
    container_name: ${PROMETHEUS_CONTAINER_NAME}
    env_file: .env
    build:
      context: prometheus
      dockerfile: Dockerfile
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - ${PROMETHEUS_PORT}:${PROMETHEUS_PORT}

volumes:
  server-node-modules:
    name: server-node-modules

Starting the Application

docker compose up

After running the application, visit localhost:9090 to see the Prometheus dashboard. We can then query for metrics and get an idea of what is going on in the application.