Überprüfung von Thanos-Bereitstellungen
Feldregeln aus dem Betrieb von ~20 Single-Tenant-Thanos-Stacks hinter einem globalen Querier auf GKE (Thanos 0.42, Caches 0.35+). Maßgebliche Quelle mit Zahlen und Begründungen:
- Architektur, Performance und FinOps: https://wiki.jdelgado.fr/monitoring/lgtm/thanos/
- Caches: https://wiki.jdelgado.fr/monitoring/lgtm/thanos-cache/
- Lesekonkurrenz: https://wiki.jdelgado.fr/monitoring/lgtm/thanos-query-concurrency/
Der vollständige Regelsatz (R1-R102, Flags, Standardwerte, Metriken, Anker) befindet sich in references/rules.md. Lesen Sie den Abschnitt jeder Komponente, die die Änderung betrifft, bevor Sie Schlussfolgerungen ziehen.
Überprüfungsverfahren
- Notieren Sie die Thanos-Version (Image-Tag). Flags und Standardwerte ändern sich: Überprüfen Sie jedes Flag anhand der Version (Regel R92).
- Bei einem Wrapper-Chart (bitnami
thanos, einem lokalen Umbrella-Chart, handgeschriebenenextraFlags/args) lesen Sie die Flags in den gerenderten Manifesten (helm templatepro Stack), nicht in den Werten: Ebenen überschreiben sich gegenseitig und manche Flags existieren nur in einer Vorlage. - Bereitstellungsspezifische Namen (das Label, nach dem Stacks beschnitten werden, Tenant-Labels, Cluster- oder Rechenzentrums-Labels) stammen aus der Konfiguration selbst. Wenn das Diff sie nicht zeigt, fragen Sie nach dem GitOps-Repo, anstatt einen Namen anzunehmen.
- Gehen Sie für jede vorhandene Komponente die folgende Checkliste durch, dann ihren Abschnitt in
references/rules.md. - Berechnen Sie die Nebenläufigkeitsinvariante mit den tatsächlichen Werten, wenn eines ihrer 4 Flags gesetzt oder auf dem Standardwert belassen wird.
- Überprüfen Sie die Alarmregeln anhand der tatsächlichen Ausführung der Komponente (CronJob, Spot, Pending-Pods).
- Berichten Sie im untenstehenden Ausgabeformat.
Checkliste nach Komponente
Nebenläufigkeitsinvariante (Query Frontend + Querier + Store Gateway). --query-range.max-query-parallelism (Standard 14) x min(selectors, --query.max-concurrent-select) (Standard 4) muss unter min(--store.grpc.series-max-concurrency, --query.max-concurrent) (beide Standard 20) bleiben. Standardwerte verletzen sie bereits (56 vs 20). Sobald die Parallelität allein die Gate-Grenze erreicht, sättigt eine Abfrage das Gate auf jedem Stack. Ein --query.max-concurrent-select weit über 4 entfernt die Begrenzung: Der Multiplikator wird zur rohen Selektor-Anzahl. Ein Gate, das dimensioniert ist, um den RSS des Store Gateway zu begrenzen (nach OOMKills gesetzt), ist eine Speicherbegrenzung, belassen Sie es. Die Invariante bindet bei breiten Abfragen: Gemessene Abfragen haben durchschnittlich 1,5 Selektoren, und die Gattersättigung stammte von mehreren gleichzeitigen Abfragen auf dem History-Shard, nicht von einer einzelnen breiten Abfrage. Ein globaler Querier erreicht regionale Querier über die StoreAPI, die deren PromQL-Gates umgeht. Korrigieren Sie beide Multiplikatoren gemeinsam, einschließlich des globalen Frontends, berechnen Sie --query.max-concurrent über den gesamten Querier-Pool hinweg und kommentieren Sie jeden Multiplikator mit der nachgelagerten Grenze, die er einhalten muss.
Querier.
- Teilergebnis ist standardmäßig aktiviert (
--query.partial-responsestandardmäßigtrue): Ein fehlendes Flag ist kein sicherer Standard. Es verwirft Stores, die einen Fehler verursachen, nicht langsame:--store.response-timeoutstandardmäßig0ms(deaktiviert), daher bringt ein einzelner langsamer Store die gesamte Abfrage bei--query.timeout(Standard 2m) zum Fehlschlagen, anstatt verworfen zu werden. - Teilergebnis bei einem Querier, der vom Ruler oder Alerting verwendet wird, kann stumm Alarme verpassen, und Teilergebnisse werden nie zwischengespeichert. Aktivieren Sie es nur pro Anfrage für menschliche Grafana-Datenquellen.
--selector-labeldarf niemals ein Vorlagenstandard sein: Ein Stack, der 2 Werte des Labels aufnimmt, gibt für den zweiten Wert EMPTY zurück (stummer Datenverlust). Für jeden Stack einzeln aktivieren, mit einem Einzelwert-Label, das Abfragen tatsächlich tragen. Überprüfen Sie jeden Stack, in dem es gesetzt ist, darauf, was dieser Stack tatsächlich aufnimmt.--grpc-compressionist nach v0.43.0 veraltet. Sein Ersatz,client_config.compressionpro Endpunkt in--endpoint.sd-config, wird ignoriert, wenn dieser Endpunkt nicht auch sein eigenestls_configsetzt (z. B.enabled: false): Das globale Flag gewinnt sonst immer noch.
Store Gateway.
- Erhöhen Sie niemals
--store.grpc.series-max-concurrency, um eine Sättigung zu beheben: Das Gate stellt in eine Warteschlange, es lehnt nicht ab. Sättigung zeigt sich durch Timeouts, die meisten Minuten liegen weit unter der Grenze, und eine Erhöhung entfernt die einzige Begrenzung während der Spitzen. Finden Sie den Client im Slow Log des Query Frontends. --store.limits.request-series=2000000ist ein kopierter Wert, der nie auslöst (größter gemessener Aufruf: 200000 Serien). Messen Sie zuerstthanos_bucket_store_series_data_touched_bucket{data_type="series"}.thanos_bucket_store_queries_dropped_totalbei 0 beweist nichts: Es zählt nur Begrenzer, die standardmäßig 0 (aus) sind.- Zeitshards müssen sich überlappen; aneinandergereihte
--min-time/--max-time-Grenzen garantieren ein blindes Fenster, wenn eine Replik fehlt.--min-timeist die älteste Grenze. Ein positiver--max-time(z. B.3h) ist keine Obergrenze: Der Shard bedient bis zur aktuellen Zeit, zusätzlich zu Receive. - Kein
emptyDirauf der Boot-Festplatte des Knotens: Der Index-Header-Download sättigt sie (30 Minuten Wiederherstellungszeit). Dedizierte PVC mit bereitgestellten IOPS/Durchsatz; auf Hyperdisk Balanced bringt Kapazität keine Leistung. --sync-block-durationunter dem 15-Minuten-Standard kostet hinter einem Caching-Bucket kaum etwas (nuriter-Aufrufe hängen davon ab, ~1 USD/Monat flottenweit gemessen): Keine eigene Ausrollung wert, beim nächsten geplanten Neustart korrigieren.- Behalten Sie den Lazy Reader bei, niemals Lazy Index-Header-Download (p99 ~224 s). Beim standardmäßigen 5-Minuten-Leerlauf-Timeout zahlt die erste Abfrage nach jeder Lücke einen vollständigen Neuladevorgang; das versteckte Flag
--store.index-header-lazy-reader-idle-timeoutauf einen Arbeitstag (10h) erhöht reduzierte die Ladezeit um 84 % bei einem Stack, auf Kosten des residenten Speichers. Verstecktes Flag: Bei Upgrades erneut prüfen.
Caches.
- Gemeinsamer Caching-Bucket über mehrere Tenants hinweg bei Thanos < v0.35.0: Schlüsselkollision, Store Gateway bleibt
Readyund liefert keine historischen Daten. Redisprefixisoliert in jeder Version. Ein Single-Tenant-Canary kann es nicht erkennen. - Index-Cache und Caching-Bucket auf derselben Redis: separate
db(Indexdb: 1). - Dimensionieren Sie einen gemeinsamen Cache nicht nach IN-MEMORY-Cache-Größen (sie messen die Pod-Laufzeit). Dimensionieren Sie nach dem Arbeitssatz über das TTL-Fenster (P95 eines rollierenden >= 24 h Subquery mit explizitem Schritt), geteilt durch die gemessene Überlappung zwischen Replikaten eines Shards, niemals nach der Pod-Anzahl. Replikate laden dieselben Blöcke, cachen aber unterschiedliche Chunks: 2 Replikate gemessen 1,4, nicht 2.
- Caching-Bucket-Subranges leben 24 h (
chunk_subrange_ttl); Redisexpirationsteuert sie nicht. - Die Cache-Datei des Query Frontends wird streng geparst: kein
ttl, keinenabled_items, TTL istexpirationunterconfig:. Unbekannte Schlüssel: Die Komponente startet nicht. - Redis hinter Sentinel: Sentinel-Dienst +
master_name, sonst gehen Schreibvorgänge nach einem Failover an den alten Master.
Receive.
- Bleibt bei Bedarf mit einer dedizierten Prioritätsklasse: die einzige Komponente, die noch nicht hochgeladene Daten hält. Alles andere läuft auf Spot.
- Dimensionieren Sie das Speicherlimit auf Basis der maximalen aktiven Serien.
head_series_limitzahlt sich nur in einem gemeinsam genutzten Multi-Tenant-Receive oder mit Replikation aus: Bei einer Replik blockiert ein 429 jeden Schreibvorgang des Tenants, ein OOM kostet nur einen Neustart und eine WAL-Wiederholung. Es ist ohne eine erreichbaremeta_monitoring_urlwirkungslos. Anforderungslimits geben 413 zurück (Prometheus verwirft die Daten), das Serienlimit gibt 429 zurück (wird wiederholt). - Ketama-Hashring, nicht
hashmod, wenn sich die Pool-Größe ändern wird. - Begrenzen Sie den Go-Heap:
GOMEMLIMITaus dem Pod-Speicherlimit oder--enable-auto-gomemlimit(standardmäßig aus, 0,9 des Container-Limits). Go setzt GOMAXPROCS aus der Cgroup seit Version 1.25, niemals GOMEMLIMIT. Ein Receive-OOM bedeutet eine WAL-Wiederholung, bevor es wieder bedient.
Kompaktor.
- Als CronJob:
concurrencyPolicy: Forbidist obligatorisch (2 Kompaktoren auf einem Bucket korrumpieren Blöcke). thanos_compact_todo_*-Gauges werden nur mit--waitregistriert: Im CronJob-Modus feuert ein Alarm auf sie nie. Der StandardalarmThanosCompactIsDownfeuert 23 h/24. Ersetzen Sie ihn durch CronJob-Regeln von kube-state-metrics und einen Store-Gateway-Proxy fürthanos_blocks_meta_synced-Nachlauf. Jeder Alarm auf einer Kompaktor-Metrik (thanos_compact_group_compactions_failures_total...) feuert im CronJob-Modus nie: Kein Scrape-Ziel zwischen den Läufen.- Niemals eine Bucket-Lebenszyklusregel oder Archivspeicherklasse auf einem Thanos-Bucket: Nur der Kompaktor kennt Blockabhängigkeiten; die Wiederholungsschleife multiplizierte die API-Rechnung um das 10-fache der Ersparnis.
- Aufbewahrungsstufen, die auf das abgestimmt sind, was Store Gateways bedienen.
Querschnittlich.
- Alle Komponenten eines Stacks in einer Zone: Inter-Zonen-Verkehr wird auf jedem Pfeil des Datenpfads abgerechnet. Eine Zonen-Streuungsbeschränkung auf einem Stack ist ein Kostenvorfall, ein bewusster Kompromiss, um dies anders zu handhaben.
- Store-Readiness-Alarm auf StatefulSet gewünschter vs. bereiter Replikate mit
for: 30m:up == 0feuert nie für einen evakuierten Pending-Pod, undfor: 5malarmiert bei normaler Spot-Wiederherstellung.
Diagnose einer Sättigung
Verwenden Sie die Verfahren D1-D10 in references/rules.md. Kern von D1: in_flight = durchsatz x dauer (Little's Law), gemessen pro Pod; Dauer dominiert normalerweise (x190 vs x4.5 im Feld). Lesen Sie Gate-Panels mit max_over_time(...[$__interval:1m]), Latenz bei p999/p9999, Flottengröße mit count(group by (pod) (up == 1)).
Ausgabeformat
Ergebnisse geordnet nach Schweregrad: Datenverlust oder stille falsche Ergebnisse, dann Ausfallrisiko (eine Konfiguration, mit der die Komponente nicht startet, zählt hier), dann Kosten, dann Hygiene. Jedes Ergebnis:
- Feld oder Flag, mit der Zeile
- das Problem und seine konkrete Konsequenz
- die Korrektur als genauer Wert oder Codeausschnitt
- die Wiki-URL mit Anker, kopiert aus
references/rules.md(Regel-IDs sind intern, lassen Sie sie weg)