7.5.3 Funktionen 2
Inhalt
Das Problem mit Instant vectors
Bisher haben wir uns ausschliesslich mit Instant vectors sowie deren Funktionen beschäftigt. Nun gibt es aber einige Funktionen, welche keine Instant Vectors akzeptieren. Eine dieser Funktionen ist beispielsweise delta()
. Sie berechnet den Unterschied innerhalb einer oder mehreren Time Series über einen bestimmten Zeitraum. Gehen wir zurück zu unserem Gedankenexperiment aus Kapitel 2. Wir hatten uns folgende Notizen gemacht:
temp_celsius{raum="wohnzimmer"} (1400, 21.2) (1410, 21.5) (1420, 21.8) (1430, 21.8) (1440, 21.7) (1450, 21.8)
temp_celsius{raum="schlafzimmer"} (1400, 18.1) (1410, 18.1) (1420, 18.4) (1430, 18.3) (1440, 18.8) (1450, 18.9)
Uns interessiert das Delta (die Differenz) der Temperatur im Wohnzimmer zwischen 14:00 und 14:50. Wenn wir diese händisch berechnen möchten, ziehen wir die Temperatur um 14:00 von der Temperatur um 14:50 ab (oder umgekehrt) und erhalten damit 0.6
(21.8 - 21.2 = 0.6
). Wie du siehst, benötigen wir für die Berechnung zwei Werte. Das Problem ist, dass ein Instant vector jeweils nur den aktuellsten Wert jeder Time Series zurückgibt. Wir könnten zwar ein Delta zwischen zwei Räumen an einem definierten Zeitpunk berechnen (z.B. temp_celsius{raum="wohnzimmer"} - temp_celsius{raum="schlafzimmer"}
) aber eben nicht das Delta eines Raums vom aktuellen Zeitpunkt zu einem früheren Zeitpunkt.
Range vector
Um dieses Problem zu lösen, existieren Range vectors. Im Gegensatz zu Instant vectors liefern diese die Samples (Messungen) von einer oder mehreren Time Series über einen bestimmten Zeitraum zurück. Im PromQL werden Range vectors mit der Syntax [<range>]
definiert, wobei <range>
mit der gewünschten Dauer ersetzt werden muss (z.B. [5m]
für 5 Minuten). Anstelle von m
für Minuten können weitere Einheiten angegeben werden:
ms
: Millisekundens
: Sekundenh
: Stundend
: Tagew
: Wocheny
: Jahre
Ein Query für einen Range vector könnte z.B. so aussehen:
prometheus_http_requests_total{handler="/", code="200"}[1m]
Wie du siehst, erhalten wir eine einzige Time Series, diese hat jedoch vier Values, welche jeweils mit einem Zeitstempel versehen sind. Das scrape_interval
von 15 Sekunden erklärt auch, warum wir über einen Zeitraum von einer Minute 4 Samples erhalten.
Funktionen
Wir lernen nun Funktionen kennen, welche mit Range Vectors arbeiten. Während Funktionen für Instant vectors eine oder mehrere Time Series an einem definierten Zeitpunkt aggregieren (avg(temp_celsius)
= Durchschnittstemperatur aller Räume zum aktuellen Zeitpunkt), aggregieren Funktionen mit Range vectors innerhalb jeder Time Series über die im Range vector selector angegebene Zeit. Das Ergebnis einer Range vector Funktion besteht somit aus gleich vielen Time Series, wie der Funktion initial übergeben wurden.
Im Kapitel drei haben wir gelernt, dass unterschiedliche Metrik Typen (gauges
, counter
, histogram
und summary
) existieren. Bevor wir auf die nächsten Funktionen eingehen, müssen wir uns nochmals mit dieser Thematik beschäftigen. Alle der in diesem Unterkapitel beschriebenen Funktionen sind darauf ausgelegt, mit einem bestimmten Metriktyp genutzt zu werden. Verwenden wir den falschen Typ, wird das Resultat fehlerhaft sein. Prometheus wird die Berechnung zwar durchführen und u.U. auch keinen Fehler / keine Warnung anzeigen, das Resultat der Berechnung wird aber in den meisten Fällen falsch sein.
Wir greifen etwas vor und schauen uns die erste unten beschriebene Funktion increase()
an. Diese Funktion berechnet den Anstieg einer oder mehrerer Time Series über einen bestimmten Zeitraum. Diese Funktion kann ausschliesslich mit Counter Metriken umgehen, da nur Counter ausschliesslich ansteigen können. Nehmen wir als Beispiel folgende Time Series:
prometheus_http_requests_total{job="prometheus", handler="/", code="200"} (1400, 0) (1410, 5) (1420, 6) (1430, 17) (1440, 20) (1450, 25)
Bei dieser Metrik prometheus_http_requests_total
handelt es sich um den Typ Counter
. Wir erkennen das am _total
Suffix im Namen der Metrik. Um 14:52 führen wir folgendes Query aus:
increase(prometheus_http_requests_total{job="prometheus", handler="/", code="200"}[30m])
Durch den Range vector selector von 30 Minuten ([30m]
) selektieren wir folgende Samples:
prometheus_http_requests_total{job="prometheus", handler="/", code="200"} (1430, 17) (1440, 20) (1450, 25)
Das erste Sample hat den Value 17
, das letzte Sample hat den Value 25
. Diese Metrik hat sich somit innerhalb der letzten 30 Minuten um 8
erhöht. Nun führen wir das gleiche Query erneut aus, diesmal sieht die Time Series wie folgt aus:
prometheus_http_requests_total{job="prometheus", handler="/", code="200"} (1430, 17) (1440, 3) (1450, 5)
Wie du siehst, hat sich der Sample Value zwischen 14:30 und 14:40 verringert. Da es sich bei dieser Metrik um einen Counter
handelt und sich dieser grundsätzlich nur nach oben bewegen kann, wissen wir, dass ein Counter Reset
stattgefunden hat. Das kann z.B. passieren, wenn ein Server während der betroffenen Zeit rebootet wurde. Da die Funktion increase()
aber nur für Counter
Metriken verwendet wird, kann Prometheus die Samples entsprechend korrigieren. Er nimmt dazu den Sample Wert vor dem Reset (in unserem Fall 17
) und addiert den Wert nach dem Reset zu diesem hinzu (17 + 3
). Das Gleiche wird für sämtliche Values nach dem Reset wiederholt. Die korrigierte Time Series sieht daher wie folgt aus:
prometheus_http_requests_total{job="prometheus", handler="/", code="200"} (1430, 17) (1440, 20) (1450, 25)
Das Resultat ist damit wieder bei 8
.
Nun verwenden wir increase()
fälschlicherweise mit folgender Gauge
Metrik:
temp_celsius{raum="wohnzimmer"} (1430, 18.3) (1440, 18.1) (1450, 18.4)
Wir führen nun um 14:52 folgendes Query aus:
increase(temp_celsius{raum="wohnzimmer"}[30m])
Da wir increase()
verwenden, geht Prometheus davon aus, dass es sich bei der Metrik um einen Counter handelt. Er deutet daher die Abnahme des Sample Values zwischen 14:30 und 14:40 als counter reset. Die “korrigierte” Time Series sieht wie folgt aus:
temp_celsius{raum="wohnzimmer"} (1430, 18.3) (1440, 36.4) (1450, 36.7)
Das Resultat dieser Berechnung wäre somit 18.4
, was nicht korrekt ist. Anstelle von increase()
muss für Gauge
Metriken die Funktion delta()
verwendet werden. Diese deutet einen abnehmenden Sample Value nicht als Counter Reset und kann sowohl positive, als auch negative Resultate zurückgeben. Funktionen für Counter können ausschliesslich 0 oder positive Zahlen zurückgeben.
Die Funktionen benötigen für die Berechnung immer min. zwei Samples. Es ist daher wichtig, den Range vector selector so zu wählen, dass immer min. 2 Samples zurückgegeben werden. Ist dies nicht der Fall, wird das PromQL Query kein Resultat zurückgeben. Nehmen wir als Beispiel nochmals folgende Time Series:
temp_celsius{raum="wohnzimmer"} (1430, 18.3) (1440, 18.1) (1450, 18.4)
Anschliessend führen wir um 14:52 folgendes Query aus:
increase(temp_celsius{raum="wohnzimmer"}[5m])
Durch den Range Vector Selector von 5 Minuten selektieren wir alle Samples zwischen 14:47 und 14:52:
temp_celsius{raum="wohnzimmer"} (1450, 18.4)
In diesem Beispiel haben wir nur noch ein Sample, was nicht ausreichend ist. Grundsätzlich sollte als Range vector selector immer min. 3x das scrape_interval
von Prometheus gewählt werden.
Funktionen für Counter-Metriken
increase()
Diese Funktion berechnet den Anstieg eines Counters über einen definierten Zeitraum.
Beispiel:
increase(prometheus_http_requests_total{job="prometheus", handler="/", code="200"}[30m])
Das Ergebnis ist die Anzahl der HTTP Requests, welche innerhalb der letzten 30 Minuten auf den Endpunkt /
durchgeführt und mit dem HTTP Status Code 200
beantwortet wurden.
rate()
Diese Funktion ist der increase()
Funktion sehr ähnlich. Sie berechnet den Anstieg eines Counters über einen definierten Zeitraum pro Sekunde. Grundsätzlich wird also die gleiche Berechnung durchgeführt, wie bei increase()
, das Resultat wird aber am Schluss noch durch die Anzahl Sekunden im Range vector Selector geteilt.
Beispiel:
rate(prometheus_http_requests_total{job="prometheus", handler="/", code="200"}[30m])
Berechnet, wie viele Requests pro Sekunde durchschnittlich über die letzten 30 Minuten auf den Endpunkt /
durchgeführt und mit dem HTTP Status Code 200
beantwortet wurden.
Funktionen für Gauge-Metriken
delta()
Diese Funktion ist das Gauge
Äquivalent zur increase()
Funktion. Sie berechnet den Unterschied innerhalb einer oder mehrere Time Series über den angegebenen Range Vector Selector.
Beispiel:
delta(temp_celsius{raum="wohnzimmer"}[30m])
Berechnet den Temperaturunterschied im Wohnzimmer über die letzten 30 Minuten. Das Resultat kann sowohl positiv (wenn die aktuelle Temperatur höher ist als vor 30 Minuten) als auch negativ (wenn die aktuelle Temperatur tiefer ist als vor 30 Minuten) sein.
deriv()
Diese Funktion verhält sich ähnlich wie die rate()
Funktion. Sie berechnet den pro Sekunde An- oder Abstieg einer Metrik.
Beispiel:
deriv(temp_celsius{raum="wohnzimmer"}[30m])
Dieses Query berechnet, um wie viel Grad pro Sekunde sich die Temperatur im Wohnzimmer über die letzten 30 Minuten verändert hat.
PromQL Dokumentation
Du fragst dich an dieser Stelle vielleicht, woher man wissen soll, welche Funktionen mit welchen Metriktypen (Gauge
, Counter
usw.) verwendet werden müssen und welche Input Vektoren (Instant vector oder Range vector) unterstützt werden. Hierbei kann die Prometheus Dokumentation
helfen. In der Beschreibung der einzelnen Funktionen wird in Klammern der unterstützte Input Vektor angegeben:
rate(v range-vector)
sort(v instant-vector)
Für welchen Metriktyp die Funktion verwendet werden soll, geht meist ebenfalls aus der Beschreibung hervor.
Fazit
- In diesem Kapitel haben wir Range vectors kennengelernt. Im Unterschied zu Instant vectors selektieren Range vectors mehrere Samples einer oder mehrerer Time Series.
- Der Range vector selector sollte immer so gewählt sein, dass min. zwei Samples zurückgegeben werden.
- Wir haben gelernt, dass es für
Counter
undGauges
separate Funktionen gibt und es wichtig ist, die auf den jeweiligen Typ passende Funktion zu verwenden.
Im nächsten Unterkapitel schauen wir uns an, wie wir Funktionen kombinieren können.