PromQL (Prometheus Query Language) est le langage de requête de Prometheus. Ce guide vous accompagne des bases aux requêtes avancées, avec des exemples concrets que vous pouvez tester immédiatement.
Premiers pas avec PromQL
Section intitulée « Premiers pas avec PromQL »Accédez à l'interface Prometheus (http://localhost:9090) et utilisez l'onglet
Graph pour tester vos requêtes.

Requête la plus simple
Section intitulée « Requête la plus simple »Tapez le nom d'une métrique pour voir toutes ses séries :
upRésultat : toutes les séries de la métrique up, une par target scrapée.
up{instance="localhost:9090", job="prometheus"} 1up{instance="node1:9100", job="node"} 1up{instance="node2:9100", job="node"} 0Filtrer avec les labels
Section intitulée « Filtrer avec les labels »Les sélecteurs de labels filtrent les séries à l'intérieur des accolades. Sans sélecteur, vous récupérez toutes les séries portant ce nom de métrique, ce qui devient vite illisible sur un cluster de plusieurs dizaines de cibles.
up{job="node"}Quatre opérateurs de matching existent, dont deux fondés sur les expressions régulières. Les regex de PromQL sont ancrées automatiquement : job=~"node" ne correspond qu'à la valeur exacte node, pas à node-exporter. C'est le piège le plus courant chez les débutants.
| Opérateur | Description | Exemple |
|---|---|---|
= | Égalité exacte | job="prometheus" |
!= | Différent de | job!="test" |
=~ | Regex match | job=~"node.*" |
!~ | Regex not match | job!~"test.*" |
Exemples pratiques :
# Toutes les requêtes HTTP sauf les 200http_requests_total{status!="200"}
# Méthodes GET ou POSThttp_requests_total{method=~"GET|POST"}
# Namespaces commençant par "prod"container_memory_usage_bytes{namespace=~"prod.*"}Types de données
Section intitulée « Types de données »PromQL manipule quatre types de données, et la plupart des messages d'erreur de l'interface viennent d'une confusion entre eux. Les deux premiers concentrent l'essentiel de l'usage quotidien : l'instant vector représente l'état courant, le range vector l'historique récent. Chaque fonction attend un type précis en entrée et en produit un autre en sortie, ce qui détermine quelles expressions peuvent s'imbriquer.
| Type | Description | Exemple |
|---|---|---|
| Instant vector | Valeurs actuelles (un point par série) | up |
| Range vector | Valeurs sur une période | up[5m] |
| Scalar | Nombre flottant | 3.14 |
| String | Chaîne (rarement utilisé) | "hello" |
Instant vector
Section intitulée « Instant vector »C'est le type le plus courant. Chaque série a une seule valeur (la dernière).
node_memory_MemAvailable_bytesRange vector
Section intitulée « Range vector »Sélectionne toutes les valeurs sur une période. Nécessaire pour les
fonctions comme rate().
http_requests_total[5m]Périodes disponibles : s (secondes), m (minutes), h (heures), d (jours),
w (semaines), y (années).
Opérateurs
Section intitulée « Opérateurs »Les opérateurs permettent de calculer, de comparer et de combiner des séries entre elles. Deux points les distinguent d'un langage de programmation classique : ils travaillent sur des ensembles de séries et non sur des nombres isolés, et une opération entre deux métriques n'a lieu que si leurs labels correspondent. C'est cette règle de correspondance qui explique la plupart des résultats vides obtenus en combinant deux métriques.
Opérateurs arithmétiques
Section intitulée « Opérateurs arithmétiques »Les opérateurs classiques s'appliquent à chaque série individuellement. Quand un des deux membres est un scalaire, le calcul porte sur toutes les séries et le nom de la métrique disparaît du résultat, puisque la valeur ne représente plus la métrique d'origine.
# Mémoire utilisée en pourcentage(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100
# Conversion bytes → gigabytesnode_filesystem_size_bytes / 1024 / 1024 / 1024| Opérateur | Description |
|---|---|
+ | Addition |
- | Soustraction |
* | Multiplication |
/ | Division |
% | Modulo |
^ | Puissance |
Opérateurs de comparaison
Section intitulée « Opérateurs de comparaison »Par défaut, une comparaison filtre : elle conserve les séries qui satisfont la condition et supprime les autres, en gardant leur valeur d'origine. C'est ce comportement qui rend les règles d'alerte lisibles, une alerte se déclenchant dès que le résultat n'est pas vide. Le modificateur bool change la donne : il conserve toutes les séries et remplace leur valeur par 0 ou 1.
# Instances avec CPU > 80%instance:cpu_usage:percent > 80
# Garder uniquement les séries au-dessus du seuilhttp_request_duration_seconds > 0.5| Opérateur | Description |
|---|---|
== | Égal |
!= | Différent |
> | Supérieur |
< | Inférieur |
>= | Supérieur ou égal |
<= | Inférieur ou égal |
Opérateurs logiques
Section intitulée « Opérateurs logiques »and, or et unless raisonnent sur la présence des séries, pas sur leurs valeurs, et ne s'appliquent qu'entre deux instant vectors. Deux séries se correspondent quand tous leurs labels sont identiques ; unless sert typiquement à exclure d'une alerte les cibles déjà en maintenance.
# Séries présentes dans les deux (intersection)metric_a and metric_b
# Séries présentes dans l'un ou l'autre (union)metric_a or metric_b
# Séries de A absentes de Bmetric_a unless metric_bFonctions essentielles
Section intitulée « Fonctions essentielles »Une poignée de fonctions couvre la quasi-totalité des tableaux de bord et des alertes. Elles se répartissent en deux familles : celles qui transforment un range vector en instant vector, comme rate(), et les agrégateurs qui réduisent plusieurs séries à une seule, comme sum(). La règle à retenir est que toute donnée de type counter passe forcément par la première famille avant d'être affichée.
rate(), Taux de variation
Section intitulée « rate(), Taux de variation »La fonction la plus importante. Calcule le taux par seconde d'un counter.
# Requêtes par seconde (sur 5 minutes)rate(http_requests_total[5m])irate(), Taux instantané
Section intitulée « irate(), Taux instantané »Comme rate() mais utilise uniquement les 2 derniers points. Plus réactif
mais plus bruité.
# Taux instantané (derniers 5 min de données)irate(http_requests_total[5m])increase(), Augmentation totale
Section intitulée « increase(), Augmentation totale »Nombre total d'augmentations sur la période.
# Nombre de requêtes sur 1 heureincrease(http_requests_total[1h])sum(), avg(), min(), max(), Agrégations
Section intitulée « sum(), avg(), min(), max(), Agrégations »Ces agrégateurs réduisent plusieurs séries à une seule valeur par instant. Sans clause by ou without, tous les labels disparaissent du résultat : vous obtenez un unique chiffre pour l'ensemble du parc, ce qui est rarement l'intention sur un graphique de supervision.
# Total des requêtes (toutes instances)sum(rate(http_requests_total[5m]))
# Moyenne de mémoire par nodeavg(node_memory_MemAvailable_bytes)
# Maximum de CPUmax(instance:cpu_usage:percent)Agrégation par label
Section intitulée « Agrégation par label »Utilisez by ou without pour contrôler l'agrégation :
# Somme par jobsum by(job) (rate(http_requests_total[5m]))
# Somme en ignorant le label instancesum without(instance) (rate(http_requests_total[5m]))histogram_quantile(), Percentiles
Section intitulée « histogram_quantile(), Percentiles »Calcule les percentiles depuis un histogram.
# P95 de latence par jobhistogram_quantile(0.95, sum by(job, le) (rate(http_request_duration_seconds_bucket[5m])))
# P99 globalhistogram_quantile(0.99, sum by(le) (rate(http_request_duration_seconds_bucket[5m])))Fonctions mathématiques
Section intitulée « Fonctions mathématiques »Ces fonctions s'appliquent point par point à un instant vector et laissent les labels intacts. Elles servent surtout à rendre un graphique lisible : arrondir une valeur avant affichage, ramener une échelle très étalée à un logarithme, ou supprimer le signe d'une variation.
| Fonction | Description | Exemple |
|---|---|---|
abs() | Valeur absolue | abs(delta(temp[1h])) |
ceil() | Arrondi supérieur | ceil(value) |
floor() | Arrondi inférieur | floor(value) |
round() | Arrondi | round(value, 0.1) |
ln() | Logarithme naturel | ln(value) |
log2() | Log base 2 | log2(value) |
log10() | Log base 10 | log10(value) |
sqrt() | Racine carrée | sqrt(value) |
Fonctions temporelles
Section intitulée « Fonctions temporelles »Elles donnent accès à l'horloge du serveur Prometheus, exprimée en UTC : hour() renvoie donc l'heure UTC, pas l'heure locale de vos équipes. C'est le détail qui décale d'une ou deux heures les alertes conditionnées aux horaires de bureau.
| Fonction | Description |
|---|---|
time() | Timestamp Unix actuel |
timestamp() | Timestamp de chaque sample |
day_of_week() | Jour de la semaine (0-6) |
hour() | Heure (0-23) |
# Alerte uniquement en heures ouvréesup == 0 and hour() >= 9 and hour() <= 18Fonctions sur range vectors
Section intitulée « Fonctions sur range vectors »Toutes ces fonctions prennent un range vector en entrée, donc une métrique suivie d'une période entre crochets, et renvoient un instant vector affichable. Les variantes _over_time conviennent aux gauges dont on veut lisser les oscillations, tandis que resets() et delta() s'adressent respectivement aux counters et aux gauges.
| Fonction | Description |
|---|---|
avg_over_time() | Moyenne sur la période |
min_over_time() | Minimum sur la période |
max_over_time() | Maximum sur la période |
sum_over_time() | Somme sur la période |
count_over_time() | Nombre de points |
stddev_over_time() | Écart-type |
changes() | Nombre de changements |
resets() | Nombre de resets (counter) |
delta() | Différence premier-dernier |
deriv() | Dérivée (pente) |
predict_linear() | Prédiction linéaire |
# Moyenne de CPU sur 1 heureavg_over_time(instance:cpu_usage:percent[1h])
# Prédiction : dans 4h, espace disquepredict_linear(node_filesystem_avail_bytes[6h], 4*3600)Requêtes avancées
Section intitulée « Requêtes avancées »Les requêtes qui suivent sont celles que vous recopierez le plus souvent dans vos tableaux de bord. Elles reposent toutes sur les métriques exposées par node_exporter et par les instrumentations HTTP standard, avec des noms conformes aux conventions Prometheus. Adaptez les noms de labels à votre environnement avant de les mettre en alerte : handler, status ou namespace varient d'une bibliothèque d'instrumentation à l'autre.
CPU usage
Section intitulée « CPU usage »Le CPU ne s'exporte pas directement en pourcentage : node_exporter publie un compteur de secondes passées dans chaque mode, dont le mode idle. On calcule donc l'inactivité, puis on la soustrait de 100.
# Pourcentage CPU utilisé par instance100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)Explication :
node_cpu_seconds_total{mode="idle"}: temps CPU en idlerate(...[5m]): taux par seconde sur 5 minavg by(instance): moyenne des cores par machine* 100: conversion en pourcentage100 - ...: usage = 100% - idle
Mémoire utilisée
Section intitulée « Mémoire utilisée »Utilisez MemAvailable et non MemFree : le noyau considère comme disponible la mémoire occupée par le cache disque, qu'il rendra à la demande. Un calcul basé sur MemFree déclenche des alertes permanentes sur des serveurs parfaitement sains.
# Pourcentage mémoire utilisée(1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100Taux d'erreurs HTTP
Section intitulée « Taux d'erreurs HTTP »Ce ratio d'erreurs est la brique de base d'un objectif de niveau de service. Les deux sum() sont indispensables : sans eux, la division se ferait série par série et échouerait, faute de labels identiques des deux côtés.
# Taux d'erreurs 5xx sur le totalsum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) * 100Latence P95
Section intitulée « Latence P95 »Le P95 indique la durée sous laquelle se situent 95 % des requêtes. Le label le doit rester dans le by, sinon la fonction perd les bornes des buckets et ne peut plus interpoler ; handler permet ensuite de repérer l'endpoint responsable.
# P95 par endpointhistogram_quantile(0.95, sum by(handler, le) (rate(http_request_duration_seconds_bucket[5m])))Prédiction d'espace disque
Section intitulée « Prédiction d'espace disque »Alerter sur un seuil fixe de remplissage arrive souvent trop tard. En extrapolant la pente de consommation observée sur les six dernières heures, vous obtenez un délai avant saturation, bien plus actionnable. La fenêtre doit rester assez large pour absorber les variations : sur une heure, la moindre rotation de logs fausse la projection.
# Prédiction : disque plein dans combien de temps (secondes)(node_filesystem_avail_bytes / node_filesystem_size_bytes) / deriv(node_filesystem_avail_bytes[6h]) * -1Top 5 des consommateurs
Section intitulée « Top 5 des consommateurs »topk() recalcule son classement à chaque point de temps, ce qui fait apparaître et disparaître des séries au fil du graphique. Le filtre container!="" écarte les séries agrégées par cgroup que cAdvisor publie en parallèle des conteneurs, et qui feraient doublon dans le classement.
# Top 5 pods par mémoiretopk(5, container_memory_usage_bytes{container!=""})Requêtes avec offset
Section intitulée « Requêtes avec offset »Le modificateur offset décale la fenêtre d'évaluation vers le passé, ce qui permet de comparer le trafic actuel à celui de la semaine précédente. Cette comparaison ne vaut que si votre durée de rétention couvre le décalage demandé, sinon la requête renvoie un résultat vide.
# Différence avec il y a 1 semainerate(http_requests_total[5m]) - rate(http_requests_total[5m] offset 1w)Vector matching
Section intitulée « Vector matching »Quand vous combinez deux vecteurs, PromQL fait du matching par labels : il cherche, pour chaque série de gauche, la série de droite qui porte exactement les mêmes labels. Aucune correspondance ne signifie aucun résultat, sans message d'erreur, et c'est la cause numéro un des graphiques désespérément vides. Les modificateurs présentés ici servent à relâcher cette règle quand les deux métriques ne partagent pas le même jeu de labels.
One-to-one (défaut)
Section intitulée « One-to-one (défaut) »Chaque série d'un côté matche exactement une série de l'autre.
# Même labels des deux côtésmetric_a / metric_bIgnoring / on
Section intitulée « Ignoring / on »Spécifie quels labels utiliser pour le matching :
# Matcher uniquement sur le label "job"metric_a / on(job) metric_b
# Matcher en ignorant le label "instance"metric_a / ignoring(instance) metric_bGroup modifiers
Section intitulée « Group modifiers »Quand une série d'un côté doit servir à plusieurs séries de l'autre, PromQL refuse l'opération tant que vous n'avez pas indiqué quel côté porte la multiplicité. group_left désigne le membre de gauche comme celui qui contient plusieurs séries par correspondance, group_right fait l'inverse. L'usage classique consiste à enrichir des mesures avec les labels d'une métrique d'information comme kube_pod_info.
# Chaque série de B matchée à une série de Ametric_a / on(job) group_left metric_bBonnes pratiques
Section intitulée « Bonnes pratiques »Une requête juste peut rester coûteuse ou trompeuse. Les trois points qui suivent traitent des sujets qui reviennent systématiquement en production : nommer les métriques pour qu'elles soient devinables, éviter les erreurs de typage qui produisent des courbes crédibles mais fausses, et contenir le coût des requêtes. La cardinalité, c'est-à-dire le nombre de séries distinctes, est le facteur qui pèse le plus sur la mémoire de Prometheus.
Nommage des métriques
Section intitulée « Nommage des métriques »La convention de nommage n'est pas cosmétique : le suffixe d'unité et le suffixe de type indiquent au lecteur comment interroger la métrique sans consulter la documentation de l'exportateur. Prometheus attend des unités de base, donc des secondes et des octets, jamais des millisecondes ni des mégaoctets.
<namespace>_<name>_<unit>_<suffix>Exemples :
http_requests_total(counter)http_request_duration_seconds(histogram)node_memory_MemAvailable_bytes(gauge)
Éviter les pièges courants
Section intitulée « Éviter les pièges courants »Ces trois erreurs ne provoquent aucun message d'erreur : elles produisent un graphique qui s'affiche normalement, avec des valeurs fausses ou un serveur qui sature. Les onglets ci-dessous opposent la version fautive à sa correction.
❌ Mauvais : rate() sur un gauge
rate(node_memory_MemAvailable_bytes[5m]) # FAUX✅ Correct : deriv() pour un gauge
deriv(node_memory_MemAvailable_bytes[5m])❌ Potentiel NaN
rate(errors[5m]) / rate(requests[5m])✅ Protégé
rate(errors[5m]) / (rate(requests[5m]) > 0)ou
rate(errors[5m]) / rate(requests[5m]) or vector(0)❌ Cardinalité explosive
sum by(user_id) (http_requests_total) # Millions de séries✅ Agrégation raisonnable
sum by(endpoint, status) (http_requests_total)Performance
Section intitulée « Performance »Le coût d'une requête dépend du nombre de séries chargées et de la profondeur d'historique parcourue. Un tableau de bord rafraîchi toutes les quinze secondes rejoue ses requêtes en permanence : ce qui est acceptable en exploration manuelle devient un problème une fois affiché sur un écran mural. Les recording rules déplacent le calcul du moment de l'affichage vers le moment de l'ingestion.
- Utilisez les recording rules pour les requêtes complexes fréquentes
- Limitez la période :
[5m]est souvent suffisant - Agrégez tôt :
sum by(job) (rate(...))plutôt que calculs sur toutes les séries - Évitez
.*dans les regex : préférez des valeurs exactes
Cheatsheet
Section intitulée « Cheatsheet »Ce récapitulatif regroupe les expressions à garder sous la main, classées par intention. Remplacez metric, counter et histogram_bucket par vos propres noms de métriques ; le reste de la syntaxe se recopie tel quel.
# === COUNTERS ===rate(counter[5m]) # Taux par secondeincrease(counter[1h]) # Augmentation totaleirate(counter[5m]) # Taux instantané
# === AGRÉGATIONS ===sum(metric) # Totalavg(metric) # Moyennemax(metric) # Maximummin(metric) # Minimumcount(metric) # Nombre de sériessum by(label) (metric) # Grouper par label
# === PERCENTILES ===histogram_quantile(0.95, sum by(le) (rate(histogram_bucket[5m])))
# === TEMPS ===avg_over_time(metric[1h]) # Moyenne sur 1hmax_over_time(metric[24h]) # Max sur 24hpredict_linear(metric[6h], 3600) # Prédiction dans 1h
# === COMPARAISON ===metric > 80 # Filtremetric > bool 80 # Retourne 0/1topk(5, metric) # Top 5bottomk(3, metric) # Bottom 3
# === LABELS ===label_replace(metric, "new", "$1", "old", "(.*)")label_join(metric, "new", ",", "label1", "label2")À retenir
Section intitulée « À retenir »- rate() pour les counters, deriv() pour les gauges
- by() et without() contrôlent l'agrégation
- histogram_quantile() pour les percentiles
- Protégez les divisions contre le zéro
- Testez dans l'UI avant de mettre en alerte