PromQL: Einführung

PromQL steht als Abkürzung für Prometheus Query Language und erlaubt es uns die von Prometheus erhobenen und gespeicherten Metriken abzufragen und zu analysieren.

Arten von Metriken

Hierzu muss man zunächst zwischen verschiedenen Arten von Metriken unterscheiden können, denn verschiedene Metriken erlauben verschiedenartige Analysen und Operationen:

  • Gauge (Pegelstand): Hierbei handelt es sich um Momentaufnahmen, die den Stand zu einem bestimmten Zeitpunkt festhalten. Diese Metriken sind für sich alleine wenig hilfreich. Ihr Nutzen entsteht dadurch, dass man sie aggregiert, d.h. statistische Auswertungen mit ihnen anstellt oder die Metrik im zeitlichen Verlauf analysiert. Die Namen solcher Metriken enden oft auf eine Einheit wie _bytes oder _seconds.
    • Beispiel: node_filesystem_size_bytes, die Grösse eines Dateisystems in Bytes.
  • Counter (Zähler): Hiermit werden Werte aufsummiert, wodurch die Metrik im zeitlichen Verlauf immer ansteigt. Interessanter als der absolute Wert, der für sich alleine schon eine aussagekräftige Kennzahl sein kann, ist die Wachstumsrate, die sich über eine bestimmte Zeitspanne berechnen lässt. Wendet man eine Ratenfunktion auf einen Zähler an, erhält man einen Pegelstand. Die Namen von Zählern haben oftmals die Endung _total.
    • Beispiel: node_network_receive_bytes_total, die Gesamtzahl der übers Netzwerk empfangenen Bytes.
  • Summary (Statistiken): Diese Metriken trifft man i.d.R. paarweise an; die eine mit der Endung _sum, die andere mit der Endung _count. Im Gegensatz zu Zählern ist nicht nur die Gesamtsumme und deren Zustandekommen von Interesse, sondern auch wie viele Ereignisse zu dieser Gesamtsumme beitragen. Wie ein Zähler lassen sich diese Metriken über die Anwendung einer Rate in einen Pegelstand umwandeln.
    • Beispiel: http_response_size_bytes_sum und http_response_size_bytes_count geben an, wie wiele HTTP-Antworten Prometheus abgeliefert hat und wie gross diese waren.
  • Histogram (Histogramme): Diese Art von Metrik gruppiert Messwerte in sogenannte Buckets (Eimer), wodurch sich Erkenntnisse über die Verteilung der Daten gewinnen lassen. Für sie stehen spezielle Statistikfunktionen zur Verfügung. Die Namen der Metriken haben das Suffix _bucket.
    • Beispiel: prometheus_http_request_duration_seconds_bucket gibt die Verteilung der HTTP-Antwortzeiten von Prometheus mit Grenzwerten an.

Hierzu ein Überblick zum Nachschlagen:

ArtBedeutungSuffixeBeispiel
GaugeMomentaufnahme_[Einheit]node_filesystem_size_bytes
Counterkumulierte Werte_totalnode_network_receive_bytes_total
SummaryStatistiken_sum & _counthttp_response_size_bytes_[sum/count]
Histogrammgruppierte Messwerte_bucketprometheus_http_request_duration_seconds_bucket

Eine gute Informationsquelle für die Bedeutung einzelner Metriken ist Promhub.

Arten von Labels

Zu jeder Metrik wird neben dem Messwert und dem Zeitstempel eine Reihe von Labels miterhoben. Hierbei ist es wichtig, zwischen zwei Arten von Labels zu unterscheiden – denn PromQL behandelt diese exakt gleich:

  1. Target Labels: Diese Labels markieren, was gemonitored wird, d.h. speichern das Monitoring-Ziel. Die Labels job und instance, die sich in der Prometheus-Konfiguration finden lassen, geben an, welches System gemonitored wird. Sie dienen der Unterscheidung der Objekte, die unter Überwachung stehen.
  2. Instrumentation Labels: Hierbei handelt es sich um Kontextinformationen, die spezifisch für die jeweiligen Metriken sind. Die Metrik node_filesystem_size_bytes verfügt beispielsweise über die Angaben device (Gerät), mountpoint (Einhängepunkt) und fstype (Dateisystemtyp).

Für Aggregationen ist es nun wichtig, dass man nach gewissen Labels unterscheidet – und nach anderen nicht. Ein Beispiel illustriert dies am einfachsten.

Die folgende Abfrage bildet die Summe der Dateisystemgrössen:

