Overview
Opis
Źródło: https://grafana.com/oss/tempo/
Tempo to wysokowydajny, ekonomiczny rozproszony backend do tracingu. Jest głęboko zintegrowany z Grafaną, Prometheusem, Loki i Mimirem. Tempo wymaga jedynie object storage do działania, co czyni go niezwykle tanim i prostym w utrzymaniu.
Kluczowe funkcjonalności
| Funkcjonalność | Opis | Status w naszym setupie |
|---|---|---|
| Distributed Tracing | Przechowywanie i odpytywanie trace’ów z dowolnej aplikacji | ✅ Skonfigurowane |
| TraceQL | Język zapytań do trace’ów (podobny do SQL) | ✅ Skonfigurowane |
| Metrics Generator | Generowanie metryk z trace’ów (metryki RED, grafy serwisów) | ✅ Skonfigurowane |
| Traces to Metrics | Powiązanie trace’ów z metrykami w Grafanie | ✅ Skonfigurowane |
| Traces to Logs | Powiązanie trace’ów z logami (Loki) w Grafanie | ✅ Skonfigurowane |
| Traces to Profiles | Powiązanie trace’ów z profilami (Pyroscope) w Grafanie | ✅ Skonfigurowane |
| Service Graphs | Automatyczna wizualizacja zależności między serwisami | ✅ Skonfigurowane |
| TraceQL Metrics (Drilldown) | Metryki w czasie rzeczywistym z zapytań TraceQL | ✅ Skonfigurowane |
| Multi-protocol Ingestion | OTLP, Jaeger, Zipkin, OpenCensus | ✅ Skonfigurowane |
| MCP Server | Serwer Model Context Protocol dla asystentów AI | ✅ Skonfigurowane |
Tryby wdrożenia
- Monolithic mode
- Wszystkie komponenty w jednym procesie
- Dobre dla: lokalnego developmentu, małych obciążeń (~20GB trace’ów/dzień)
- Może skalować się horyzontalnie przez gossip ring
- Simple Scalable (domyślny)
- Rozdziela ścieżki odczytu i zapisu
- Dobre dla: do kilku TB/dzień
- Microservices mode (nasz setup)
- Każdy komponent wdrożony oddzielnie
- Najlepsze dla: produkcji, high availability, dużej skali
- Pełna kontrola nad skalowaniem każdego komponentu
Komponenty

Distributor
- Punkt wejścia dla ingescji trace’ów — przyjmuje spany w wielu formatach (OTLP, Jaeger, Zipkin, OpenCensus)
- Pod spodem korzysta z warstwy receiverów z frameworka OpenTelemetry Collector
- Routuje spany do Ingesterów przez hashowanie
traceIDi użycie rozproszonego consistent hash ringa - Zalecany format to OTel Proto (OTLP) ze względu na wydajność — dlatego Grafana Alloy używa OTLP exportera/receivera do wysyłania spanów do Tempo
- Stateless — można skalować niezależnie
Ingester
- Odbiera spany od Distributora i partycjonuje atrybuty spanów/zasobów do schematu Parquet w celu wydajnego odczytu
- Grupuje trace’y w bloki przez skonfigurowany okres czasu lub do osiągnięcia maksymalnego rozmiaru bloku
- Generuje bloom filtry i indeksy dla każdego bloku, a następnie flushuje wszystko do backend object storage
- Struktura bloków w storage:
<bucketname>/<tenantID>/<blockID>/meta.json /index /data /bloom_0 ... bloom_n - W skalowanych wdrożeniach tworzy mechanizmy redundancji (niedostępne w trybie monolitycznym)
- Uczestniczy w gossip ringu do synchronizacji stanu klastra
- Hostuje procesory Metrics Generatora
Query Frontend
- Główny interfejs zapytań — obsługuje wyszukiwanie trace’ów po ID (
GET /api/traces/<traceID>) oraz filtrowanie przez TraceQL - Sharduje przestrzeń wyszukiwania — dzieli przestrzeń blockID na konfigurowalne shardy
- Rozrzuca shardowane requesty na jeden lub więcej Querierów równolegle, żeby przyspieszyć odpowiedź
- Łączy dane spanów zwrócone z wielu Querierów w jedną spójną odpowiedź
- Zapewnia kolejkowanie zapytań, retry i opcjonalne cachowanie
- Gdy osiągnięty zostanie limit trace’ów — zwraca dotychczasowe wyniki i anuluje pozostałe requesty do Querierów
Querier
- Wykonuje faktyczne przeszukiwanie danych w blokach, żeby znaleźć pasujące spany
- Odpytuje Ingestery o ostatnio przyjęte trace’y (jeszcze nie zflushowane do storage)
- Pobiera bloom filtry i indeksy z backend storage, żeby efektywnie zlokalizować trace’y w blokach object storage
- To podejście z dwóch źródeł zapewnia dostęp zarówno do świeżych, jak i historycznych danych
- Zapytania powinny zawsze trafiać do Query Frontend, a nie bezpośrednio do Queriera
- Więcej Querierów = szybsze odpowiedzi na zapytania
Compactor
- Uruchamiany w zaplanowanych interwałach — kompresuje, deduplikuje i reorganizuje bloki zapisane przez Ingestery
- Uwzględnia dane w ramach konkretnych trace’ów, żeby zminimalizować przyszłą przestrzeń wyszukiwania
- Wymusza polityki retencji — usuwa dane po upływie skonfigurowanego czasu życia
- Zmniejsza koszty storage i poprawia wydajność zapytań
- Stopniowo zastępowany przez architekturę Scheduler/Worker (patrz niżej)
Scheduler & Worker (nowa architektura)
- Zaprojektowane, żeby ostatecznie zastąpić Compactor bardziej deterministycznym podejściem z mniejszą duplikacją
- Scheduler: odpowiada za planowanie i śledzenie zadań przydzielanych Workerom. Powinien działać tylko jeden scheduler naraz
- Worker: łączy się ze Schedulerem przez gRPC, odbiera i wykonuje przydzielone zadania (obecnie kompakcja i retencja), raportuje status z powrotem
- Workery utrzymują blocklistę dla wszystkich tenantów (wcześniej odpowiedzialność Compactora) i koordynują odpytywanie tenantów przez ring
- Migracja: skaluj Workery w górę, jednocześnie skalując Compactor do zera, żeby uniknąć konfliktów
Metrics Generator
- Opcjonalny komponent generujący metryki z danych trace’ów
- Procesory:
- span-metrics: metryki RED (Rate, Errors, Duration) per serwis/operacja
- service-graphs: zależności między serwisami
- local-blocks: metryki TraceQL dla widoku Drilldown
- Wysyła wygenerowane metryki do endpointu remote write (Mimir/Prometheus)
Gateway
- Reverse proxy (Nginx)
- Routuje ruch HTTP/gRPC do odpowiednich komponentów
- Pojedynczy punkt wejścia dla zewnętrznego dostępu