Der zweite von zwei Spickzetteln zum Seminar Observability mit OpenTelemetry, Prometheus und Grafana: alles ab dem Backend. Was davor liegt — Instrumentierung, Collector, Sampling — steht auf OpenTelemetry-Instrumentierung.
Maßgeblich sind prometheus.io und grafana.com/docs. Dieses Blatt hält die Regeln fest, die man beim Abfragen und Alarmieren immer wieder braucht — vor allem die, deren Verletzung ein plausibles, aber falsches Ergebnis liefert.
Pull-Modell: Begriffe und Wege hinein
| Begriff | Bedeutung | Beispiel |
|---|---|---|
| Target | eine abzufragende Adresse | eine Instanz eines Dienstes |
| Job | Gruppe gleichartiger Targets | pfandwerk-annahme |
| Instance | einzelnes Target im Job | annahme-7c4f |
| Label | Dimension der Zeitreihe | filiale, materialart |
Service Discovery hält die Zielliste aktuell; statische Listen veralten in dynamischen Umgebungen sofort. Für kurzlebige Aufträge gibt es das Push Gateway — für dauerhafte Dienste ist es Zweckentfremdung.
| Weg | Geeignet für | Zu beachten |
|---|---|---|
| Exporter | Systeme ohne eigene Instrumentierung | eigener Prozess je System |
| Collector | einheitlicher Weg für alle Signale | eine Komponente mehr |
| OTLP direkt | reine OpenTelemetry-Umgebungen | Namensumsetzung beachten |
Namen und Labels ändern sich beim Übergang von OpenTelemetry: Punkte werden
standardmäßig zu Unterstrichen, Typ und Einheit werden angehängt · service.name
und service.instance.id werden zu job und instance · übrige
Resource-Attribute landen in einer eigenen Metrik target_info · ausgewählte
Attribute lassen sich gezielt zu Labels befördern. Wer target_info übersieht,
vermisst Umgebungsangaben, die längst da sind.
Modul: Prometheus-Architektur und Datenerfassung
Metriknamen und Kardinalität
| Metrikname | Typ | Misst |
|---|---|---|
pfandwerk_behaelter_angenommen_total | Counter | angenommene Behälter |
pfandwerk_pruefung_dauer_seconds | Histogram | Dauer der Materialprüfung |
pfandwerk_automat_fuellstand_ratio | Gauge | Füllstand des Automaten |
pfandwerk_build_info | Gauge | Version als Metadaten |
Eine Zeit in Millisekunden ist kein Detail — sie bricht jede Rechnung, die Basiseinheiten erwartet. Ein Name muss über alle Labeldimensionen dasselbe messen; nur dann sind Summe und Mittelwert über alle Labelwerte sinnvoll.
Kardinalität ist ein Produkt, keine Summe:
| Label | Werte | Wirkung |
|---|---|---|
dienst | 3 | unkritisch |
filiale | 40 | 120 Zeitreihen, tragbar |
materialart | 4 | 480 Zeitreihen, tragbar |
bon_nummer | unbegrenzt | unbrauchbar, sprengt den Speicher |
Drei harmlose Labels ergeben gemeinsam ein Problem. Was als Label nicht taugt, gehört oft als Span-Attribut in den Trace — dort kostet es nicht dasselbe.
Modul: Metrikdesign und Kardinalität
PromQL: Selektoren, Raten, Quoten
| Operator | Bedeutung | Beispiel |
|---|---|---|
= | genau gleich | dienst="pfandwerk-bon" |
!= | ungleich | ergebnis!="erfolg" |
=~ | regulärer Ausdruck | dienst=~"pfandwerk-.*" |
!~ | negierter Ausdruck | filiale!~"test.*" |
Reguläre Ausdrücke sind vollständig verankert — ein Teiltreffer genügt nicht.
pfandwerk_anfragen_total{dienst="pfandwerk-bon"}
pfandwerk_anfragen_total{dienst="pfandwerk-bon"}[5m]
Die Zeitdauer in eckigen Klammern macht aus dem Instant Vector einen Range
Vector; Einheiten sind ms s m h d w y. offset verschiebt den Zeitpunkt, etwa
für den Vergleich mit der Vorwoche.
sum by (dienst) (
rate(pfandwerk_anfragen_total[5m]))
sum by (dienst) (rate(
pfandwerk_anfragen_total{ergebnis="fehler"}[5m]))
/
sum by (dienst) (
rate(pfandwerk_anfragen_total[5m]))
Beide Seiten der Division brauchen dieselbe Gruppierung, sonst bleibt das Ergebnis leer.
Die wichtigste Regel der Sprache — erst rate, dann sum:
# richtig
sum(rate(pfandwerk_anfragen_total[5m]))
# falsch
rate(sum(pfandwerk_anfragen_total)[5m:])
rate erkennt den Neustart eines Zählers und gleicht ihn aus. Wird vorher
summiert, sieht jeder Neustart wie ein Einbruch aus — das Ergebnis bleibt
plausibel und ist trotzdem falsch. Die falsche Variante ist syntaktisch gültig,
genau deshalb überlebt sie so lange im Dashboard.
Leere Ergebnisse sind fast immer ein Label-Problem: Eine Seite trägt ein
Label, das die andere nicht kennt, oder die Gruppierung unterscheidet sich
zwischen Zähler und Nenner. Ein Join über target_info braucht ausdrückliches
Vektor-Matching.
Modul: PromQL systematisch einsetzen
Histogramme und Quantile
| Bauart | Stärke | Schwäche |
|---|---|---|
| klassisch | überall unterstützt | Grenzen vorab festzulegen |
| Summary | schnell abzufragen | nicht aggregierbar |
| nativ | auflösend und sparsam | jüngste der drei Bauarten |
Die Empfehlung ist eindeutig: wo möglich Native Histograms, vor klassischen Histogrammen und Summaries.
histogram_quantile(0.95, sum(rate(
pfandwerk_pruefung_dauer_seconds[5m])))
histogram_quantile(0.95, sum by (le) (rate(
pfandwerk_pruefung_dauer_seconds_bucket[5m])))
Oben nativ, unten klassisch. Wer bei der klassischen Bauart das le weglässt,
bekommt kein Ergebnis — und sucht meist lange.
Histogramme lassen sich aggregieren, Quantile nicht. Vorberechnete Quantile eines Summary sind nicht mittelbar; der Mittelwert zweier Perzentile ist statistisch ohne Bedeutung. Bei replizierten Diensten gilt deshalb faktisch immer das Histogramm.
Exemplars führen von der Metrikspitze zum konkreten Trace. Sie müssen im Backend aktiviert sein, und der Trace dahinter darf nicht weggesampelt worden sein.
Modul: Histogramme, Quantile und Exemplars
Grafana: Datenquellen, Explore, Verbindungen
| Quelle | Sprache | Typische erste Frage |
|---|---|---|
| Prometheus | PromQL | Seit wann steigt die Fehlerquote? |
| Tempo | TraceQL | Welcher Vorgang war der langsamste? |
| Loki | LogQL | Was meldete der Dienst zu diesem Vorgang? |
Die Reihenfolge der Fragen ist typisch: erst die Metrik, dann der Trace, dann das Log. Explore steht vor dem Dashboard — eine Abfrage lässt sich sofort erproben, der Query Inspector zeigt, was tatsächlich an das Backend ging. Erst was sich in Explore bewährt, lohnt den Weg ins Dashboard.
| Von | Nach | Beantwortet |
|---|---|---|
| Log mit Trace-ID | Trace in Tempo | Was geschah im ganzen Vorgang? |
| Trace mit Dienstname | Metrik in Prometheus | Ist das ein Einzelfall? |
| Metrikspitze mit Exemplar | Trace in Tempo | Welcher Vorgang war das? |
Annotations beantworten die häufigste Rückfrage von selbst: Lag es an einem Release? Die Correlation immer an einem Fehlerfall prüfen, nicht am Normalfall — dort fehlt die Trace-ID am ehesten.
Modul: Grafana-Datenquellen und Explore
Dashboards: Panelform nach Fragestellung
| Panel | Zeigt | Geeignet für |
|---|---|---|
| Time Series | Verlauf über die Zeit | Rate, Fehlerquote, Latenz |
| Heatmap | Verteilung über die Zeit | Latenz aus dem Histogramm |
| State Timeline | Zustandswechsel | Verfügbarkeit je Filiale |
| Tabelle | Rangfolge und Detail | die zehn langsamsten Vorgänge |
Reihen je Dienst, geordnet nach Datenfluss, bilden die Architektur im Dashboard ab. Die Panelreihenfolge sollte dem Analyseweg folgen, nicht der Entstehung.
Kopien sind die Ursache der meisten veralteten Dashboards: Eine Korrektur muss danach an vierzig Stellen nachgezogen werden, und niemand weiß, welche Kopie die gültige ist. Statt eines Dashboards je Filiale gehört dorthin eine Variable — die dann auch wirklich in jedem Panel verwendet werden muss.
Modul: Aussagekräftige Grafana-Dashboards
Alerting und Recording Rules
groups:
- name: pfandwerk
rules:
- record: dienst:anfragen:fehlerquote5m
expr: sum by (dienst) (...)
- alert: PfandwerkFehlerquoteHoch
expr: dienst:anfragen:fehlerquote5m > 0.05
for: 10m
labels: { schwere: warnung }
Das Namensschema level:metric:operations hält vorberechnete Zeitreihen von
erhobenen unterscheidbar. Recording Rules sind mehr als eine Optimierung: Alarm
und Dashboard rechnen garantiert dasselbe, weil beide dieselbe Reihe lesen.
| Mittel | Wirkt gegen | Beispiel |
|---|---|---|
for | kurze Ausschläge | 10 Minuten anhaltende Fehlerquote |
| Gruppierung | Alarmlawinen | alle Filialen in einer Meldung |
| Inhibition | Folgealarme | Netzausfall unterdrückt Dienstalarme |
| Wiederholung | Vergessen | alle vier Stunden erneut |
keep_firing_for verhindert das Flattern, wenn eine Bedingung um den
Schwellwert pendelt — zu großzügig gesetzt hält es den Alarm unnötig aktiv.
Grafana-verwaltet oder datenquellenverwaltet ist eine echte Wahl: Regeln in Prometheus laufen auch, wenn Grafana nicht erreichbar ist; Regeln in Grafana können mehrere Datenquellen in einer Bedingung verbinden. Zwei parallele Wege führen zu doppelter Zustellung — die Entscheidung gehört einmal getroffen und dokumentiert.
Modul: Alerting und Recording Rules
SLI, SLO und Error Budgets
| Indikator | Gut ist | Gesamt ist |
|---|---|---|
| Erfolgsquote Rücknahme | Rücknahme abgeschlossen | alle Rücknahmeversuche |
| Latenz Auszahlung | Auszahlung unter 2 s | alle Auszahlungen |
| Verfügbarkeit Automat | Automat antwortet | alle Prüfzeitpunkte |
Anfragebasierte Indikatoren zählen Ereignisse, zeitbasierte zählen gute Zeitfenster. Der Indikator muss den Nutzer abbilden, nicht die Technik: Ein laufender Prozess ist keine erfolgreiche Rücknahme, und fachliche Ablehnungen gehören nicht zu den schlechten Ereignissen.
| Burn Rate | Fehlerquote | Budget aufgebraucht nach |
|---|---|---|
| 1 | 0,1 Prozent | 30 Tagen |
| 6 | 0,6 Prozent | 5 Tagen |
| 14,4 | 1,44 Prozent | rund 2 Tagen |
Die Tabelle gilt für ein Ziel von 99,9 Prozent. Eine Burn Rate von 1 heißt: Das Budget reicht genau bis zum Ende des Messfensters.
Multi-Window-Multi-Burn-Rate löst zwei Probleme zugleich — ein schneller Verbrauch soll sofort melden, ein langsamer erst als Ticket; das kurze Fenster verhindert, dass ein längst beendeter Verbrauch weiter meldet. Empfohlen sind 14,4 über 1 Stunde, 6 über 6 Stunden, 1 über 3 Tage; das kurze Fenster beträgt jeweils ein Zwölftel des langen.
Modul: SLI, SLO und Error Budgets
Observability as Code
datasources:
- name: Prometheus
type: prometheus
url: ${PROM_URL}
isDefault: true
| Artefakt | Ablage | Ohne Versionierung |
|---|---|---|
| Collector-Konfiguration | Repository | niemand kennt den Stand |
| Datenquellen | provisioning/datasources | je Umgebung verschieden |
| Dashboards | provisioning/dashboards | Kopien und Karteileichen |
| Alarmregeln | provisioning/alerting | nicht nachvollziehbar |
Werte über Umgebungsvariablen halten dieselbe Datei für alle Umgebungen brauchbar. Git Sync verbindet Grafana-Ressourcen zusätzlich mit einem Git-Arbeitsablauf. Die Probe aufs Exempel ist der Neuaufbau aus dem Repository — er deckt jede handgeklickte Einstellung auf.
Modul: Observability as Code und produktiver Betrieb
Reifegrad im September 2026
| Thema | Stand | Bedeutung für die Planung |
|---|---|---|
| Traces, Metrics, Logs | stabil | tragen Produktion |
| deklarative Konfiguration | stabil | ab jetzt einsetzbar |
| Profiles | Public Alpha | beobachten, nicht bauen |
| OpenMetrics 2.0 | in Arbeit | Ausblick |
Spezifikation stabil und Sprachimplementierung stabil sind zwei verschiedene
Aussagen. Praktisch am wichtigsten ist die deklarative Konfiguration: eine
YAML-Datei konfiguriert SDK und Instrumentierung über Sprachen hinweg, stabil
sind Datenmodell, Dateiformat und die Variable OTEL_CONFIG_FILE. Sie löst die
gewachsene Sammlung einzelner Umgebungsvariablen ab; die alte experimentelle
Variable gehört dabei entfernt, nicht liegengelassen.
Modul: Aktuelle Entwicklungen und Ausblick
Typische Fallen
- Erst summiert, dann die Rate gebildet — jeder Neustart erzeugt einen Scheinausfall. Das Ergebnis sieht plausibel aus.
- Das
lefehlt beim klassischenhistogram_quantile— kein Ergebnis, lange Suche. - Perzentile werden über Instanzen gemittelt und ergeben eine erfundene Zahl.
- Die Latenz wird als Mittelwert erfasst. Zehn schnelle und eine sehr langsame Anfrage ergeben einen unauffälligen Schnitt.
- Das Ergebnis ist leer, und die Ursache wird in den Daten gesucht statt in den Labels.
irateim Dashboard liefert sprunghafte Kurven; dasrate-Fenster ist kürzer als zwei Scrape-Intervalle und liefert Lücken.- Abfragen werden gegen die OTLP-Schreibweise formuliert und finden nichts —
Punkte sind Unterstriche,
service.nameistjob. - Alle Resource-Attribute werden zu Labels befördert und sprengen die Kardinalität.
- Die Bon-Nummer wird Label, weil sie in der Analyse praktisch wäre. Ebenso: ungruppierte Anfragepfade, Ausnahmetexte.
- Ein Counter ohne
_total, Millisekunden im Namen und Sekunden in der Rechnung, ein Gauge als Counter erfasst. - Ein Alarm ohne
forlöst bei jedem einzelnen Ausreißer aus; ohne Gruppierung erzeugen vierzig Filialen vierzig Meldungen. - Dieselbe Bedingung existiert in Prometheus und in Grafana — doppelte Zustellung, widersprüchliche Stände.
- Der Alarm meldet eine Zahl ohne Einheit und ohne betroffenen Dienst, und der Runbook-Verweis zeigt ins Leere.
- Das SLO wird gesetzt, aber nie mit einer Entscheidung verknüpft — oder der Zielwert ist so niedrig, dass er nie verfehlt wird.
- Nur ein Fenster für die Burn Rate, und der Alarm bleibt nach der Störung stundenlang aktiv.
- Dashboards werden bereitgestellt und trotzdem in der Oberfläche verändert; Zugangsdaten stehen im Klartext in der Datenquellendatei.
Zum Seminar Observability mit OpenTelemetry, Prometheus und Grafana