sum without(job, instance)(node_filesystem_size_bytes)

Im Ergebnis wird nicht mehr nach den Targets (d.h. nach den Servern) unterschieden, sondern nur nach den verschiedenen Arten von Dateisystemen.

ElementValue
{device="/dev/vda1",fstype="ext4",mountpoint="/"}20712292352
{device="/dev/vda15",fstype="vfat",mountpoint="/boot/efi"}259436544
{device="tmpfs",fstype="tmpfs",mountpoint="/tmp"}487276544

Im Gegensatz dazu wird in der nächsten Abfrage wiederum die Summe der Dateisystemgrössen ermittelt:

sum without(device,fstype,mountpoint)(node_filesystem_size_bytes)

Im Ergebnis wird nun nicht mehr nach den Metrik-Kontextinformationen (d.h. nach den Dateisystemen) unterschieden, sondern nur nach den einzelnen Instanzen:

ElementValue
{instance="159.100.250.46:9100",job="epimetheus"}10729502720
{instance="localhost:9100",job="localhost"}10729502720

Beide Server verfügen also in der Summe über exakt gleich grosse Dateisysteme.

Das Gegenteil von without ist by, welches aussagt, welche Labels beibehalten werden sollen:

sum by(device,fstype,mountpoint)(node_filesystem_size_bytes)
sum without(job, instance)(node_filesystem_size_bytes)

Diese beiden Abfrage sind äquivalent.

Abfragetechniken

PromQL ist eine umfassende Abfragesprache, die verschiedene Techniken wie Filterung, Zeitfensterbildung, Berechnungen und Funktionsanwendungen unterstützt. Nicht jede Abfragetechnik lässt sich auf alle Arten von Metriken anwenden.

Filterung

Alle Metriken lassen sich anhand ihrer Labels filtern. Hierzu verwendet man Label-Wert-Paare in geschweiften Klammern, die einer Metrik hintenangestellt werden, z.B. {device="eth0"}.

Folgende Abfrage liefert die Grösse sämtlicher Dateisysteme auf allen Targets zurück:

node_filesystem_size_bytes

Folgende Abfrage schränkt nach dem Dateisystemtyp (fstype) ext4 ein:

node_filesystem_size_bytes{fstype="ext4"}

Dies erlaubt einen Vergleich aller ext4-Dateisysteme über die verschiedenen Server.

Folgende Abfrage schränkt nach einer bestimmten Instanz (instance) ein:

node_filesystem_size_bytes{instance="159.100.250.46:9100"}

Dies erlaubt einen Vergleich aller Dateisysteme auf der jeweiligen Instanz.

Zur genauen Recherche lassen sich auch mehrere Filter kombinieren:

node_filesystem_size_bytes{instance="159.100.250.46:9100",fstype="ext4"}

Die verschiedenen Kriterien werden mittels UND-Logik verknüpft.

Neben der Gleicheit (=) lässt sich auch auf Ungleichheit (!=) sowie auf das Passen (=~) bzw. Nichtpassen (!~) auf einen regulären Ausdruck prüfen.

Zeitfenster und Raten

Counter- und Summary-Metriken lassen sich zur Auswertung in Zeitfenster unterschiedlicher Grössen aufteilen, auf welche sich dann Raten mit der rate-Aggregierung berechnen lassen. Zeitfenster werden als Suffix in eckigen Klammern mit einem Zeitausdruck angegeben, z.B. [5m].

Der Zähler node_network_receive_bytes_total gibt die Anzahl der empfangenen Bytes zurück. Diese Metrik ist wenig aussagekräftig. Betrachtet man aber die Wachstumsrate, lassen sich Erkenntnisse über die Netzwerkauslastung gewinnen:

rate(node_network_receive_bytes_total[5m])

Die 5m bedeuten hier, dass eine Momentaufnahme der letzten 5 Minuten zurückgegeben werden soll. Es lassen sich u.a. folgende Suffixe verwenden:

  • s: Sekunden
  • m: Minuten
  • h: Stunden
  • d: Tage

Bei Summary-Metriken ist oftmals das Verhältnis zwischen der Summe und der Anzahl von Interesse:

sum (rate(prometheus_http_response_size_bytes_sum[5m]))
/
sum (rate(prometheus_http_response_size_bytes_count[5m]))

Damit wird die durchschnittliche Antwortgrösse in den letzten 5 Minuten errechnet.

Möchte man nur die Metriken einer gerade vergangenen Zeitperiode betrachten, lässt sich dies mit der Funktion last_over_time bewerkstelligen:

