Observability ~4 Min.

PromQL und RED Method

Rate, Errors, Duration — Queries für Platform-Services.

von PlatPrep

Die RED Method ist schnell erklärt: Rate, Errors, Duration. Wie viele Requests bearbeitet der Service pro Sekunde? Wie viele davon schlagen fehl? Wie lang dauern sie im Median und im 95. Perzentil?

Klingt trivial. Wird trotzdem oft falsch umgesetzt.

Das häufigste Problem: Metriken werden gesammelt, aber nicht mit den richtigen Labels. Ein http_requests_total ohne status_code-Label ist für Error-Rate-Berechnung kaum nützlich. Ein Duration-Histogram ohne ausreichend Buckets zeigt kein sinnvolles p99.

Rate und Errors: die Basics, an denen man trotzdem scheitert

Konkrete PromQL für Rate, summiert über alle Instanzen:

sum(rate(http_requests_total{job="checkout"}[5m]))

Zwei Stolperfallen stecken schon hier. Erstens: rate() braucht mindestens zwei Datenpunkte im Zeitfenster — bei einem Scrape-Intervall von 30 Sekunden ist [1m] zu knapp, [5m] ist der übliche Kompromiss zwischen Glättung und Reaktionszeit. Zweitens: rate() gehört innerhalb von sum(), nie umgekehrt. rate(sum(...)) ist nicht nur semantisch falsch — Counter-Resets einzelner Instanzen (Pod-Restarts) verfälschen dann die aggregierte Reihe.

Error Rate als Anteil der 5xx-Antworten:

sum(rate(http_requests_total{job="checkout", status=~"5.."}[5m]))
/
sum(rate(http_requests_total{job="checkout"}[5m]))

Das ist das Format für einen SLO-Indicator. Ein Randfall, der in Produktion auftaucht: wenn es im Zeitfenster keine 5xx-Antworten gab, existiert die Zähler-Zeitreihe womöglich gar nicht und die Query liefert nichts statt 0. Für Dashboards lässt sich das mit or vector(0) auf dem Zähler abfangen — für Alerts ist „keine Daten” ein eigener Zustand, den man bewusst behandeln sollte.

Duration: Histogramme, nicht Summaries

Für Latenz-Perzentile sind Histogramme das richtige Instrument, keine Summary-Metriken. Der Grund ist fundamental: Histogramm-Buckets sind einfache Counter und lassen sich über Instanzen aufsummieren. Vorberechnete Summary-Quantile lassen sich mathematisch nicht aggregieren — der Durchschnitt von zehn p95-Werten ist nicht das p95 der Gesamtheit.

histogram_quantile(0.95,
  sum(rate(http_request_duration_seconds_bucket{job="checkout"}[5m])) by (le)
)

Das by (le) ist nicht optional — ohne Grouping über das Bucket-Label funktioniert histogram_quantile nicht korrekt.

Was man dabei wissen muss: das Ergebnis ist eine lineare Interpolation innerhalb des Buckets, in dem das Perzentil liegt. Sind die Buckets schlecht gewählt — etwa der größte explizite Bucket bei 1 Sekunde, während dein p99 bei 3 Sekunden liegt — zeigt die Query schlicht die Bucket-Grenze an und du merkst es nicht. Bucket-Grenzen müssen zur erwarteten Latenzverteilung des Services passen, idealerweise mit feiner Auflösung rund um deine SLO-Schwelle. Wenn dein Latenz-SLO bei 300 ms liegt, brauchst du einen Bucket bei genau 300 ms — sonst misst du dein SLO gegen eine interpolierte Schätzung.

Labels: der Unterschied zwischen nützlich und teuer

Bei den Labels lauert die zweite klassische RED-Falle: Kardinalität. Jede einzigartige Label-Kombination ist eine eigene Zeitreihe, und Prometheus’ Memory-Verbrauch skaliert direkt damit. Ein path-Label mit rohen URLs (/users/12345/orders/67890) erzeugt für jeden User eine neue Zeitreihe — ein Service mit moderatem Traffic produziert so Hunderttausende Serien, und Prometheus kippt. Die Regel: Pfade auf Route-Templates normalisieren (/users/:id/orders/:id), niemals User-IDs, Session-IDs oder Request-IDs in Labels. Was du pro Request wissen willst, gehört in Traces oder Logs, nicht in Metrik-Labels. Ein Blick auf prometheus_tsdb_head_series im Zeitverlauf zeigt, ob ein Deployment gerade eine Kardinalitäts-Explosion eingeschleppt hat.

Recording Rules: Pflicht, sobald Dashboards geteilt werden

Die obigen Queries bei jedem Dashboard-Load über 30 Tage live zu berechnen, ist teuer — histogram_quantile über zehntausende Bucket-Zeitreihen kann mehrere Sekunden dauern. Recording Rules berechnen sie einmal pro Evaluationsintervall vor:

groups:
  - name: red-checkout
    rules:
      - record: job:http_request_error_ratio:rate5m
        expr: |
          sum(rate(http_requests_total{job="checkout", status=~"5.."}[5m]))
          /
          sum(rate(http_requests_total{job="checkout"}[5m]))

Die Namenskonvention level:metric:operations ist verbreitet und macht auf einen Blick lesbar, was aggregiert wurde. Dashboards und Alerts greifen dann auf die vorberechnete Reihe zu — Ladezeiten von Sekunden auf Millisekunden, und die Prometheus-Query-Last sinkt spürbar. Für SLO-Berechnungen über lange Zeiträume sind Recording Rules ohnehin Voraussetzung.

RED ohne SLO ist Metrik ohne Urteil

Was beim RED-Setup gern vergessen wird: die Baseline kennen. Eine Error-Rate von 0,1 Prozent klingt gut — aber ist sie für deinen Service gut? Manche Services haben saisonal höhere Fehlerquoten, andere strukturelle Background-Errors, die als normal gelten (Client-Abbrüche, Bot-Traffic auf 404). Erst die SLO-Definition legt fest, was akzeptabel ist: „99,9 % der Requests erfolgreich, p95 unter 300 ms, gemessen über 30 Tage.” RED liefert die Indikatoren, das SLO das Urteil — wie aus beidem zusammen ein Error Budget und eine Deployment-Policy werden, steht in SLOs und Error Budgets in der Praxis.

Die PromQL-Grundlagen von Counter, Gauge und Histogram bis zu Alert-Regeln kannst du im Prometheus-Lernpfad systematisch durcharbeiten.