Aller au contenu
English
English
Outils medium

Exporters Prometheus

24 min de lecture

Prometheus ne sait faire qu'une chose pour collecter : envoyer une requête HTTP et lire un texte au format qu'il attend. Un exporter est le programme qui produit ce texte à partir d'une source qui l'ignore, système d'exploitation, base de données, équipement réseau. Ce guide vous aide à choisir le bon outil et à comprendre comment les pièces s'articulent.

  • Distinguer les trois familles d'exporters, et savoir laquelle impose du relabeling.
  • Choisir entre un exporter et l'instrumentation directe de votre code.
  • Installer Node Exporter en service systemd, sous un compte dédié.
  • Mettre en place le pattern multi-target avec Blackbox, relabeling compris.
  • Éviter les pièges de cardinalité et ne pas exposer /metrics sans protection.

L'exporter n'envoie jamais rien de lui-même : c'est Prometheus qui vient lire son endpoint /metrics à intervalle régulier, une opération appelée scrape. Ce modèle pull, par opposition au push d'un agent classique, explique l'essentiel du reste de la page : l'exporter doit être joignable en réseau depuis le serveur Prometheus, et non l'inverse.

Flux d'un exporter : la source envoie les métriques à l'exporter qui les expose pour Prometheus

[SOURCE] → (exporter / instrumentation) → /metrics ──scrape──▶ Prometheus
  • Source : OS, DB, service réseau, application…
  • Exporter : interroge la source et publie le résultat sur /metrics
  • Prometheus : collecte, stocke, interroge, alerte

Le choix se décide sur un seul critère, la possession du code. Instrumenter consiste à faire produire ses propres métriques à l'application, ce qui donne accès à des indicateurs métier qu'aucun outil externe ne pourra déduire : un panier moyen, un nombre de commandes en attente. L'exporter, lui, observe de l'extérieur ce qu'un composant veut bien exposer, et se limite donc à ce que ce composant publie déjà.

SituationSolutionPourquoi
J'ai le code de l'appInstrumentation (client libs, OTel)Métriques métier précises, moins d'approximation
Produit tiers, pas de codeExporter dédiéMySQL, PostgreSQL, Redis, Nginx…
Système d'exploitationAgent (Node/Windows Exporter)CPU, RAM, disque, réseau
Vérifier la disponibilité externeBlackbox ExporterProbes HTTP, DNS, TCP, ICMP

C'est la distinction à comprendre avant de configurer quoi que ce soit, parce qu'elle change la façon d'écrire le scrape_config. Le critère de tri est simple : combien de cibles un exemplaire de l'exporter peut-il observer, et où tourne-t-il par rapport à elles. Les deux premières familles se configurent avec une liste de cibles ordinaire ; la troisième impose de passer par le relabeling, détaillé plus bas.

Un exporter = une machine/service. L'exporter tourne sur ou près de la cible.

ExempleUsage
Node ExporterMétriques système Linux
Windows ExporterMétriques système Windows

Configuration Prometheus (simple) :

scrape_configs:
- job_name: node
static_configs:
- targets: ['server1:9100', 'server2:9100']

Chaque target est directement l'exporter.

L'exporter se connecte à un service (MySQL, PostgreSQL, Redis…) via TCP + credentials, puis expose /metrics.

ExempleUsage
mysqld_exporterMySQL/MariaDB
postgres_exporterPostgreSQL
redis_exporterRedis
mongodb_exporterMongoDB

Points d'attention :

  • Droits minimum (read-only quand possible)
  • Latence de collecte → ajuster scrape_timeout
  • Cardinalité des labels

Un seul exporter peut monitorer N cibles via un paramètre target=.... C'est le pattern multi-target exporter.

ExempleUsage
Blackbox ExporterProbes HTTP, DNS, TCP, ICMP
SNMP ExporterÉquipements réseau SNMP

Pourquoi c'est différent ?

  • L'exporter ne connaît pas les cibles à l'avance
  • Prometheus passe la cible en paramètre via relabeling
  • Un seul exporter peut sonder des centaines d'URLs

