Comment créer un exporter Prometheus ?

21 Décembre 2021 • 6 min

Si vous êtes arrivé(e) sur cet article, c’est que vous avez surement déjà un bon bagage technique sur Kubernetes et Prometheus… Ça tombe bien puisqu’on est là pour en parler !

De la définition au scrapping en passant par son déploiement, je vous explique tout (ou presque) sur l’exporter que nous allons créer ensemble. Ne vous inquiétez pas, ça va bien se passer. 😉

Avant de commencer, je précise que j’utilise un cluster sandbox sur lequel Prometheus est déjà déployé avec prometheus-operator.

Qu’est-ce qu’un exporter Prometheus ?

Alors, commençons par la théorie. Les exporter Prometheus sont communs et nombreux, vous en retrouvez une liste non-exhaustive sur la documentation officielle ici. Rappelons succinctement ce que c’est et à quoi ça sert.

La définition simple : c’est ni plus ni moins qu’un simple serveur qui renvoie des métriques standardisées pour Prometheus, avec ce type de structure :

# HELP prometheus_engine_query_log_enabled State of the query log.
# TYPE prometheus_engine_query_log_enabled gauge
prometheus_engine_query_log_enabled 0
# HELP prometheus_engine_query_log_failures_total The number of query log failures.
# TYPE prometheus_engine_query_log_failures_total counter
prometheus_engine_query_log_failures_total 0

Alors oui c’est clairement moins sexy que les graphes Grafana mais il faut garder à l’esprit que les belles courbes que vous avez en tête sur Grafana sont issues de ces données !

Mais alors, c’est à nous d’écrire toutes ces métriques ? A la main ? Et bien non ! Et il est important de savoir que Prometheus met à disposition plusieurs bibliothèques pour définir et exposer vos métriques selon le langage utilisé par votre exporter :

Et d’autres librairies non officielles peuvent être trouvées ici.

Comment créer un exporter Prometheus ?

Passons maintenant à la partie pratique en commençant par créer un simple serveur HTTP.

Pour ce tutoriel, je vais utiliser le langage python et la librairie cliente officielle. Mais comme expliqué plus haut, moulte autres languages de votre choix pourraient être utilisés pour arriver à vos fins.

Le ssserveur 🐍

Inutile de vous expliquer comment créer un projet, alors passons directement au code serveur.

Ce code est une démo de la librairie que nous utilisons : prometheus_client qui est le client python officiel pour Prometheus.

from prometheus_client import start_http_server, Summary
import random
import time

# Create a metric to track time spent and requests made.
REQUEST_TIME = Summary('request_processing_seconds', 'Time spent processing request')

# Decorate function with metric.
@REQUEST_TIME.time()
def process_request(t):
    """A dummy function that takes some time."""
    time.sleep(t)

if __name__ == '__main__':
    # Start up the server to expose the metrics.
    start_http_server(8000)
    # Generate some requests.
    while True:
        process_request(random.random())

code exemple fourni par la librairie client_python de Prometheus

Dans ce simple code, nous retrouvons un serveur accessible depuis le port 8000, renvoyant des métriques basiques données à titre d’exemple.

Une fois exécuté, une simple commande curl 127.0.0.1:8000 vous permet de consulter les métriques.

# HELP python_gc_objects_collected_total Objects collected during gc
# TYPE python_gc_objects_collected_total counter
python_gc_objects_collected_total{generation="0"} 322.0
python_gc_objects_collected_total{generation="1"} 43.0
python_gc_objects_collected_total{generation="2"} 0.0
# HELP python_gc_objects_uncollectable_total Uncollectable object found during GC
# TYPE python_gc_objects_uncollectable_total counter
python_gc_objects_uncollectable_total{generation="0"} 0.0
python_gc_objects_uncollectable_total{generation="1"} 0.0
python_gc_objects_uncollectable_total{generation="2"} 0.0
# HELP python_gc_collections_total Number of times this generation was collected
# TYPE python_gc_collections_total counter
python_gc_collections_total{generation="0"} 38.0
python_gc_collections_total{generation="1"} 3.0
python_gc_collections_total{generation="2"} 0.0
# HELP python_info Python platform information
# TYPE python_info gauge
python_info{implementation="CPython",major="3",minor="9",patchlevel="2",version="3.9.2"} 1.0
# HELP request_processing_seconds Time spent processing request
# TYPE request_processing_seconds summary
request_processing_seconds_count 21.0
request_processing_seconds_sum 8.899460147999998
# HELP request_processing_seconds_created Time spent processing request
# TYPE request_processing_seconds_created gauge
request_processing_seconds_created 1.619795017426719e+09

C’est moche, ça ne ressemble pas à grand chose mais c’est exploitable !

Un serpent dans le conteneur 📦

Comme vous vous en doutez, la suite logique est de mettre ce serveur dans un conteneur pour ensuite le déployer sur le cluster Kubernetes de vos rêves.

Pour cela, rien de plus simple, créez votre Dockerfile comme ceci :