last_over_time(promhttp_metric_handler_requests_total[4h])

Die obige Abfrage gibt die Anzahl der Prometheus-Metric-Requests der letzten vier Stunden (4h) zurück.

Quantile

Für Histogramme lassen sich die sogenannten Quantile ermitteln. Ein Beispiel: Die Histogramm-Metrik go_gc_heap_allocs_by_size_bytes_bucket sagt aus, wie viel Bytes Arbeitsspeicher pro Allokation reserviert werden. Das neunzigste Quantil dieser Metrik ermittelt nun einen Grenzwert mit folgender Aussagekraft:

  • 90% der Allokationen reservieren weniger Speicher.
  • 10% der Allokationen reservieren mehr Speicher.

Das Quantil lässt sich über die histogram_quantile-Funktion ermitteln:

histogram_quantile(0.9, go_gc_heap_allocs_by_size_bytes_bucket)

Dadurch lassen sich Extremwerte ermitteln. Den Median errechnet man über das fünfzigste Quantil:

histogram_quantile(0.5, go_gc_heap_allocs_by_size_bytes_bucket)

Aggregationen

Auf Momentaufnahmen und extrahierte Raten von Zählern und Statistiken lassen sich Aggregationsfunktionen wie die folgenden anwenden:

FunktionBedeutung
sumSumme
countAnzahl verschiedener Ergebnisse
avgarithmetisches Mittel
minMinimum (kleinster Wert)
maxMaximum (grösster Wert)
quantileBestimmtes Quantil

Hierzu einige Anwendungsbeispiele:

sum(go_gc_duration_seconds) # Summe der Sekunden für die Garbage Collection
count(node_systemd_units) # Anzahl Ergebnisse
avg(node_forks_total) # Durchschnittliche Anzahl von Forks
min(node_filesystem_size_bytes) # kleinstes Dateisystem
max(node_filesystem_size_bytes) # grösstes Dateisystem
quantile(0.5, node_filesystem_size_bytes) # mittlere Dateisystemgrösse

Operatoren: arithmetische und logische

Abfrageergebnisse lassen sich mit arithmetischen und logischen Funktionen weiterverarbeiten. Die folgende Abfrage rechnet die Dateisystemgrösse von Bytes in GB um:

node_filesystem_size_bytes{instance="159.100.250.46:9100",fstype="tmpfs"} / (1024*1024)

Es stehen u.a. folgende Rechenoperationen zur Verfügung:

OperatorBedeutung
+Addition
-Subtraktion
*Multiplikation
/Division
%Modulo
^Potenzierung

Die folgende Abfrage gibt nur Dateisysteme zurück, die mindestens 1GB gross sind:

node_filesystem_size_bytes / (1024^2) > 1024

Es stehen u.a. folgende logische Operatoren zur Verfügung:

OperatorBedeutung
==Gleichheit
!=Ungleichheit
>grösser als
<kleiner als
>=grösser gleich
<=kleiner gleich

Funktionen

Operatoren werden oft zusammen mit Funktionsergebnissen verwendet. Der aktuelle Zeitstempel lässt sich beispielsweise mit time() ermitteln. Auf frühere Zeitpunkte gelangt man, indem man von diesem Ergebnis subtrahiert:

time() - 24*60*60 # die aktuelle Uhrzeit gestern

Informationen über das aktuelle Datum enthält man über die folgenden Funktionen:

FunktionRückgabewert
year()aktuelles Kalenderjahr
month()aktueller Kalendermonat
day_of_month()aktueller Kalendertag
day_of_week()aktueller Wochentag
day_of_year()aktueller Jahrestag
days_in_month()Anzahl Tage im aktuellen Monat
hour()aktuelle Stunde
minute()aktuelle Minute

Solche Abfragetechniken lassen sich kombinieren, um sehr spezifische Erkenntnisse über das System zu gewinnen. Die folgende Abfrage ermittelt die Anzahl der CPU-Sekunden, die für den Benutzer und das System in den letzten dreissig Minuten auf dem Node localhost verwendet worden sind:

rate(node_cpu_seconds_total{mode=~"user|system",instance="localhost:9100"}[30m])
  • Die Abfrage basiert auf der Metrik node_cpu_seconds_total.
  • Es wird nach den Modi user und system gefiltert, wozu ein regulärer Ausdruck verwendet wird.
  • Weiter wird auf die Instanz localhost:9100 gefiltert.
  • Es wird die Rate der letzten dreissig Minuten berücksichtigt.