Mr.PlanB Logo

    Newsletter

    Newsletter abonnieren

    Neue Infrastruktur-Guides, Vergleichsberichte und Migrationshinweise direkt ins Postfach.

    Infrastruktur-Notizen, Guides und neue Tools. Jederzeit abbestellbar.

    Zurück zum Blog
    Prometheus
    Cardinality
    SRE
    Monitoring

    Wie ein Team den Prometheus-Speicherverbrauch von 60 GB auf 20 GB gesenkt und dabei ein Cardinality-Problem aufgedeckt hat

    22. Januar 2026
    5 Min. Lesezeit

    Irgendwann erlebt jeder Prometheus-Betreiber diesen Moment. Die UI wird träge, Scrapes scheitern, PromQL-Queries laufen in ein Timeout, und Dashboards hängen gerade lange genug, um einen an den eigenen Lebensentscheidungen zweifeln zu lassen. Dann prüft man den Speicher und sieht, wie sich Prometheus lässig durch 50 bis 60 GB RAM frisst.

    Mit „leicht überdimensioniert" hat das nichts mehr zu tun, eher mit „das hier wird gleich der größte Incident des Jahres".

    Ein Engineer schilderte genau dieses Szenario: einen Cluster, in dem Prometheus auf rund 60 GB angeschwollen war. Die Scrape-Zuverlässigkeit sank, Queries liefen in Timeouts, und die Stabilität bröckelte. Um theoretisches Tuning ging es da längst nicht mehr, sondern ums Überleben. Die Lösung war auch nicht exotisch, sie war schlicht gnadenlos.

    Die wahren Übeltäter: drei vorhersehbare Killer

    Die Untersuchung deckte drei klassische Speicherfallen von Prometheus auf:

    1. Doppeltes Scraping
    2. Histogramm-Überlastung
    3. Label-Explosion

    Wer Prometheus lange genug in Kubernetes betreibt, ist wahrscheinlich schon mindestens einmal von einer davon gebissen worden, und die meisten Teams trifft es bei allen dreien.

    1. Doppeltes Scraping: der versehentliche 2x-Multiplikator

    Prometheus scrapte Ingress-Metriken sowohl von den Pods als auch von einem ServiceMonitor. Das klingt harmlos, ist es aber nicht.

    Jeder doppelte Scrape verdoppelt:

    • die Anzahl der Time Series
    • den Speicherverbrauch
    • den WAL-Druck
    • den Head-Block-Churn

    Sofern man nachgelagert nicht ausdrücklich dedupliziert, hat man die Cardinality für das gesamte Metrik-Set damit verdoppelt. Dieser Multiplikator fällt nicht sofort auf, summiert sich aber über Cluster hinweg, und ein kleines Config-Versehen kann einen zig Gigabyte kosten.

    Die Lösung bestand darin, das unnötige Scraping auf Pod-Ebene zu deaktivieren, was einfach, rigoros und wirksam war.

    2. Histogramm-Überlastung: Tod durch Buckets

    Histogramme sind mächtig, aber auch teuer. Metriken wie *_duration_seconds_bucket können Hunderttausende Time Series erzeugen, wenn:

    • sie viele Buckets haben
    • sie viele Label-Kombinationen haben
    • sie viele Replicas haben

    Man multipliziert Buckets × Labels × Pods × Cluster, das Ganze noch mit der Retention im Head-Block, und plötzlich sieht der Speicherverlauf aus wie ein Hockeyschläger.

    Histogramme sind in Exportern oft standardmäßig aktiviert, und Teams prüfen selten nach, ob sie wirklich alle Buckets brauchen oder ob High-Cardinality-Labels daran hängen. Am Ende wird Prometheus zum Histogramm-Lagerhaus.

    3. Label-Explosion: das Cardinality-Monster

    Diese Falle ist die gefährlichste. Labels wie:

    • replicaset
    • path
    • container_id

    erzeugten über 10.000 eindeutige Werte. Das ist nicht „ein bisschen viel", das ist eine Cardinality-Bombe.

    Jede eindeutige Label-Kombination entspricht einer eigenen Time Series. Wenn path rohe URLs mit eingebetteten IDs enthält, sich container_id bei jedem Deploy ändert und replicaset ständig rotiert, erzeugt man Tausende neuer Time Series pro Rollout. Prometheus hält sie alle im Speicher, und zwar nicht aus einem Defekt heraus, sondern weil es genau so funktioniert.

    Die Lösung war nicht raffiniert, dafür diszipliniert

    Die Abhilfemaßnahmen waren unkompliziert:

    • ungenutzte Metriken entfernen (nach Prüfung von Dashboards und Alerts)
    • redundantes Scraping deaktivieren
    • High-Cardinality-Labels entfernen, die gar nicht genutzt wurden
    • Skripte schreiben, um zu prüfen, was gefahrlos entfernt werden konnte

    Der letzte Punkt ist wichtig, denn wer Labels blind entfernt, riskiert Ingestion-Fehler. Ein Moderator meldete sich mit einer entscheidenden Warnung: labeldrop entfernt das Label, aber nicht die Series. War dieses Label für die Eindeutigkeit nötig, kann seine Entfernung zu Kollisionen doppelter Series und zu Ingestion-Fehlern führen.

    Über solche subtilen Eigenheiten von Prometheus stolpern viele. Man löscht keine Series, man führt sie zusammen, und gedankenloses Zusammenführen endet im Chaos. Der Autor passte seinen Beitrag nach dieser Korrektur entsprechend an.

    Schon dieser Austausch zeigt, dass Prometheus-Tuning Präzisionsarbeit ist und kein Rätselraten.

    Das Ergebnis: von 60 GB auf 20 GB

    Nach dem Aufräumen sank der Speicherverbrauch von rund 60 GB auf etwa 20 GB, und die Stabilität kehrte zurück. Die Scrapes normalisierten sich, die UI reagierte wieder zügig, und PromQL lief nicht mehr in Timeouts. Cluster und Workloads waren dieselben, nur die Disziplin war eine andere.

    Die 40 GB Unterschied kamen nicht durch magische Komprimierung zustande, sondern dadurch, dass Verschwendung entfernt wurde.

    Das unangenehme Muster, das sich durch alle Teams zieht

    Die Kommentare sind aufschlussreich. Mehrere Teams berichteten von ähnlichen Problemen. Jemand witzelte: „Laughs in VictoriaMetrics." Ein anderer meinte: „NetData ;)"

    Der Kreislauf ist immer derselbe:

    1. Prometheus wächst.
    2. Der Speicherverbrauch explodiert.
    3. Die Leute geben der Datenbank die Schuld.
    4. Alternative TSDB-Anbieter betreten die Bühne.

    Unbequem, aber wahr: Die meisten Speicherexplosionen bei Prometheus haben architektonische Ursachen, nämlich doppeltes Scraping, unbegrenzte Label-Cardinality, übereifrige Histogramme und zu viele Kubernetes-Standardmetriken, und Prometheus selbst ist daran selten schuld.

    Eine deutliche Empfehlung lautete deshalb, die Standard-action: drop-Regeln in kube-prometheus-stack zu überprüfen, weil Metriken vom Kubernetes-apiserver und von cAdvisor kleine Setups überfordern können. Kubernetes sendet jede Menge Metriken, und wer sie alle blind aufnimmt, meldet sich freiwillig für eine Speicherkrise.

    Die eigentliche Lektion: Prometheus verzeiht keine Faulheit

    Prometheus ist schonungslos ehrlich und speichert genau das, was man ihm aufträgt. Schlechte Labeling-Entscheidungen komprimiert es nicht einfach weg, vor Cardinality-Explosionen schützt es einen nicht automatisch, und es warnt auch nicht, wenn sich Histogramme mit Replicas vervielfachen. Es geht davon aus, dass man weiß, was man tut, und das ist Macht und Gefahr zugleich.

    Wenn Leute sagen „Prometheus skaliert nicht", meinen sie oft eigentlich: „Wir haben die Cardinality nicht im Griff gehabt."

    Wenn Sie gerade bei 40 GB+ RAM liegen

    Dann gehen Sie diese unbequeme Checkliste durch:

    • Scrapen Sie denselben Endpoint doppelt?
    • Brauchen Sie wirklich Pod-Ebenen-Metriken für jeden Service?
    • Wie viele Histogramm-Buckets exportieren Sie?
    • Hängen Sie User- oder Path-Level-Labels an High-Volume-Metriken?
    • Stecken Container-IDs oder Replica-Hashes in Ihren Labels?
    • Haben Sie kürzlich ungenutzte Metriken geprüft?
    • Überprüfen Sie die Drop-Regeln in kube-prometheus-stack?

    Wenn Sie sich diese Fragen noch nicht gestellt haben, wartet Ihr Speicherverlauf nur auf seinen Auftritt.

    Prometheus mit 20 GB ist nicht klein, sondern kontrolliert

    Die Erkenntnis lautet „bewusst betreiben" und nicht „weniger betreiben". Eine Prometheus-Instanz, die für einen großen Kubernetes-Cluster verantwortungsvoll 20 GB nutzt, kann vollkommen gesund sein. Eine Instanz, die 60 GB verbraucht, weil sie Ingress-Metriken dupliziert und jede jemals erzeugte Container-ID mitverfolgt, verschwendet dagegen Ressourcen.

    Den Unterschied macht die Disziplin, und in der Observability skaliert Disziplin jedes Mal besser als Hardware.