Ce tableau part de l'intention et non de l'outil : formulez d'abord ce que vous voulez savoir, la colonne famille vous indique ensuite le type de configuration à prévoir. La dernière ligne fait exception, cAdvisor n'étant pas un exporter au sens strict, comme l'explique l'encadré qui suit.

IntentionOutilFamilleCe que ça mesure
Santé machine LinuxNode ExporterACPU, RAM, disque, réseau
Santé Windowswindows_exporterAPerformance counters
Disponibilité HTTP/DNS/TCP/ICMPBlackbox ExporterCProbes externes, SSL expiry
Santé MySQL/MariaDBmysqld_exporterBConnexions, requêtes, réplication
Santé PostgreSQLpostgres_exporterBConnexions, locks, bloat
Santé Redisredis_exporterBMémoire, keys, ops/sec
Métriques conteneurscAdvisor / kubeletSourceCPU, RAM, I/O par conteneur

Liste complète des exporters officiels

C'est l'exporter à déployer en premier : il collecte les métriques système d'une machine Linux, CPU, mémoire, disques, interfaces réseau. Il s'installe sur chaque serveur à surveiller et écoute par défaut sur le port 9100. Aucune configuration n'est nécessaire pour démarrer : les réglages servent uniquement à activer ou désactiver des collecteurs.

Le binaire est le mode recommandé sur une machine physique ou virtuelle, car l'exporter doit voir le système hôte pour en mesurer quoi que ce soit. La variante Docker existe mais réclame plusieurs options de partage avec l'hôte, détaillées dans son onglet.

  1. Télécharger l'archive et le fichier de sommes

    Fenêtre de terminal
    cd /tmp
    VERSION="1.12.1"
    BASE="https://github.com/prometheus/node_exporter/releases/download/v${VERSION}"
    wget "${BASE}/node_exporter-${VERSION}.linux-amd64.tar.gz"
    wget "${BASE}/sha256sums.txt"

    Sur une installation minimale de Debian 13, wget n'est pas présent alors que curl l'est : un apt install wget préalable évite un command not found dès la première commande de la procédure.

  2. Vérifier l'empreinte avant d'extraire

    Fenêtre de terminal
    sha256sum --ignore-missing -c sha256sums.txt

    La sortie doit afficher la ligne de l'archive suivie de OK. Si elle affiche ÉCHEC ou FAILED, l'archive est corrompue ou altérée : supprimez-la sans l'extraire. L'ordre compte, et c'est tout l'intérêt de cette étape : une empreinte calculée après extraction ne protège de rien.

  3. Installer

    Fenêtre de terminal
    tar xvfz node_exporter-${VERSION}.linux-amd64.tar.gz
    sudo cp node_exporter-${VERSION}.linux-amd64/node_exporter /usr/local/bin/
    sudo useradd --system --no-create-home --shell /usr/sbin/nologin node_exporter

    L'option --system n'est pas décorative : sans elle, useradd pioche dans la plage des comptes humains. Mesuré sur Debian 13, le même compte reçoit l'UID 1002 sans --system et l'UID 988 avec, et un UID supérieur à 1000 fait apparaître un compte de service dans les écrans de connexion et les inventaires d'utilisateurs. Le groupe node_exporter, lui, est créé automatiquement parce que /etc/login.defs porte USERGROUPS_ENAB yes : c'est ce qui permet à l'unité de réclamer Group=node_exporter sans autre commande.

  4. Créer le service

    /etc/systemd/system/node_exporter.service
    [Unit]
    Description=Node Exporter
    Wants=network-online.target
    After=network-online.target
    [Service]
    User=node_exporter
    Group=node_exporter
    Type=exec
    ExecStart=/usr/local/bin/node_exporter
    Restart=always
    # Hardening
    NoNewPrivileges=true
    ProtectSystem=strict
    ProtectHome=true
    [Install]
    WantedBy=multi-user.target

    Le Type=exec mérite une explication, parce que Type=simple est la valeur qu'on rencontre le plus souvent dans les unités d'exporter. Les deux lancent bien un processus au premier plan, mais ils ne disent pas la même chose à systemctl start. Mesuré sur systemd 257, Debian 13, avec un ExecStart volontairement introuvable :

    RéglageCe que rend systemctl startÉtat réel du service
    Type=simple0, donc un succèsactive, alors que rien ne tourne
    Type=exec1, donc un échecfailed

    Avec Type=simple, systemd considère le service démarré dès qu'il a forké, sans attendre de savoir si le binaire s'exécute. Une faute de frappe dans le chemin, ou une étape de copie oubliée, produit donc un service annoncé actif et un terminal muet. Type=exec attend l'execve() et remonte l'échec.

  5. Démarrer

    Fenêtre de terminal
    sudo systemctl daemon-reload
    sudo systemctl enable node_exporter
    sudo systemctl start node_exporter
  6. Vérifier que l'exporter répond vraiment

    Un service active prouve que le processus tourne, pas qu'il publie ses métriques. Les deux commandes se complètent :

    Fenêtre de terminal
    systemctl is-active node_exporter
    curl -s http://localhost:9100/metrics | head -5

    La première doit afficher active. La seconde doit ouvrir sur les deux lignes de métadonnées que Prometheus attend en tête de chaque métrique, # HELP puis # TYPE, suivies des valeurs elles-mêmes. Une réponse vide ou un refus de connexion sur le port 9100 signale un exporter démarré mais qui n'écoute pas, cas typique d'une option mal orthographiée dans ExecStart : journalctl -u node_exporter -n 20 le dira.

