Prometheus mám rád. Je jednoduchý, spoľahlivý a v jednom klastri ho nič netromfne. Problém začína, keď máte 20+ klastrov a niekto na stretnutí položí otázku „aké bolo p99 latency naprieč všetkými prostrediami za posledný kvartál?". Vtedy zistíte, kde má jeden Prometheus svoje hranice.
Kde sám Prometheus naráža
Štyri steny, do ktorých som narážal opakovane:
- Retencia. Prometheus je lokálna TSDB na disku. 15 dní je pohodlných, kvartál už nie, rok je fantázia. Lokálne úložisko proste neškáluje.
- Žiadny globálny pohľad. Každý klaster má svoj Prometheus, svoj Grafana datasource. Dotaz cez všetky znamená ručne prepínať alebo lepiť panely. Nepoužiteľné.
- HA bez deduplikácie. Pre HA bežia dva identické Prometheusy. Sú to dve nezávislé TSDB s mierne posunutými scrape časmi — pri dotaze vidíte rozdvojené čiary a sami si nededuplikujú.
- Cardinalita a pamäť. Prometheus drží celú hlavu (head) v RAM. Jeden zlý label s vysokou cardinalitou (
user_id,podv cykle reštartov,paths UUID) a OOM kill je na svete.
Dve cesty integrácie s Thanosom
Thanos rieši tieto problémy, ale prvé zásadné rozhodnutie je, ako doň metriky dostať.
Cesta A — Sidecar. Vedľa každého Prometheusu beží Thanos Sidecar. Robí dve veci: nahráva hotové TSDB bloky do object storage (S3, Azure Blob) a sprístupňuje živé dáta Prometheusu cez gRPC Store API. Výhoda: minimálny zásah do Prometheusu, push modelu sa vyhnete. Nevýhoda: bloky sa nahrávajú až po uzavretí (typicky každé 2 hodiny), takže pre čerstvé dáta sa stále spolieha na lokálnu TSDB. Toto je moja default voľba pre vlastné klastre, kde mám Prometheus pod kontrolou.
Cesta B — remote-write do Thanos Receive. Prometheus posiela samply v reálnom čase cez remote_write do komponenty Receive, ktorá ich consistent-hashingom rozdistribuuje, ukladá do lokálnych TSDB a postupne tlačí do object storage. Výhoda: centralizácia, edge klaster nemusí mať vlastné úložisko ani object-storage credentials. Toto dáva zmysel pre tenké/edge klastre alebo multi-tenant. Nevýhoda, ktorá ma spálila: back-pressure. Keď Receive nestíha alebo spadne, remote_write queue v Prometheuse rastie, latencia ingestu stúpa a v najhoršom prípade strácate samply. Receive sa stáva kritickou cestou, ktorú musíte škálovať a monitorovať ako produkčnú databázu.
V praxi kombinujem oboje: Sidecar tam, kde vlastním klaster, Receive pre edge.
Komponenty Thanosu a čo robia
- Sidecar — upload blokov + Store API nad živým Prometheusom.
- Receive — príjem remote-write, consistent hashing, lokálne TSDB, upload do object storage.
- Store Gateway — sprístupňuje historické bloky z object storage cez Store API. Drží lokálne index header a in-memory index cache (
--index-cache-size). - Compactor — kompakcia blokov, retencia a downsampling. Beží mimo dotazovej cesty.
- Query (Querier) — implementuje Prometheus HTTP API, fan-out na všetky Store API endpointy (Sidecar, Store Gateway, Ruler), deduplikuje HA páry, vyhodnocuje PromQL.
- Ruler — vyhodnocuje recording a alerting pravidlá nad dátami z Query a výsledky zapisuje ako nové bloky.
Dlhodobé úložisko, downsampling a kompakcia
Všetko dlhodobé žije v object storage. Compactor robí dve veci, ktoré rozhodujú o cene aj rýchlosti. Kompakcia zlučuje malé bloky do väčších a zmenšuje indexy. Downsampling vytvára nižšie rozlíšenia: raw dáta (= scrape interval) si pri blokoch starších ako 40 hodín agreguje na 5-minútové rozlíšenie, a to staršie ako 10 dní na 1-hodinové. Querier potom auto-volí rozlíšenie podľa rozsahu dotazu — ročný graf nečíta miliardy raw samplov, ale 1h agregáty. Pozor na pascu: ak je retencia na danom rozlíšení kratšia než minimálny vek pre ďalší downsampling pass, dáta sa zmažú skôr, než ich stihne agregovať.
Deduplikácia HA párov
Toto je elegantné. Každému Prometheusu v HA páre dáte rovnaké external labels okrem jedného replica labelu (napr. replica=a / replica=b). Querier dostane --query.replica-label=replica a pri dotaze obe série zmerguje do jednej. Žiadne rozdvojené čiary. Dôležité: external labels musia byť konzistentné, inak deduplikácia nefunguje.
Cardinalita je ten skutočný nákladový pákový bod
Object storage je lacné, ale počet sérií platíte všade — v pamäti Prometheusu, v indexoch Store Gateway, v čase dotazov. Najväčší dopad nemá výber komponentov, ale relabeling. Agresívne dropujem labely s vysokou cardinalitou (metric_relabel_configs s action: labeldrop, alebo drop celých metrík) ešte pri scrape. Jedna metrika s id per request dokáže vygenerovať viac sérií než zvyšok klastra dohromady. Cardinalitu treba riešiť pri zdroji, nie liečiť výkonom downstreamu.
Gotchas z prevádzky
- Compactor MUSÍ byť singleton na jeden stream blokov v jednom buckete. Dve inštancie = overlap issues, ktoré sa riešia ručne. Ak potrebujete škálovať, shardujte podľa labelov, nie replikami.
- Store Gateway a pamäť. Index cache a počet otvorených blokov vie OOM-núť. Sledujte ju, dajte jej rozumný
--index-cache-size, prípadne externý Memcached/Redis cache. - Receive back-pressure. Monitorujte
prometheus_remote_storage_*queue metriky. Keď queue rastie, Receive nestíha — škálujte ho skôr, než začnete strácať dáta.
Globálny pohľad v Grafane je nakoniec triviálny: jeden datasource mieriaci na Querier, a vidíte všetkých 20+ klastrov naraz, deduplikovane, s históriou na rok. Cesta k tomu ale nie je o pridaní komponentov — je o disciplíne pri cardinalite a rešpekte k tým trom gotchas vyššie.