Para tener un poco más de contexto vamos a crear una API muy sencilla con expressJS, la cual va a usar el agente swagger stats para recolectar las métricas, como plataforma de monitoreo vamos a usar prometheus y como plataforma de visualización y análisis vamos a usar grafana.

Sistema de monitoreo

Repositorio

Los sistemas de monitoreos son todos aquellos que nos ayudan a recolectar diferentes métricas de nuestro sistema, procesos y/o aplicaciones, generalmente estos datos recolectados son de tipo llave:valor acompañados de un timestamp y son guardados en una base de datos de tipo TSDB (time Series Data Base), las cuales están optimizadas para trabajar con grandes cantidades de datos en formato plano. El objetivo principal de un sistema de monitoreo es tener la capacidad de tener información casi en tiempo real de nuestro sistema como por ejemplo la carga del procesador, el numero de peticiones, cantidad de errores y poder hacer todas estas consultas de manera eficiente para asi poder crear gráficas que nos ayuden a entender patrones de comportamiento o a buscar errores en nuestra aplicación y actuar lo más rápido posible.

Los sistemas de monitoreo pueden hacer un llamado a un api donde se expongan todas las métricas y cada cierto tiempo requerirlo para recolectar todos los datos o puede esperar que un cliente envíe los datos.

Infraestructura

Cuando queremos implementar un sistema de monitoreo por lo general lo podemos dividir en tres grandes componentes:

  • El agente que se encarga de la recolección de las métricas de nuestro sistema o aplicacion, por lo general es un plugin o paquete (collectd, statsd, prometheus exporter, telegraf, swagger stats).
  • La plataforma de monitoreo en donde se va a almacenar toda la información recolectada y la cual nos va a permitir hacer consultas sobre estos datos (graphite, influxDB, prometheus, dataDog, newRelic). Muchos sistemas de monitoreo también proveen módulos para visualización y generación de alertas.
  • Un sistema de visualización y análisis de los datos, con este tipo de sistema vamos a poder crear gráficas y en algunos casos proveen sistemas de inteligencia artificial para ayudarnos a detectar patrones, por lo general consumen los datos de nuestra plataforma de monitoreo (grafana, splunk, kibana, tableu).

| API/APP | <--pull/push-->| TSBD | <--pull--| GRAPH |

Implementación

Para tener un poco más de contexto vamos a crear una API muy sencilla con expressJS, la cual va a usar el agente swagger stats para recolectar las métricas, como plataforma de monitoreo vamos a usar prometheus y como plataforma de visualización y análisis vamos a usar grafana.

El primer paso es crear una aplicación en este caso una API con un único endpoint hecha con expressjs y donde vamos a utilizar el paquete swagger-stats como middleware, este paquete se encarga de recolectar todas las métricas y exponerlas como un endpoint en la ruta /swagger-stats/metrics, el cual va ser consumido por el sistema de monitoreo. Lo interesante de este paquete es que nos permite interactuar con una UI y observar todas nuestras estadísticas de manera gráfica localmente (/swagger-stats/ui).

API

Con swagger también podemos hacer la documentación de nuestro API mediante un archivo .json o yml, este archivo lo podemos pasar a nuestro middleware y nos va permitir filtrar por cada endpoint y así concentrarnos en la información que estamos buscando, sin embargo no tenemos que utilizarlo si no lo deseamos esto significa no pasar opciones al middleware (swaggerSpec:apiSpec).

const swStats = require('swagger-stats');
// Load your swagger specification 
const apiSpec = require('./swagger.json');
// Enable swagger-stats middleware in express app, passing swagger specification as option 
app.use(swStats.getMiddleware({swaggerSpec:apiSpec}));
// index.js

const express = require("express");
const app = express();
const bodyParser = require('body-parser');
const swStats = require('swagger-stats');
app.use(swStats.getMiddleware());
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())

app.get("/user", (req, res) => {
  res.status(200).send({ message: 'Get all'});
});

app.listen(4000, () => {
  console.log("Server is running on port 4000");
});

// http://localhost:4000/users
// http://localhost:4000/swagger-stats/ui
// http://localhost:4000/swagger-stats/stats
// http://localhost:4000/swagger-stats/metrics

Prometheus