Côté Prometheus, rien de particulier : chaque serveur est déclaré comme une cible ordinaire, avec son port d'exporter. Le nom du job devient un label attaché à toutes les métriques collectées, et il servira ensuite à filtrer dans chaque requête : choisissez-le explicite.

prometheus.yml
scrape_configs:
- job_name: 'node'
static_configs:
- targets:
- 'server1:9100'
- 'server2:9100'

Node Exporter publie plusieurs centaines de séries ; celles-ci suffisent à couvrir la surveillance courante. Notez le suffixe _total, qui signale un compteur cumulatif : sa valeur brute n'a aucun intérêt, seule sa variation dans le temps en a, d'où l'usage de rate() juste après.

MétriqueDescription
node_cpu_seconds_totalTemps CPU par mode (idle, user, system)
node_memory_MemAvailable_bytesMémoire disponible
node_filesystem_avail_bytesEspace disque disponible
node_network_receive_bytes_totalBytes reçus par interface
node_load1, node_load5, node_load15Load average

Ces trois requêtes reposent sur le même principe : Node Exporter ne fournit aucun pourcentage d'utilisation, il faut le calculer. Pour le processeur, on part du temps passé en mode idle et on le soustrait à 100 ; pour la mémoire et le disque, on rapporte l'espace disponible à l'espace total.