FROM python:3.9-slim

WORKDIR /app
COPY . /app/

RUN pip install prometheus-client
    
EXPOSE 8000

CMD python -u src/server.py

Et exécutez le en n’oubliant pas de mapper le port de votre conteneur.

docker build -t kkonovod/pyprom:latest .
docker run --name pyprom -dp 127.0.0.1:8000:8000 kkonovod/pyprom:latest

Comme cité plus haut, curl 127.0.0.1:8000 pour consulter les métriques.

Pour finir ce chapitre, il nous manque une dernière étape, essentielle pour la suite : pousser votre image sur Docker hub. Cela se fait en trois étapes maximum:

  1. docker login pour s’authentifier,
  2. docker build -t <user>/pyprom:latest . pour build avec votre utilisateur Docker Hub,
  3. docker push <user>/pyprom pour pousser l’image sur Docker Hub.

Suite à cela, votre image sera accessible publiquement et pourra être utilisée par notre futur pod Kubernetes !

Untitled

A l’abordage 🚢 (sans Helm)

Il s’agit maintenant de déployer tout cela dans un cluster. Allons-y !

⚠️ Nous n’abordons pas ici la création de clusters Kubernetes, mais vous pouvez par exemple consulter cet article sur des méthodes d’installation basée sur Rancher.

Pour déployer notre tout dans un cluster Kubernetes, il va nous falloir plusieurs éléments :

  • Un pod dans lequel notre conteneur sera exécuté
  • Un service pour exposer le pod
  • Un servicemonitor pour permettre à Prometheus de retrouver nos métriques

Nous pourrions utiliser un gestionnaire de paquets comme Helm ou Kustomize mais restons simple à l’image de ce blogpost. Nous allons donc créer un fichier .yaml par objet Kubernetes à déployer.

Pour des raisons pratiques, je vous conseille de créer un dossier dédié (“manifests” dans mon cas) à la racine du projet pour y mettre les .yaml.

Pod

L’incontournable pod qui a pour rôle de déployer notre simple sssserveur python.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pyprom
spec:
  replicas: 1
  selector:
    matchLabels:
      app: pyprom
  template:
    metadata:
      labels:
        app: pyprom
    spec:
      containers:
      - name: pyprom
        image: kkonovod/pyprom
        ports:
        - containerPort: 8000

On place un label, ici app: pyprom, pour être retrouvé par notre futur service. Et on n’oublie pas le port 8000 correspondant au serveur Python, pour rappel start_http_server(8000).

Service

L’essentiel service qui fait le lien entre le(s) pod(s) et le futur servicemonitor.

apiVersion: v1
kind: Service
metadata:
  name: pyprom
  labels:
    app: pyprom
spec:
  selector:
    app: pyprom
  ports:
    - name: metrics
      port: 9253
      targetPort: 8000
      protocol: TCP

Il est important de prêter attention au port 9253 même s’il sert d’exemple, puisque les exporters Prometheus doivent respecter un standard d’attribution de port. Plus d’info ici. Aussi, le nom du port (metrics) n’est pas un hasard puisque c’est via ce nom que le servicemonitor va pouvoir récupérer le port.

ServiceMonitor

Et bien-sûr un servicemonitor qui a pour rôle de monitorer un ensemble de services pour permettre à Prometheus la découverte de métriques.

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: pyprom-servicemonitor
  namespace: monitoring
  labels:
    app: pyprom
    release: prometheus-operator
spec:
  selector:
    matchLabels:
      app: pyprom
  endpoints:
  - port: metrics
    interval: 15s

Ici rien de spécial dans ce .yaml simpliste, mais faites néanmoins attention à deux paramètres :

  • release: prometheus-operator": Label utilisé par Prometheus pour la découverte de ce servicemonitor.
  • matchLabels": Les labels utilisés pour retrouver les services à monitorer.

💡 Le servicemonitor doit se trouver dans le même namespace Kubernetes que Prometheus !

On se retrouve avec cet agencement :

.
├── Dockerfile
├── Pipfile
├── Pipfile.lock
├── manifests
│   ├── pod.yaml
│   ├── service.yaml
│   └── servicemonitor.yaml
└── src
    └── server.py

2 directories, 7 files

Et on termine avec :

kubectl apply -f chart/ -n <namespace>

Plus de peur que de mal

Vous voyez, c’est finalement très simple. Et vous pouvez dès à présent vous rendre sur votre serveur Prometheus préféré et rechercher notre métrique custom fraichement créée.

Untitled

Si vous êtes toujours là, déjà merci … et surtout bravo : vous êtes l’heureux créateur d’un jeune exporter tout frais !

Et si vous voulez jeter un oeil à un exporter Prometheus plus complexe, vous pouvez rendre une petite visite à notre Certificate Exporter opensourcé, il facilite grandement la gestion et l’opération des certificats dans des clusters Kubernetes.


Ne ratez pas nos prochains articles DevOps et Cloud Native! Suivez Enix sur Twitter!