Ahora que ya tenemos nuestra API y existe un endpoint donde podemos encontrar nuestras métricas, debemos configurar nuestro sistema de monitoreo, por lo general los sistemas de monitoreo son aplicaciones, librerías o servicios, los cuales debemos instalar en nuestro sistema operativo o usarlos en la nube, sin embargo con docker podemos hacerlo de una manera muy sencilla que nos ayuda a entender lo que estamos haciendo y a tener un mayor control.

// docker-compose.yml

version: '3'
services:
  prometheus:
    image: prom/prometheus
    container_name: prometheus
    volumes:
      - ./prometheus:/etc/prometheus
      - prometheus_data:/prometheus
    expose:
      - 9090
    ports:
      - "9090:9090"
    networks:
      - backend
networks:
  backend:
    driver: bridge

volumes:
  prometheus_data: {}

Con el archivo de arriba lo que estamos haciendo es crear una imagen de prometheus (instalar el programa) y decirle que tome los archivos de configuración que se encuentran en una carpeta llamada ./prometheus, que podamos acceder a el por el puerto 9090 y que todos los datos recolectados se guarden en un volumen llamado prometheus_data.

la configuración de prometheus la hacemos por medio de un archivo .yml, en el cual vamos a describir cada cuanto tiempo se va a recolectar los datos y de donde se van a recolectar, para el ejemplo se van a tomar datos de nuestra API gracias al endpoint que generamos anteriormente y ademas vamos a recolectar los datos del propio prometheus.

# prometheus.yml

global:
  # How frequently to scrape targets by default.
  scrape_interval:     15s
  # How frequently to evaluate rules.
  evaluation_interval: 15s

# Load and evaluate rules in this file every 'evaluation_interval' seconds.
rule_files:
  - 'alert.rules'

# A scrape configuration containing exactly one endpoint to scrape.
scrape_configs:
  - job_name: 'swaggerstats'
    scrape_interval: 15s
    metrics_path: '/swagger-stats/metrics'
    static_configs:
      - targets: ['api:4000']

  - job_name: 'prometheus'
    scrape_interval: 15s
    static_configs:
      - targets: ['prometheus:9090']

También podemos generar alertas por medio de reglas, que básicamente dependerán del lenguaje de consulta de prometheus, para este ejemplo muy sencillo, se va a generar una alerta si alguna de nuestras instacias (api, prometheus) esta sin funcionar por cinco minutos.

# alert.rules

groups:
- name: example
  rules:
  # Alert for any instance that is unreachable for >5 minutes.
  - alert: InstanceDown
    expr: up == 0
    for: 5m
    labels:
      severity: page
    annotations:
      summary: "Instance  down"
      description: " of job  has been down for more than 5 minutes."

Prometheus provee una manera de probar nuestras expresiones y además nos lista todas las métricas que ha recolectado, de esta manera podemos hacer algunas pruebas rápidas, por ejemplo si queremos saber que instancias están funcionando basta con escribir como expresión up y nos mostrara lo siguiente:

element value
up{instance=”api:4000”,job=”swaggerstats”} 1
up{instance=”prometheus:9090”,job=”prometheus”} 0

Si queremos que solo traiga algo en especifico podemos acotar la misma expresión, en el siguiente ejemplo vamos a corroborar que la instacia de swagger esta funcionando:

up{job="swaggerstats"} == 1

Grafana

`Grafana es nuestro sistema de visualización y análisis de información y permite obtener datos de varios sistemas de monitoreo o lugar de almacenamiento y unificar toda esta información para explorar patrones de comportamiento y crear gráficas que nos permitan tomar rápidamente decisiones.

version: '3'
services:
  grafana:
    image: grafana/grafana
    container_name: grafana
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning
    environment:
      - GF_SECURITY_ADMIN_USER=${ADMIN_USER:-admin}
      - GF_SECURITY_ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin}
      - GF_USERS_ALLOW_SIGN_UP=false
    restart: unless-stopped
    expose:
      - 3000
    ports:
      - "3000:3000"
    networks:
      - backend
networks:
  backend:
    driver: bridge

volumes:
  grafana_data: {}

Con grafana podemos configurar varios datasources estos son las bases de datos que podemos consultar, en nuestro caso será una TSBD de prometheus, en el archivo anterior configuramos una instacia de grafana que obtendrá su configuración de la carpeta /grafana/provisioning donde podremos agregar diferentes data source y dashboard (conjunto de gráficas) por defecto, la informacíon se guardara en un volumen llamado grafana_data y lo podremos visualizar en el puerto 3000