Eksiksiz Observability (Gözlemlenebilirlik) Rehberi — Prometheus, Grafana, Loki & Jaeger
“LGTM+P” yığınını (Loki, Grafana, Tempo/Jaeger, Prometheus) derinlemesine ele alan pratik bir rehber — mimari, sorgu dilleri, alerting, dashboard tasarımı, tracing ve production seviyesinde best practice’ler.
İçindekiler
- Observability’nin Temelleri
- Prometheus
- Grafana
- Loki
- Jaeger ve Distributed Tracing
- Metrik, Log ve Trace’leri İlişkilendirme
- SLO, SLI ve Error Budget
- Production Kontrol Listesi
1. Observability’nin Temelleri
1.1 Üç Sütun (Three Pillars)
| Sütun | Cevapladığı Soru | Araç |
|---|---|---|
| Metrics (Metrikler) | “Bir sorun var mı, ne kadar kötü?” | Prometheus |
| Logs (Loglar) | “Tam olarak ne oldu?” | Loki |
| Traces (İzler) | “Nerede oldu, hangi serviste?” | Jaeger |
Observability, monitoring (izleme) ile aynı şey değildir. Monitoring, bilinen hata senaryolarının gerçekleştiğini söyler; observability ise yeni kod yazmadan sistem davranışı hakkında keyfi/yeni sorular sormanıza olanak tanır.
1.2 RED ve USE Metodolojileri
RED (istek tabanlı servisler için):
- Rate — saniyedeki istek sayısı
- Errors — saniyedeki başarısız istek sayısı
- Duration — gecikme (latency) dağılımı (p50/p90/p99)
USE (kaynaklar için — CPU, disk, memory, network):
- Utilization — kaynağın meşgul olduğu zaman yüzdesi
- Saturation — kuyruk derinliği / bekleyen iş yükü
- Errors — hata olayları
Genel kural: RED’i servislere, USE’u altyapı kaynaklarına uygulayın.
1.3 Dört Altın Sinyal (Google SRE)
Latency (gecikme), Traffic (trafik), Errors (hatalar), Saturation (doygunluk) — esasen RED + Saturation.
2. Prometheus
2.1 Mimari
┌────────────┐ scrape (pull) ┌──────────────┐
│ Target/ │◄─────────────────│ Prometheus │
│ Exporter │ │ Server │
└────────────┘ │ - TSDB │
│ - PromQL │
│ - Rule Engine │
└──────┬─────────┘
│ alert
┌──────▼─────────┐
│ Alertmanager │
└────────────────┘
- Prometheus pull tabanlıdır: yapılandırılmış bir aralıkta (varsayılan 15s/1dk)
/metricsHTTP endpoint’lerini kazır (scrape). - Push tabanlı iş yükleri için (batch job, lambda) Pushgateway kullanılabilir — ama dikkatli kullanın; tek hata noktası (single point of failure) haline gelir ve staleness (bayatlık) tespitini bozar.
- Local TSDB, veriyi disk üzerinde 2 saatlik bloklar halinde saklar, zamanla compact edilir. Varsayılan retention: 15 gün.
- Uzun süreli saklama / yatay ölçek / multi-tenancy için
remote_writeile Thanos, Cortex veya Grafana Mimir kullanın.
2.2 Veri Modeli
Her time series, bir metrik adı + key-value label çiftleri kümesiyle benzersiz şekilde tanımlanır:
http_requests_total{method="POST", handler="/api/orders", status="200"} 27
2.2.1 Dört Metrik Tipi
| Tip | Açıklama | Örnek |
|---|---|---|
| Counter | Monoton olarak artan değer; restart’ta 0’a döner | http_requests_total |
| Gauge | Artabilen veya azalabilen değer | node_memory_available_bytes |
| Histogram | Gözlemleri yapılandırılabilir bucket’lara örnekler, _sum ve _count içerir | http_request_duration_seconds |
| Summary | Histogram’a benzer ama quantile’ları (φ-quantile) client tarafında hesaplar | rpc_duration_seconds |
Histogram vs Summary — neredeyse her zaman histogram seçin:
- Histogram’lar instance’lar arası agregasyona izin verir (
histogram_quantile()toplanmış bucket’lar üzerinde); summary’ler bunu yapamaz — önceden hesaplanmış quantile’ların ortalaması alınamaz. - Summary’ler client tarafında daha ucuzdur, ama server tarafında keyfi quantile’lar için daha pahalıdır.
- Client kütüphaneniz destekliyorsa, yüksek çözünürlüklü ve düşük kardinalite maliyetli histogram’lar için native histogram’ları (Prometheus 2.40+) kullanın.
2.3 PromQL Derinlemesine
Instant Vector vs Range Vector
http_requests_total # instant vector (anlık değer)
http_requests_total[5m] # range vector (son 5 dakikadaki tüm örnekler)
Rate, Increase, IRate
rate(http_requests_total[5m]) # 5dk pencere üzerinde saniye başına ortalama artış — ALERTING/DASHBOARD İÇİN KULLANIN
irate(http_requests_total[5m]) # sadece son iki nokta kullanarak saniyelik oran — dalgalı, sadece hızlı değişen grafiklerde kullanın
increase(http_requests_total[1h]) # 1 saatlik toplam artış (rate * süre, ekstrapole edilmiş)
Altın kural: counter’lara
sum()ile agregasyon yapmadan önce her zamanrate()/irate()uygulayın. Ham counter’ları öncesum()edip sonrarate()almayın — bu, per-series reset tespitini bozar.
# DOĞRU
sum(rate(http_requests_total[5m])) by (service)
# YANLIŞ — counter reset yönetimini bozar
rate(sum(http_requests_total) by (service))[5m]
Agregasyon Operatörleri
sum(rate(http_requests_total[5m])) by (job)
avg(node_load1) by (instance)
max(up) by (job)
count(up == 0) # down olan target sayısı
topk(5, rate(http_requests_total[5m]))
bottomk(3, node_filesystem_free_bytes)
by belirtilen label’ları korur; without belirtilen label’ları kaldırıp geri kalan her şeyi tutar.
Histogram Quantile’ları
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service)
)
le(less-than-or-equal), bucket sınırını gösteren label’dır —by()içinde mutlaka korunmalıdır.- Sonuç interpole edilmiş bir tahmindir, kesin değildir — bucket sınırları büyük önem taşır. Bucket’ları gerçek SLO eşiklerinize göre seçin (ör. 0.1, 0.25, 0.5, 1, 2.5, 5, 10).
Binary Operatörler ve Vector Matching
# one-to-one: label kümeleri (metrik adı hariç) tam olarak eşleşmelidir
node_memory_MemFree_bytes / node_memory_MemTotal_bytes
# many-to-one, ignoring/group_left ile — metrikleri ekstra label ile zenginleştirme
sum(rate(http_requests_total[5m])) by (instance)
* on(instance) group_left(datacenter) node_meta
# eşleştirme için belirli label'ları göz ardı etme
rate(a[5m]) / ignoring(instance) rate(b[5m])
Faydalı Fonksiyonlar
predict_linear(node_filesystem_free_bytes[6h], 4*3600) # 4 saat sonra disk dolacak mı tahmini
deriv(some_gauge[10m]) # saniye başına türev
delta(cpu_temp_celsius[1h]) # aralık üzerindeki fark (gauge'lar için)
absent(up{job="critical-service"}) # metrik yoksa 1 döner — "sessiz hata" alert'leri için harika
changes(process_start_time_seconds[1h]) # restart sayısı
resets(some_counter[1h]) # counter reset sayısı
label_replace(up, "svc", "$1", "job", "(.*)-prod") # regex ile label manipülasyonu
clamp_min(my_metric, 0)
Subquery’ler
max_over_time(rate(http_requests_total[5m])[1h:1m])
Güçlü ama pahalıdır — dikkatli kullanın; tekrar eden ağır sorgular için recording rule tercih edin.
2.4 Service Discovery
Statik konfigürasyonlar ölçeklenmez. SD mekanizmalarını kullanın:
scrape_configs:
- job_name: kubernetes-pods
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
- source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
action: replace
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
target_label: __address__
Desteklenen SD: Kubernetes, Consul, EC2, Azure, GCE, DNS, dosya tabanlı (file_sd_configs — özel envanterler için ideal), Docker, OpenStack.
relabel_configs scrape öncesinde çalışır (__address__‘ı değiştirebilir, target’ları düşürebilir); metric_relabel_configs scrape sonrasında çalışır (yüksek kardinaliteli metrik/label’ları düşürme/yeniden adlandırma).
2.5 Recording Rule’lar
Pahalı/sık kullanılan sorguları önceden hesaplayıp yeni time series’lere dönüştürün:
groups:
- name: api_slos
interval: 30s
rules:
- record: job:http_requests:rate5m
expr: sum(rate(http_requests_total[5m])) by (job)
- record: job:http_request_errors:rate5m
expr: sum(rate(http_requests_total{status=~"5.."}[5m])) by (job)
- record: job:http_error_ratio:rate5m
expr: job:http_request_errors:rate5m / job:http_requests:rate5m
İsimlendirme kuralı: level:metric:operations (ör. job:http_errors:rate5m).
2.6 Alerting Rule’lar ve Alertmanager
groups:
- name: availability
rules:
- alert: HighErrorRate
expr: job:http_error_ratio:rate5m > 0.05
for: 10m
labels:
severity: critical
annotations:
summary: "{{ $labels.job }} üzerinde yüksek hata oranı"
description: "{{ $labels.job }} hata oranı {{ $value | humanizePercentage }} (eşik %5)"
- alert: InstanceDown
expr: up == 0
for: 5m
labels:
severity: warning
annotations:
summary: "{{ $labels.instance }} down durumda"
Önemli pratikler:
- Geçici sıçramalarda alert’in “flapping” (yanıp sönme) yapmaması için her zaman
for:ayarlayın. - Ham nedenler yerine (CPU%) semptomlar üzerinden (SLO burn, hata oranı, gecikme) alert oluşturun — nedenler dashboard’a aittir, sayfaya (page) değil.
- SLO tabanlı sayfalama için multi-window multi-burn-rate alert’leri kullanın (bkz. §7).
Alertmanager, routing, gruplama, tekilleştirme (deduplication), susturma (silencing) ve inhibition işlemlerini yönetir:
route:
receiver: default
group_by: [alertname, cluster]
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
- match: {severity: critical}
receiver: pagerduty
- match: {severity: warning}
receiver: slack
inhibit_rules:
- source_match: {severity: critical}
target_match: {severity: warning}
equal: [alertname, cluster, service]
2.7 Exporter’lar
| Exporter | Amaç |
|---|---|
node_exporter | Host seviyesi metrikler (CPU, memory, disk, network) |
blackbox_exporter | HTTP/DNS/TCP/ICMP üzerinden probe — sentetik kontroller |
kube-state-metrics | Kubernetes objelerinin durumu (deployment, pod vb — cAdvisor seviyesi kaynak kullanımı DEĞİL) |
cAdvisor | Container kaynak kullanım metrikleri |
mysqld_exporter / postgres_exporter | Veritabanı metrikleri |
redis_exporter | Redis metrikleri |
Genel amaçlı bir exporter ile sarmalamak yerine kendi uygulama kodunuzu bir client kütüphanesi (client_golang, client_python, Node için prom-client, Java için simpleclient) ile enstrümante etmeyi tercih edin — direkt enstrümantasyon iş (business) seviyesinde metrikler sağlar.
2.8 Kardinalite — Prometheus’un #1 Katili
Her benzersiz label-değer kombinasyonu yeni bir time series demektir. Yaygın kardinalite bombaları:
- Label değeri olarak kullanıcı ID’leri, request ID’leri, e-posta adresleri
- Sınırsız URL path’leri (
/users/12345yerine/users/:idkullanın) - Label olarak tam stack trace veya hata mesajları
- Büyük ölçekte IP adresleri
Genel kural: instance başına aktif seri sayısını en fazla tek haneli milyonlar seviyesinde tutun; metrik başına kardinalite tahmin edilebilir ve sınırlı olmalıdır. Denetim için count({__name__=~".+"}) ve topk(10, count by (__name__)({__name__=~".+"})) kullanın.
2.9 Federation, Remote Write ve Uzun Süreli Saklama
- Federation (
/federateendpoint’i): alt seviye Prometheus’lardan agregasyonlu metrikleri global bir Prometheus’a çeker. Hiyerarşik rollup’lar için iyidir, ham uzun süreli saklama için değil. remote_write: tüm (veya filtrelenmiş) örnekleri, kalıcı, yatay ölçeklenebilir, uzun retention’lı ve global sorgu görünümü sağlayan Thanos/Mimir/Cortex/VictoriaMetrics’e gönderir.- Thanos: Sidecar + Store Gateway + Compactor + Querier, object storage (S3/GCS) destekli. HA Prometheus replikaları arasında dedup yapar.
- Grafana Mimir: Prometheus remote_write/PromQL ile tam uyumlu, yatay ölçeklenebilir, tasarım gereği multi-tenant.
3. Grafana
3.1 Veri Kaynakları (Data Source)
Grafana, birçok backend’in üzerinde bir görselleştirme ve alerting katmanıdır: Prometheus, Loki, Jaeger/Tempo, Elasticsearch, InfluxDB, MySQL/Postgres, CloudWatch ve daha fazlası. UI üzerinden veya kod olarak yapılandırılabilir:
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
jsonData:
timeInterval: 30s
exemplarTraceIdDestinations:
- name: trace_id
datasourceUid: jaeger-uid
3.2 Dashboard Tasarım Best Practice’leri
- Yukarıdan aşağıya düzen: özet satır (RED/USE özeti) → altında detaya inen satırlar.
- Bir dashboard, bir amaç. İlgisiz servisleri tek bir dashboard’a sıkıştırmayın.
- İkincil detay panelleri için satır (row) daraltmayı kullanın.
- Aynı tip panellerde tutarlı birim ve eşikler kullanın (gecikme her zaman
s/ms, asla karışık olmasın). - “Grafik çorbasından” kaçının: dashboard başına panel sayısını sınırlayın (~12–20); daha fazla panel eklemek yerine detay dashboard’a drill-down linkini tercih edin.
- Dashboard sorgularında sabit
[5m]yazmak yerine$__rate_intervalkullanın — seçilen zaman aralığına ve scrape interval’ına otomatik uyum sağlar. - Deploy/olayları Annotations API ile işaretleyin, böylece dashboard’lar nedensel olarak ilgili işaretleri gösterir.
3.3 Değişkenler (Variables) ve Templating
Değişken: $datacenter → label_values(up, datacenter)
Değişken: $instance → label_values(up{datacenter="$datacenter"}, instance)
Değişken: $interval → interval tipi: 1m,5m,10m,30m,1h
Multi-value + Include All seçeneklerini dikkatli kullanın — “All” genellikle büyük bir regex sorgusu üretir (=~"a|b|c|...") ve yavaş olabilir; önce kapsamı daraltmak için zincirlenmiş (chained) değişkenleri düşünün.
3.4 Kod Olarak Provisioning
Dashboard’ları JSON (veya Jsonnet/grafonnet) olarak git’te saklayın, şu şekilde provision edin:
apiVersion: 1
providers:
- name: default
folder: Platform
type: file
options:
path: /etc/grafana/provisioning/dashboards
grafonnet/jsonnet, dashboard’ları şablonlaştırmanıza olanak tanır (servisler arasında DRY paneller) — JSON’u elle düzenlemek yerine ölçekte yaygın olarak kullanılır.
3.5 Unified Alerting
Legacy dashboard alert’lerinden farklı olarak, Grafana yönetimli alert kuralları herhangi bir veri kaynağını (sadece Prometheus değil) sorgulayabilir.
# Alert rule (kavramsal)
condition: B
data:
- refId: A
query: sum(rate(http_requests_total{status=~"5.."}[5m]))
- refId: B
reduceExpression: last(A)
threshold: "> 10"
for: 5m
Bildirim politikaları, Alertmanager’ın ağaç tabanlı routing yapısını yansıtır (label matcher’ları → contact point’ler) ve zaten Prometheus çalıştırıyorsanız Grafana, harici bir Alertmanager’a proxy yapabilir.
3.6 Bilinmesi Gereken Panel Tipleri
- Time series — varsayılan, en yaygın kullanılan.
- Stat / Gauge — tek bir anlık değer, SLO/error-budget başlık rakamları için harika.
- Heatmap — histogram bucket dağılımlarını zaman içinde görselleştirmek için ideal (latency heatmap’leri).
- Table — top-N kırılımları, log tail benzeri görünümler için.
- Node graph — servis bağımlılık görselleştirmesi (trace verisiyle iyi eşleşir).
- Logs panel — canlı tailing ile native Loki log satırı render’ı.
- Traces panel — bir Jaeger/Tempo trace waterfall’ını içeride render eder.
3.7 Exemplar’lar
Exemplar’lar, belirli bir histogram gözlemine bir trace_id ekler ve latency grafiğinin üzerinde nokta olarak render edilir — tıklayarak doğrudan Jaeger/Tempo’daki trace’e geçebilirsiniz. Gereksinimler:
- Client kütüphane desteği (OpenMetrics exemplar formatı)
- Prometheus veri kaynağında yapılandırılmış
exemplarTraceIdDestinations
4. Loki
4.1 Felsefe
Loki, Elasticsearch’ten farklı olarak sadece metadata’yı (label’ları) indeksler, tam log metnini değil. Bu, ölçekte çalıştırmayı çarpıcı biçimde ucuzlatır; bedeli ise full-text arama’nın daha yavaş olmasıdır (sorgu zamanında ters indeks yerine sıkıştırılmış chunk’lar üzerinde grep yapar).
“Prometheus gibi, ama loglar için” — aynı label modeli, PromQL’den ilham alan aynı sorgu dili (LogQL).
4.2 Mimari
Promtail/Alloy/Fluent Bit → Distributor → Ingester → Chunk'lar (object storage: S3/GCS)
│
Compactor
│
Querier ◄── Query Frontend ◄── Grafana
- Distributor: log akışlarını alır, doğrular, ingester’lara hash’ler.
- Ingester: chunk’lara batch/sıkıştırma yapar, object storage’a flush eder.
- Querier: LogQL çalıştırır, storage’dan chunk’ları + ingester’lardan son verileri getirir.
- Compactor: index’i birleştirir/tekilleştirir, retention uygular.
- Dağıtım modları: monolithic (tek binary, küçük ölçek), simple scalable (read/write ayrımı), microservices (tam bileşen ayrımı, büyük ölçek).
4.3 LogQL
Log Stream Selector (PromQL label matcher’ları gibi)
{app="checkout", env="prod"}
{app="checkout"} |= "error"
{app="checkout"} != "healthcheck"
{app="checkout"} |~ "error|panic|fatal"
{app="checkout"} |= "error" | json | line_format "{{.msg}}"
Operatörler: |= içerir, != içermez, |~ regex eşleşir, !~ regex eşleşmez.
Parser’lar
{app="api"} | json # JSON log satırlarını label'lara ayrıştır
{app="api"} | logfmt # logfmt satırlarını ayrıştır
{app="api"} | pattern "<ip> - - <_> \"<method> <uri>"
{app="api"} | regexp "(?P<level>\\w+): (?P<msg>.*)"
Label Filtreleri ve Satır Formatlama
{app="api"} | json | status_code >= 500
{app="api"} | json | duration > 1s
{app="api"} | json | line_format "{{.timestamp}} {{.level}} {{.msg}}"
{app="api"} | json | label_format new_label="{{.old_label}}"
Metrik Sorguları (logları sayılara agrege etme)
sum(rate({app="api"} |= "error" [5m])) by (pod)
sum(count_over_time({app="api"}[5m])) by (level)
topk(5, sum(rate({app="api"}[5m])) by (route))
# unwrap: log satırındaki sayısal bir alanı agregasyon için değere çevirir
quantile_over_time(0.99,
{app="api"} | json | unwrap duration_ms [5m]
) by (route)
4.4 Promtail / Grafana Alloy Yapılandırması
scrape_configs:
- job_name: kubernetes-pods
kubernetes_sd_configs:
- role: pod
pipeline_stages:
- docker: {}
- json:
expressions:
level: level
msg: message
- labels:
level:
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_app]
target_label: app
Grafana Alloy, Promtail’in modern halefidir (birçok pipeline için OTel Collector kullanım senaryosunu da karşılar) — yeni deployment’lar genellikle Alloy’u tercih etmelidir.
4.5 Label Kardinalitesi — Prometheus’tan Bile Daha Kritik
Loki’nin index’i label kümelerinden oluşturulur ve her benzersiz label kombinasyonu yeni bir stream yaratır. Yüksek kardinaliteli değerleri (user_id, request_id, trace_id) label olarak koymak index’i patlatır ve performansı/maliyeti mahveder. Bunun yerine:
- Label’ları düşük kardinaliteli, statik boyutlarla sınırlı tutun:
app,env,namespace,pod(sınırlı),level. - Yüksek kardinaliteli alanları (user_id, trace_id, request_id) log satırı gövdesine koyun, sorgu zamanında
| json/| logfmt/|=ile filtreleyin/çıkarın. - Timestamp, UUID veya tam URL gibi dinamik label’lardan kaçının.
4.6 Retention ve Compaction
limits_config:
retention_period: 720h # 30 gün
compactor:
retention_enabled: true
delete_request_store: s3
Tenant başına override’lar, kritik audit loglarını debug loglarından daha uzun süre saklamanıza olanak tanır.
5. Jaeger ve Distributed Tracing
5.1 Temel Kavramlar
- Trace: bir isteğin servisler arasındaki tüm yolculuğu.
- Span: bir trace içindeki tek bir iş birimi (ör. bir HTTP çağrısı, bir DB sorgusu) — bir isim, başlangıç/bitiş zamanı, tag’ler, log’lar ve bir parent span referansı içerir.
- Context propagation (bağlam yayılımı): trace/span ID’lerinin süreç sınırları arasında header’lar ile taşınması (W3C Trace Context’te
traceparent, veya Jaeger’ın kendiuber-trace-id‘si). - Root span: trace’teki ilk span (parent’ı yoktur).
Trace: abc123
├── span: gateway (0-120ms)
│ ├── span: auth-service (5-20ms)
│ └── span: order-service (25-115ms)
│ ├── span: db-query (30-70ms)
│ └── span: payment-service (75-110ms)
5.2 Mimari
Uygulama (SDK/OTel) → Jaeger Agent/OTel Collector → Jaeger Collector → Storage (Elasticsearch/Cassandra/Kafka buffer) → Query Service → Jaeger UI / Grafana
- Client/SDK: kodu enstrümante eder, span’ler üretir.
- Agent (eski) veya OTel Collector (modern): span’leri batch’leyip ileten yerel sidecar/daemonset.
- Collector: doğrular, işler (sampling, zenginleştirme), storage’a yazar.
- Storage: production için Cassandra veya Elasticsearch; dev için Badger/in-memory.
- Query: trace alımı için UI ve API sağlar.
Modern öneri: kodu OpenTelemetry SDK’ları ile (vendor-neutral) enstrümante edin, OTLP üzerinden bir OpenTelemetry Collector‘a export edin; bu collector da Jaeger’a (veya Grafana Tempo’ya) yönlendirir. Uygulama kodunu artık Jaeger’ın native client kütüphanelerine bağlamayın — bunlar bakım modundadır; standart artık OTel’dir.
5.3 Enstrümantasyon
// Go + OpenTelemetry örneği
tracer := otel.Tracer("order-service")
ctx, span := tracer.Start(ctx, "ProcessOrder")
defer span.End()
span.SetAttributes(
attribute.String("order.id", orderID),
attribute.Int("order.item_count", len(items)),
)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
}
- Çoğu framework/dil için auto-instrumentation mevcuttur (Java agent, Python
opentelemetry-instrument, Node@opentelemetry/auto-instrumentations-node) — elle span yazmadan önce buradan başlayın. - Asenkron sınırlar (mesaj kuyrukları, goroutine’ler) arasında context’i manuel olarak yayın — bu, bozuk/yetim (orphaned) trace’lerin #1 kaynağıdır.
5.4 Sampling Stratejileri
Ölçekte her isteği trace etmek pahalıdır (storage + overhead). Seçenekler:
| Strateji | Açıklama | Trade-off |
|---|---|---|
| Head-based, probabilistic | Trace’lerin X%‘ini trace başlangıcında örnekle | Basit, ama nadir hataları kaçırabilir |
| Rate limiting | Servis başına saniyede N trace | Öngörülebilir maliyet |
| Tail-based sampling | Tüm trace’i buffer’la, sonucu gördükten sonra karar ver (ör. hataları/yavaş trace’leri her zaman tut) | Daha iyi sinyal, buffer yapan bir collector gerektirir (OTel Collector tail_sampling processor) — daha fazla altyapı |
| Adaptive sampling | Jaeger collector, hedef hacme ulaşmak için endpoint başına oranı ayarlar | Düşük ve yüksek trafikli servisleri dengeler |
Best practice: düşük head-based sample oranı (ör. %1-10) ile, head kararından bağımsız olarak hata trace’lerini ve belirli bir gecikme eşiğinin üzerindekileri her zaman tutan tail-based sampling kurallarını birleştirin.
5.5 Context Propagation Formatları
- W3C Trace Context (
traceparent,tracestateheader’ları) — modern standart, varsayılan olarak bunu kullanın. - B3 (Zipkin kökenli,
X-B3-*header’ları) — hala eski Istio/Envoy kurulumlarında yaygın. - Jaeger native (
uber-trace-id) — eski (legacy), yeni sistemlerde kaçının.
5.6 Trace’leri Metrik/Loglarla İlişkilendirme
- Her log satırında
trace_idvespan_id‘yi yapılandırılmış (structured) alanlar olarak ekleyin (çoğu logging kütüphanesinde OTel-aware formatter/hook mevcuttur). - Yavaş bir latency bucket’ını doğrudan bir trace’e bağlamak için Prometheus histogramlarında exemplar’ları kullanın.
- Grafana Tempo (ve Jaeger veri kaynağı), veri kaynağı ayarlarında yapılandırılan “Trace to logs” ve “Trace to metrics” panel bağlantılarını destekler.
6. Metrik, Log ve Trace’lerin Korelasyonu
Observability araçlarının gerçek gücü, tek başına herhangi bir sütundan değil, çapraz gezinmeden (cross-navigation) gelir.
Alert tetiklenir (Prometheus/Alertmanager)
→ Dashboard hata oranı sıçramasını gösterir (Grafana)
→ Latency panelindeki exemplar noktasına tıkla → yavaş Trace'e atla (Jaeger)
→ Trace hangi span/servisin yavaş olduğunu gösterir
→ "Bu span için loglar"a tıkla → trace_id'ye göre filtrelenmiş Loki sorgusu (Loki)
→ Kök neden log satırında bulunur
Uygulama kontrol listesi:
-
trace_id,span_id,service,envalanlarıyla yapılandırılmış (JSON) logging. - Prometheus/Loki arasında tutarlı label isimlendirmesi (
service,namespace,pod). - Latency histogramlarında exemplar’lar etkin.
- Grafana veri kaynağı bağlantıları yapılandırılmış: Prometheus→Jaeger (exemplar’lar), Jaeger→Loki (trace-to-logs), Loki→Jaeger (
trace_id‘nin| jsonile çıkarılıp derived field olarak bağlanması). - Loki veri kaynağı yapılandırmasında, loglardaki
trace_idmetnini tıklanabilir trace linklerine çeviren derived field’lar.
7. SLO, SLI ve Error Budget
7.1 Tanımlar
- SLI (Service Level Indicator): ölçülen bir metrik, ör. başarılı isteklerin oranı.
- SLO (Service Level Objective): bir SLI için bir pencere üzerindeki hedef, ör. “30 gün boyunca isteklerin %99.9’u başarılı.”
- Error budget:
1 - SLO— özellik geliştirmeyi durdurup güvenilirliğe odaklanmadan önce izin verilen “kötülük” miktarı.
7.2 Örnek SLI Sorguları
# Erişilebilirlik SLI
sum(rate(http_requests_total{status!~"5.."}[5m]))
/ sum(rate(http_requests_total[5m]))
# Gecikme SLI (isteklerin %'si 300ms altında)
sum(rate(http_request_duration_seconds_bucket{le="0.3"}[5m]))
/ sum(rate(http_request_duration_seconds_count[5m]))
7.3 Multi-Window, Multi-Burn-Rate Alerting
Hata oranı üzerine naif eşik alert’leri ya çok geç ateşlenir (günler süren yavaş yanma) ya da çok sık (kısa sıçramalarda gürültülü). Standart SRE deseni, önem derecesi başına iki zaman penceresi kullanır:
# Hızlı yanma: bütçenin 14.4x'ini tüketiyor → 30 günlük bütçeyi 2 saatte bitirir
- alert: ErrorBudgetBurnFast
expr: |
(
job:http_error_ratio:rate5m > 14.4 * 0.001
and
job:http_error_ratio:rate1h > 14.4 * 0.001
)
labels: {severity: critical}
# Yavaş yanma: 6sa/3g üzerinden bütçenin 1x'ini tüketiyor → sadece sürdürülürse sayfala
- alert: ErrorBudgetBurnSlow
expr: |
(
job:http_error_ratio:rate1h > 1 * 0.001
and
job:http_error_ratio:rate6h > 1 * 0.001
)
labels: {severity: warning}
Bu, doğrudan Google’ın SRE workbook’undaki multi-window burn-rate metodolojisinden gelir — tespit hızı ile hassasiyet (yanlış sayfaları önleme) arasında denge kurar.
8. Production Kontrol Listesi
Prometheus
- Flapping’i önlemek için tüm alert’lerde
for:ayarlanmış - Pahalı/sık kullanılan dashboard sorguları için recording rule’lar
- Kardinalite düzenli olarak denetleniyor (
topk(10, count by (__name__)({__name__=~".+"}))) - 15 günden uzun retention gerekiyorsa uzun süreli storage’a remote-write (Thanos/Mimir/Cortex)
- Thanos/Mimir katmanında dedup ile HA Prometheus server çifti
- Alertmanager routing amtool ile test edilmiş; alert fırtınalarını azaltmak için inhibition kuralları
Grafana
- Dashboard’lar kod olarak, versiyon kontrolünde
- Sabit pencereler yerine
$__rate_intervalkullanılıyor - Klasör/izin yapısı takım sahipliğiyle eşleşiyor
- Exemplar’lar + trace-to-logs bağlantıları yapılandırılmış
Loki
- Label’lar düşük kardinaliteli tutuluyor; yüksek kardinaliteli alanlar log gövdesinde bırakılıyor
- Tenant/namespace başına retention politikası
- Alloy/Promtail pipeline aşamaları doğru label çıkarımı için test edilmiş
Jaeger/Tracing
- OpenTelemetry SDK/auto-instrumentation mevcut
- Asenkron sınırlar (kuyruklar, worker’lar) arasında context propagation doğrulanmış
- Tail-based sampling, hata/yavaş trace’lerin %100’ünü tutuyor
- trace_id, yapılandırılmış loglara enjekte ediliyor
Genel
- Multi-window burn-rate alert’leri ile tanımlanmış SLO’lar
- Her alert annotation’ından runbook’lara bağlantı
- Nöbetçi (on-call) dashboard’ları aksiyona dönüştürülebilir, semptom seviyesindeki sinyallerle sınırlı
Rehberin sonu. İki dilli takımlar için İngilizce eşlik eden belge ile birlikte kullanın.