Cela est dû au fait que Prometheus privilégie une définition cohérente de ce qu'est une plage plutôt que la précision. C'est-à-dire qu'il définit toujours une plage comme étant tous les échantillons se situant dans l'intervalle (inclusif) [now() - plage, now()]. Cette définition a tout son sens pour les jauges : si vous voulez calculer une avg_over_time()
avec une plage temporelle égale à l'intervalle, vous souhaitez que chaque échantillon d'entrée soit inclus dans le calcul d'un seul échantillon de sortie.
Mais il en est autrement pour les compteurs. Avec une plage temporelle égale à l'intervalle, une valeur d'entrée (c'est-à-dire l'augmentation entre deux échantillons successifs) est essentiellement jetée. (Voir les problèmes de Prometheus #3746 et 3806 pour BEAUCOUP plus de détails.) Pour compenser les données qu'il rejette, Prometheus utilise l'extrapolation pour ajuster le résultat du calcul.
Cela signifie que si (comme dans votre cas) vous utilisez une plage temporelle qui est 2 fois votre intervalle de grattage (1m
de plage pour un intervalle de grattage de 30s
), Prometheus trouvera (en moyenne) 2 échantillons dans chaque plage, mais la plage temporelle réelle couverte par ces 2 échantillons sera d'environ 30s
. Ainsi, Prometheus extrapolera la valeur pour obtenir le 1m
demandé en doublant la valeur. D'où le résultat de 2 au lieu de l'attendu 1. Vous remarquerez également que parce que certaines augmentations entre des échantillons successifs sont rejetées (même si aucun échantillon n'est rejeté), toutes les augmentations de votre compteur n'apparaissent pas dans votre graphique rate()
. (C'est-à-dire qu'il n'y a pas de saut dans le rate()
correspondant à la troisième augmentation du compteur. Si vous actualisez à différents moments, différentes augmentations apparaîtront et disparaîtront. Grafana a "résolu" ce dernier problème en alignant toujours les plages demandées avec l'intervalle et en manquant ainsi systématiquement les mêmes augmentations.)
La solution suggérée par les développeurs de Prometheus est de calculer des taux sur des durées plus longues. Mais tout cela ne fait que réduire l'erreur (vous obtenez 1,5 avec un facteur 3, 1,33 avec un facteur 4, 1,25 pour un facteur 5, etc.), sans jamais s'en débarrasser. L'extrapolation de Prometheus est suffisamment bien cachée par les compteurs qui augmentent de manière régulière, mais elle est flagrante avec des compteurs comme le vôtre, qui augmentent rarement.)
La seule solution de contournement pour ce problème (à part corriger Prometheus, pour lequel j'ai soumis une PR et je maintiens un fork) est de rétro-ingénierie de l'implémentation de rate()
de Prometheus. C'est-à-dire qu'en supposant un intervalle de grattage de 30s
, une expression comme rate(foo[1m])
doit être remplacée par :
rate(foo[90s]) * 60 / 90
ou de manière plus générale (notez que l'expression entre crochets doit être une durée temporelle littérale, elle ne peut pas être un calcul)
rate(foo[intended_range + scrape_interval]) * intended_range / (intended_range + scrape_interval)
La raison pour laquelle cela fonctionne est que la plage intended_range + scrape_interval
vous donnera suffisamment d'échantillons pour couvrir les augmentations sur intended_range
, ce qui est ce que vous souhaitez. Mais ensuite, vous devez annuler le changement introduit par l'extrapolation de Prometheus, d'où la multiplication et la division qui suivent. C'est un hack peu élégant et dépend de vous connaissant votre intervalle de grattage et de le coder en dur dans vos règles d'enregistrement et/ou vos requêtes Grafana.
À noter que quelle que soit la méthode utilisée, vous n'obtiendrez probablement pas une valeur exacte de 1. En raison des services, du réseau et de la latence interne de Prometheus, les échantillons ne seront généralement pas alignés sur la milliseconde, de sorte que le taux d'augmentation par seconde sera légèrement inférieur ou légèrement supérieur à la valeur attendue.