# CPU utilisé (%)
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# Mémoire utilisée (%)
(1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100
# Disque utilisé (%)
(1 - node_filesystem_avail_bytes / node_filesystem_size_bytes) * 100

Surveille la disponibilité depuis l'extérieur : HTTP, DNS, TCP, ICMP. C'est du synthetic monitoring (probes), pas de la télémétrie interne.

Blackbox ne tourne pas sur les cibles qu'il surveille. Un seul exemplaire peut sonder des centaines d'URLs, parce que la cible ne fait pas partie de sa configuration : Prometheus la lui transmet à chaque scrape, dans le paramètre target de la requête HTTP.

Prometheus scrappe:
GET /probe?target=https://example.com&module=http_2xx
Blackbox répond:
probe_success 1
probe_duration_seconds 0.234

Un seul exemplaire suffit pour l'ensemble des cibles. Le point à décider n'est donc pas combien en déployer, mais où : placez-le là d'où vous voulez mesurer la disponibilité. Une sonde exécutée depuis le même datacenter que le service ne dira rien de ce que vivent vos utilisateurs.

Fenêtre de terminal
docker run -d \
--name blackbox_exporter \
-p 9115:9115 \
-v $(pwd)/blackbox.yml:/config/blackbox.yml:ro \
prom/blackbox-exporter:v0.28.0@sha256:e753ff9f3fc458d02cca5eddab5a77e1c175eee484a8925ac7d524f04366c2fc \
--config.file=/config/blackbox.yml

Le fichier de configuration ne contient aucune cible : il déclare des modules, c'est-à-dire des manières de sonder. Chaque module porte un nom que Prometheus réclamera au moment du scrape. Le module http_2xx illustre le point important : ce sont les valid_status_codes qui décident si la sonde est un succès, et un 404 correctement renvoyé sera donc compté comme un échec.

blackbox.yml
modules:
http_2xx:
prober: http
timeout: 5s
http:
valid_http_versions: ["HTTP/1.1", "HTTP/2.0"]
valid_status_codes: [200, 201, 204]
follow_redirects: true
tcp_connect:
prober: tcp
timeout: 5s
icmp:
prober: icmp
timeout: 5s
dns_resolve:
prober: dns
dns:
query_name: "example.com"
query_type: "A"

Le relabeling transforme une liste d'URLs en paramètres pour Blackbox :

prometheus.yml
scrape_configs:
- job_name: 'blackbox-http'
metrics_path: /probe
params:
module: [http_2xx]
static_configs:
- targets:
- https://example.com
- https://api.example.com/health
- https://docs.example.com
relabel_configs:
# 1. L'URL devient le paramètre ?target=
- source_labels: [__address__]
target_label: __param_target
# 2. L'URL est aussi copiée dans le label "instance"
- source_labels: [__param_target]
target_label: instance
# 3. L'adresse réelle de scrape devient Blackbox Exporter
- target_label: __address__
replacement: blackbox-exporter:9115

Ce qui se passe :

  1. Prometheus voit https://example.com dans targets
  2. Relabeling transforme en GET blackbox-exporter:9115/probe?target=https://example.com
  3. Le label instance garde l'URL d'origine pour identifier les métriques

Toutes les sondes produisent le même petit ensemble de métriques, quel que soit le module utilisé, ce qui rend les alertes uniformes : une règle écrite pour une sonde HTTP vaut pour une sonde TCP. La dernière ligne fait exception à deux titres : c'est celle qu'on oublie de surveiller, puisqu'elle permet d'être prévenu de l'expiration d'un certificat avant la coupure, et c'est la seule qui n'apparaît pas toujours.

MétriqueDescription
probe_success1 si probe réussie, 0 sinon
probe_duration_secondsDurée totale de la probe
probe_http_status_codeCode HTTP retourné
probe_ssl_earliest_cert_expiryDate d'expiration du certificat TLS, uniquement sur une sonde HTTPS

La dernière ligne mérite sa réserve. Vérifié sur Blackbox 0.28.0 : une sonde vers une URL en http:// ne produit aucune série probe_ssl_earliest_cert_expiry, là où probe_success, probe_duration_seconds et probe_http_status_code en produisent une chacune. Une alerte d'expiration de certificat posée sur un endpoint en clair ne se déclenchera donc jamais, et ce silence ressemble à s'y méprendre à « tout va bien ».

Ces deux règles couvrent l'essentiel de ce qu'apporte Blackbox. Le for: 2m de la première évite de réveiller quelqu'un pour un incident réseau passager : l'expression doit continuer à rendre au moins une série pendant deux minutes d'affilée. La formulation compte, car un comparateur PromQL filtre le vecteur au lieu de rendre un booléen ; la configuration de Prometheus détaille ce mécanisme. La seconde compare la date d'expiration du certificat à l'heure courante et déclenche trente jours avant l'échéance, ce qui laisse le temps d'un renouvellement.

rules/blackbox.yml
groups:
- name: blackbox
rules:
- alert: EndpointDown
expr: probe_success == 0
for: 2m
labels:
severity: critical
annotations:
summary: "{{ $labels.instance }} is down"
- alert: SSLCertExpiringSoon
expr: probe_ssl_earliest_cert_expiry - time() < 86400 * 30
for: 1h
labels:
severity: warning
annotations:
summary: "SSL cert for {{ $labels.instance }} expires in < 30 days"

Ces exporters ont tous le même fonctionnement : ils ouvrent une connexion au moteur avec un compte dédié, interrogent ses vues statistiques internes et en publient le résultat. Deux points de vigilance en découlent. Le compte utilisé doit disposer du minimum de privilèges nécessaires à la lecture des statistiques, jamais des droits d'administration. Et la chaîne de connexion contient un mot de passe : elle n'a rien à faire dans un dépôt Git, passez-la par une variable d'environnement ou un fichier protégé.

Les droits PROCESS, REPLICATION CLIENT et SELECT suffisent à alimenter toutes les métriques exposées. Remplacez le mot de passe de l'exemple avant toute exécution.

  1. Créer l'utilisateur MySQL (droits minimum)

    CREATE USER 'exporter'@'localhost' IDENTIFIED BY 'password';
    GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'localhost';
    FLUSH PRIVILEGES;
  2. Lancer l'exporter

    Fenêtre de terminal
    export DATA_SOURCE_NAME='exporter:password@(localhost:3306)/'
    ./mysqld_exporter

    Ou via Docker :

    Fenêtre de terminal
    docker run -d \
    --name mysqld_exporter \
    -p 9104:9104 \
    -e DATA_SOURCE_NAME='exporter:password@(mysql:3306)/' \
    prom/mysqld-exporter:v0.20.0@sha256:abed8dac117b4ae5b70757f988e44795935b9a72c3b58d720c67ba688f8cb79e
  3. Configuration Prometheus

    - job_name: 'mysql'
    static_configs:
    - targets: ['localhost:9104']

Métriques clés :

MétriqueDescription
mysql_upMySQL accessible (1/0)
mysql_global_status_threads_connectedConnexions actives
mysql_global_status_slow_queriesRequêtes lentes
mysql_global_status_questionsRequêtes exécutées

La configuration tient dans une seule variable d'environnement au format URI. Le sslmode=disable de l'exemple suppose que l'exporter tourne sur la même machine que la base ; dès que la connexion traverse le réseau, exigez require ou mieux, sans quoi les identifiants passent en clair.

Fenêtre de terminal
export DATA_SOURCE_NAME='postgresql://exporter:password@localhost:5432/postgres?sslmode=disable'
./postgres_exporter

Métriques clés :

MétriqueDescription
pg_upPostgreSQL accessible
pg_stat_activity_countConnexions par état
pg_stat_database_deadlocksDeadlocks
pg_stat_database_tup_fetchedLignes lues

L'adresse de l'instance se passe en option de ligne de commande. Si votre Redis demande un mot de passe, fournissez-le par la variable d'environnement prévue à cet effet plutôt que dans la commande : tout compte du système peut lire la liste des processus, et l'y trouverait en clair.

Fenêtre de terminal
docker run -d \
--name redis_exporter \
-p 9121:9121 \
oliver006/redis_exporter:v1.91.1@sha256:c67a432dba6b4ae30f471e3c77cf14a289133bbeeb89abb0bb03e6092efb2836 \
--redis.addr=redis://localhost:6379

Pour des métriques d'un système tiers non couvert, créez un exporter.

La bibliothèque cliente officielle fait tout le travail de format et de service HTTP : start_http_server ouvre l'endpoint /metrics, il ne reste qu'à mettre à jour les valeurs. Attention à la boucle finale : l'intervalle de rafraîchissement doit rester inférieur ou égal au scrape_interval de Prometheus, sinon Prometheus collectera deux fois la même valeur et le graphique affichera un palier qui n'existe pas.

my_exporter.py
from prometheus_client import start_http_server, Gauge
import requests
import time
# Définir les métriques
ORDERS_PENDING = Gauge('orders_pending_total', 'Pending orders count')
ORDERS_VALUE = Gauge('orders_pending_value_euros', 'Value of pending orders')
def collect_metrics():
"""Collecte depuis une API externe."""
response = requests.get('https://api.internal/orders/stats')
data = response.json()
ORDERS_PENDING.set(data['pending_count'])
ORDERS_VALUE.set(data['pending_value'])
if __name__ == '__main__':
start_http_server(8000)
print("Exporter running on :8000/metrics")
while True:
collect_metrics()
time.sleep(15)

Le type se choisit à la déclaration et ne se change pas ensuite sans casser les requêtes existantes. La distinction fondamentale oppose le Counter, qui ne fait que croître et se remet à zéro au redémarrage du processus, et la Gauge, qui représente une valeur instantanée pouvant monter comme descendre.

TypeUsageExemple
CounterValeur croissante uniquementRequêtes, erreurs
GaugeMonte et descendMémoire, connexions
HistogramDistribution avec bucketsLatence
SummaryQuantiles côté clientLatence (moins recommandé)

Le nom d'une métrique est public et durable : le renommer casse tous les tableaux de bord et toutes les alertes qui l'utilisent. Deux règles suffisent : préfixer par le nom du composant et terminer par l'unité de mesure, en unités de base (secondes et octets, jamais millisecondes ni mégaoctets).

namespace_name_unit_suffix
  • myapp_http_requests_total ✅
  • myapp_request_duration_seconds ✅
  • requestCount ❌ (pas de namespace, pas d'unité)

Deux de ces pièges méritent une attention particulière. Un endpoint /metrics laissé ouvert renseigne un attaquant sur votre parc, versions et volumétrie comprises, sans aucune authentification. Et la cardinalité, c'est-à-dire le nombre de combinaisons de labels distinctes, est la première cause de saturation d'un Prometheus : mettre un identifiant d'utilisateur ou une URL complète en label crée une série temporelle par valeur rencontrée.

PiègeImpactSolution
/metrics exposé sans authFuite d'infosFirewall, TLS, basic auth
Trop de labelsExplosion cardinalitéLabels faible cardinalité uniquement
Scrape interval trop courtSurcharge exporter15s-60s selon criticité
Scrape timeout trop courtTargets "DOWN"Ajuster scrape_timeout

Le premier réflexe consiste à interroger l'endpoint /metrics à la main, depuis la machine qui héberge Prometheus et non depuis votre poste : une cible marquée DOWN l'est le plus souvent pour une raison de filtrage réseau, et le poste de travail n'est pas sur le même chemin. Si la réponse arrive mais que les métriques attendues manquent, le problème s'est déplacé vers les droits du compte utilisé par l'exporter.

SymptômeCause probableSolution
Exporter non accessibleFirewall/réseaucurl http://exporter:port/metrics
Métriques videsPas de permissionsVérifier droits DB/système
Scrape timeoutExporter lentAugmenter scrape_timeout
Target "DOWN"DNS/réseauVérifier résolution + ping

Commandes de debug :

Fenêtre de terminal
# Vérifier l'endpoint
curl -s http://localhost:9100/metrics | head -20
# Vérifier dans Prometheus
# Status → Targets
# Pour Blackbox, tester la probe manuellement
curl "http://localhost:9115/probe?target=https://example.com&module=http_2xx"
  • 3 familles : single-target (Node), service (MySQL), multi-target (Blackbox)
  • Instrumentation > Exporter si vous avez le code
  • Blackbox = synthetic monitoring (disponibilité externe)
  • Relabeling : essentiel pour le pattern multi-target
  • Sécurité : ne jamais exposer /metrics sans protection réseau

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens ce site gratuitement, sans publicité, sans profilage et sans compte à créer. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn