Monitoring with Prometheus, Simple Counter Example with Deno

Prometheus ↱ is a monitoring and alerting toolkit. The post Monitoring with Prometheus gives a brief introduction to the Prometheus ecosystem.

ts_prometheus ↱ is a third party native TypeScript implementation of a Prometheus compatible client library. See Writing client libraries ↱ how to create Prometheus compatible client libraries.

Docker Setup

For this example the Prometheus application runs in a Docker container. The example service is also running in a Docker container. Both containers are connected through a virtual network. The prometheus.yml configuration file is mounted into the Prometheus container.

Diagram Docker Virtual Network

Create the Docker Network

Create a Docker network for the two containers.

$ docker network create 00001-monitoring

A Example Service

This example service counts all requests and offers the metrics as a counter with the name ‘basic_monitoring_http_requests_total’ for Prometheus.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import { Application, Router } from "https://deno.land/x/oak/mod.ts";
import { Counter, Registry } from "https://deno.land/x/ts_prometheus/mod.ts";

const counter = Counter.with({
  name: "basic_monitoring_http_requests_total",
  help: "The total number of HTTP requests.",
  labels: ["method", "status", "path"],
});

const app = new Application();

const router = new Router();
router
  .get("/hello", (ctx) => {
    ctx.response.body = "Hello world!";
  })
  .get("/hello/:name", (ctx) => {
    const { params } = ctx;
    ctx.response.body = `Hello, ${params.name}!`;
  })
  .get("/metrics", (ctx) => {
    ctx.response.headers.set("Content-Type", "text/plain; charset=utf-8"); // to avoid guessing
    ctx.response.body = Registry.default.metrics();
  });

app.use(async (ctx, next) => {
  await next();
  counter.labels({
    path: ctx.request.url.pathname,
    method: ctx.request.method as string,
    status: "" + ctx.response.status,
  }).inc();
});

app.use(router.routes());
await app.listen({ port: 8080 });

Line 2
Import the Counter and the Registry from ts_prometheus ↱ library.

Lines 4 - 8
Define a Counter with the name ‘basic_monitoring_http_requests_total’, add a ‘HELP’ information with a short description about the counter. Add labels for the request method (‘method’), request status (‘status’) and the request path (‘path’).

Lines 21 - 24
Return the collected metrics at the standard path ‘/metrics’ as plain text.

Lines 26 - 33
Increment the counter on every request and add/set the values for the labels to add more detailed information about the request -status and -path.

Start the Sample Service Container

Compile the sample application to run on a Linux host.

$ cd examples/Deno/release/00001-monitoring/monitoring-simple-counter
$ deno compile --target x86_64-unknown-linux-gnu --allow-env --allow-net app.ts

Build a Docker image with the sample service.

$ docker build -t monitoring-simple-counter -f ./deploy/Dockerfile .

Start the container with the sample service. To make the sample service accessible from the Prometheus container, name the container ‘monitoring-simple-counter’ and add the container to the ‘00001-monitoring’ Docker network.

$ docker run -d -p 2000:2000 \
  --name monitoring-simple-counter \
  --network 00001-monitoring \
  monitoring-simple-counter

Prometheus Configuration

Add a new Job to the ‘prometheus.yml’

The sample prometheus.yml monitors the Prometheus application in its own Docker container and the sample service in the docker container monitoring-simple-counter.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
global:
  scrape_interval:     15s # By default, scrape targets every 15 seconds.

  # Attach these labels to any time series or alerts when communicating with
  # external systems (federation, remote storage, Alertmanager).
  external_labels:
    monitor: 'codelab-monitor'

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: 'prometheus'

    # Override the global default and scrape targets from this job every 5 seconds.
    scrape_interval: 5s

    static_configs:
      - targets: ['localhost:9090']

  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: 'basic_monitoring'

    # Override the global default and scrape targets from this job every 5 seconds.
    scrape_interval: 5s

    # FIXME, change IP/hostname here,
    static_configs:
      - targets: [ 'monitoring-simple-counter:2000' ]

Line 1 - 7
The global part defines defaults and other setting. In this example we define a default scrape_interval of 15 seconds for all jobs.

Line 11
In the scrape_configs jobs can be defined.

Line 13 - 19
The first job monitors Prometheus itself every 5 Seconds.

Line 22 - 29
The lines 22 to 29 define the job to pull the metrics from the sample webservice. Here we name the job basic_monitoring, line 22. The Prometheus server should scrape / pull (scrape_interval, line 25) every 5 seconds for monitoring metrics data. The node static_configs contains a array targets with hostnames/ ip addresses and port of the example service. Prometheus pulls then from ‘http://<hostnames:port>/metrics’ the metrics data. The service to monitor runs in a Docker container named ‘monitoring-simple-counter’.

Microservices in a productive environment

This is an example only. In many productive environments the monitoring information are served to another TCP/IP port for security reasons.

Start a Prometheus Container

Start a Prometheus container and mount the prometheus.yml configuration file into the container. Set the container name to ‘prometheus’ and add the container to the already created Docker network ‘00001-monitoring’.

$ cd examples/Deno/release/00001-monitoring/monitoring-simple-counter
$ docker run -d -p 9090:9090 \
    -v $PWD/prometheus.yml:/etc/prometheus/prometheus.yml \
    --name prometheus \
    --network 00001-monitoring \
    prom/prometheus

Check the logs if the startup was successful.

$ docker logs -f prometheus

Access the Prometheus UI.

The Prometheus container should now up and running. To access the Prometheus UI open the URL in a browser: http://localhost:9090.

Start the Sample Service

Compile the sample application.

$ cd examples/Deno/release/00001-monitoring/monitoring-simple-counter
$ deno compile --target x86_64-unknown-linux-gnu --allow-env --allow-net app.ts

Build a image with the sample application.

$ docker build -t monitoring-simple-counter -f ./deploy/Dockerfile .

Start the container with the sample application.

$ docker run -d -p 2000:2000 --name monitoring-simple-counter --network 00001-monitoring monitoring-simple-counter

Make some Requests on the Sample Service

Make some calls to the sample service to increase the counter.

http://localhost:2000/hello
http://localhost:2000/hello/Nagos

Check if the sample service returns the metrics data (this increases the counter also)
http://localhost:2000/metrics/

Check the collected metrics in the Prometheus UI

Open http://localhost:9090 and in the metrics explorer tab (main “Prometheus’ tab), search for basic_monitoring_http_requests_total. The ‘Table’ tab displays the collected metrics. The ‘Graph’ tab show a simple line graph for the collected metrics.

Stop and Remove all Container and remove the Network Configuration

Stop the sample service.

$ docker stop monitoring-simple-counter
$ docker rm monitoring-simple-counter

Stop the Prometheus container.

$ docker stop prometheus
$ docker rm prometheus

Remove the Docker network.

$ docker network rm 00001-monitoring

Requires

Tool Version See
deno > 1.17.2 Tools
docker > 19.03.0 Tools
Prometheus, (docker image) > 2.15.1, (prom/prometheus:v2.15.0) Prometheus, Docker Image ↱

See also