Haziran 6 2026

Dört Altın Sinyal Öldü Mü? Determinimistik Olmayan Altyapılarda Yeni Nesil Telemetri Tasarımı

Google SRE Book hayatımıza girdiğinden beri latency, traffic, errors ve saturation dörtlüsünü (yani golden signals) kutsal kase olarak kabul ettik. Ancak günümüzün non-deterministic infrastructure (deterministik olmayan altyapı) ve dinamik microservices monitoring dünyasında, bu statik metrikler artık sistemin gerçek sağlığını göstermekte yetersiz kalıyor. Modern ve dinamik sistemlerde ayakta kalabilmek için observability odaklı, yeni nesil bir telemetry design yaklaşımına geçmek zorundayız.

Peki ama neden? Çünkü artık “sunucu ayakta mı?” sorusu anlamsız. Kubernetes cluster’ınızda bir pod ölüyor, yerine yenisi geliyor; serverless fonksiyonlar milisaniyeler içinde cold start yaşayıp kayboluyor. Bu yazıda, geleneksel dört altın sinyalin neden yetersiz kaldığını ve modern dağıtık mimarilerde nasıl bir telemetri mimarisi kurmanız gerektiğini teknik detaylarıyla inceleyeceğiz.

Klasik Metriklerin Epistemolojik Çöküşü

Eski dünyada her şey basitti. Bir bare-metal sunucumuz, bir load balancer’ımız ve bir veritabanımız vardı. CPU %90’a ulaştığında disk IOPS limitine yaklaşıyorduk ve bu net bir alarm sebebiydi. Bugün ise CPU kullanımı tek başına hiçbir şey ifade etmiyor. Container bazlı kısıtlamalar (CFS throttling), multi-tenant yapılar ve mikroservislerin birbirleriyle olan asenkron ilişkileri, klasik “saturation” metriğini anlamsızlaştırıyor.

Deterministik olmayan altyapılarda bir hata (error), genellikle tek bir bileşenin çökmesinden değil; ağ gecikmesi, rate limitler, queue derinliği ve veri tutarsızlığı gibi birden fazla faktörün bir araya gelmesiyle (emergent behavior) oluşur. Dolayısıyla, sadece giriş ve çıkış kapılarını izleyerek içeride ne olduğunu anlamamız imkansızdır.

Dört Altın Sinyal Nerede Patlıyor?

Klasik dört altın sinyali tek tek masaya yatıralım ve modern mimarideki sınırlarını görelim:

  • Latency (Gecikme): Ortalama (mean) latansı ölçmek bir yalandır. p99 veya p99.9 latansı bile bazen yanıltıcı olabilir. Eğer bir istek arka planda 50 farklı mikroservise dokunuyorsa, kümülatif p99 latansı tamamen kontrol dışı kalır. Bize gereken, istek bazlı bağlamsal gecikmedir (contextual latency).
  • Traffic (Trafik): Saniyedeki istek sayısı (RPS) tek başına sistem yükünü açıklamaz. 1000 basit read isteği ile 10 tane yoğun write/join isteği sistemde aynı saturation’ı yaratmaz.
  • Errors (Hatalar): HTTP 500’leri yakalamak kolaydır. Peki ya 200 OK dönen ama boş gövde (empty payload) gönderen veya mantıksal olarak hatalı çalışan servisler? Ya da circuit breaker devreye girdiği için hızlıca hata dönen (“fail-fast”) ama aslında downstream servislerin ayakta olduğu durumlar?
  • Saturation (Doygunluk): Kubernetes ortamında bir container’ın CPU limiti (limit ve request farkı) yüzünden throttle edilmesi, host makinenin CPU’sunun boş olmasına rağmen uygulamanın yavaşlamasına neden olur. Klasik host-level saturation izleme burada tamamen kör kalır.

Yeni Nesil Telemetry Design: OpenTelemetry ile Bağlamsal İzleme

Çözüm, metrik, log ve trace verilerini birbirinden bağımsız adalar olarak toplamayı bırakmaktır. Modern bir telemetry design, yüksek kardinaliteye (high cardinality) ve bağlam geçişine (context propagation) dayanmalıdır.

Aşağıdaki OpenTelemetry Collector konfigürasyon bloğu, deterministik olmayan bir ortamda metrik ve trace verilerini nasıl zenginleştirebileceğimizi ve sistem üzerindeki “gürültüyü” nasıl azaltabileceğimizi gösteriyor. Burada özellikle transform processor’ını kullanarak dinamik etiketleme (enrichment) yapıyoruz:


receivers:
  otlp:
    protocols:
      grpc:
      http:

processors:
  batch:
    send_batch_size: 8192
    timeout: 5s
    send_batch_max_size: 10240

  memory_limiter:
    check_interval: 1s
    limit_percentage: 75
    spike_limit_percentage: 15

  resourcedetection:
    detectors: [env, gcp, ecs, ec2, k8snode]
    timeout: 2s

  transform:
    error_mode: ignore
    metric_statements:
      - context: datapoint
        statements:
          - set(attributes["env"], "production")
          - set(attributes["service.tier"], "critical") where attributes["service.name"] == "payment-gateway"

exporters:
  otlp/prometheus:
    endpoint: "prometheus-gateway:9090"
    tls:
      insecure: true
  otlp/jaeger:
    endpoint: "jaeger-collector:4317"
    tls:
      insecure: true

service:
  pipelines:
    metrics:
      receivers: [otlp]
      processors: [memory_limiter, resourcedetection, transform, batch]
      exporters: [otlp/prometheus]
    traces:
      receivers: [otlp]
      processors: [memory_limiter, resourcedetection, batch]
      exporters: [otlp/jaeger]

Bu konfigürasyondaki resourcedetection adımı kritiktir. Uygulamanın hangi k8s podunda, hangi cloud provider üzerinde çalıştığını koda dokunmadan telemetri verisine enjekte eder. Bu sayede, deterministik olmayan altyapılardaki transient (geçici) hataları analiz ederken “Altyapısal bir dalgalanma mı oldu yoksa kodda mı hata var?” sorusuna anında yanıt bulabiliriz.

Dinamik Eşik Değerleri ve Anomalilerle Mücadele

Geleneksel alerting sistemleri statik threshold (eşik değer) mantığına dayanır. Örneğin: “CPU %80’i geçerse alarm ver.” Ancak auto-scale olan bir cluster’da bu kural sabaha karşı anlamsız alarmlarla uyanmanıza sebep olur. Bunun yerine PromQL kullanarak dinamik standart sapma (standard deviation) bazlı anomalileri tespit etmeliyiz.

Aşağıdaki PromQL sorgusu, bir servisin son 5 dakikadaki latansını (p99), son 1 haftalık geçmiş verinin ortalaması ve standart sapmasıyla karşılaştırır. Eğer mevcut latans, tarihsel sapmanın 3 katından fazlaysa bu gerçek bir anomalidir:


(
  histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service))
  -
  avg_over_time(histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service))[1w])
)
/
stddev_over_time(histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service))[1w])
> 3

Neden böyle bir sorgu kullanmalıyız? Çünkü her pazartesi saat 09:00’da sistemde doğal bir trafik artışı (ve dolayısıyla latans yükselmesi) oluyorsa, statik bir alert tetiklenir. Ancak bu dinamik sorgu, geçmiş pazartesi verilerini de hesaba katarak sistemi tolere eder. Böylece SRE ekibinin alert fatigue (alarm yorgunluğu) yaşamasının önüne geçilmiş olur.

eBPF: Altyapıyı Kodsuz Gözlemlemek

Uygulama seviyesinde SDK entegrasyonu yapmak her zaman mümkün veya pratik olmayabilir. Özellikle legacy servislerin veya üçüncü parti bileşenlerin (örneğin redis, nginx veya veri tabanları) telemetri verilerini toplamak ciddi bir operasyonel yük getirir.

İşte burada devreye eBPF (Extended Berkeley Packet Filter) giriyor. Kernel seviyesinde çalışan eBPF probaları sayesinde uygulamaya tek bir satır kod eklemeden, ağ paketlerini ve sistem çağrılarını (syscalls) intercept ederek ultra-low overhead ile telemetri üretebiliriz.

Örneğin, bir Kubernetes cluster’ında tüm podlar arasındaki TCP handshake sürelerini ve paket kayıplarını eBPF tabanlı bir tool (örn. Hubble/Cilium veya Pixie) ile izlediğinizde, “Ağda paket kaybı var ama hangi podlar etkileniyor?” sorusuna sıfır kod değişikliğiyle yanıt bulursunuz. Bu, modern observability vizyonunun en kritik yapı taşlarından biridir.

Sonuç: Yeni Nesil Telemetri Tasarımı İçin Yol Haritası

Dört altın sinyal tamamen ölmedi, ancak artık tek başlarına yetersizler. Onları sistemin genel durumunu gösteren kaba göstergeler olarak tutmalı, altını ise bağlamsal izleme ile doldurmalıyız. Başarılı bir yeni nesil telemetri tasarımı için şu adımları uygulamalısınız:

  1. Log, metrik ve trace’leri tek bir trace_id ve span_id ile birbirine bağlayın (Context Propagation).
  2. Kardinalite korkusunu aşın. Etiketlerde (tags/attributes) dinamik verileri (customer_id, k8s_pod_name, deployment_version) kullanmaktan çekinmeyin.
  3. Statik threshold alarmlarından kaçının; anomali tespiti için matematiksel modelleri (Z-score, Holt-Winters) PromQL seviyesinde uygulayın.
  4. eBPF teknolojisini altyapı gözlemlenebilirliği için bir standart haline getirin.

Unutmayın, sistemleriniz artık deterministik değil. Onları izleme yöntemleriniz de doğrusal ve durağan olamaz.

Category: Genel | LEAVE A COMMENT
Mart 7 2025

Mikroservis Labirentinde Kaybolmamak: OpenTelemetry ve Jaeger ile Dağıtık İzleme

Modern bir devops ekibinin kabusu, gecenin üçünde gelen bir PagerDuty alarmıyla başlar. Sisteminizdeki 10’larca mikroservis birbirine zincirlenmişken, ödeme adımındaki bir gecikmenin ya da hatanın kaynağını bulmak samanlıkta iğne aramaya benzer. Geleneksel log analiz araçları (ELK, Loki) bu kaotik ortamda yetersiz kalır; çünkü size korelasyonu değil, sadece bağımsız olayları sunarlar. Tam da bu yüzden, modern sistem mimarilerinde uçtan uca observability sağlamak ve hata tespitini dakikalara indirmek için opentelemetry ve jaeger ikilisi fiili endüstri standardı haline geldi. Bu rehberde, lafı hiç uzatmadan, production ortamında çalışan servisleriniz için dağıtık izleme (distributed tracing) altyapısını nasıl kuracağımızı ve karmaşık darboğazları nasıl analiz edeceğimizi göreceğiz.

Neden APM Ajanları Değil de OpenTelemetry?

Geçmişte New Relic, Datadog veya Dynatrace gibi kapalı kaynak APM (Application Performance Monitoring) ajanlarını uygulamaya gömmek kolay bir kaçış yoluydu. Ancak bu yaklaşım beraberinde ciddi bir “vendor lock-in” (tedarikçiye bağımlılık) ve kontrol edilemez maliyet artışları getiriyor. OpenTelemetry (OTel), CNCF çatısı altında geliştirilen satıcıdan bağımsız (vendor-agnostic) açık bir standarttır. Yarın öbür gün Jaeger yerine başka bir backend’e (örneğin Grafana Tempo) geçmek isterseniz, uygulama kodunuzda tek bir satır bile değiştirmeden sadece konfigürasyon seviyesinde bu değişikliği yapabilirsiniz.

Peki, arka planda bu iş nasıl dönüyor? İşin sırrı W3C Trace Context standardında saklıdır. Servisler arası HTTP veya gRPC çağrıları yapılırken, istek başlıklarına (headers) benzersiz bir traceparent eklenir:

traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
# Format: sürüm-traceId-spanId-traceFlags

Bu başlık sayesinde, istek hangi servise giderse gitsin, o servisin ürettiği loglar ve span’ler aynı üst kimlik (Trace ID) altında birleşir.

Mimariyi Doğru Tasarlamak: Collector Neden Şart?

Uygulamalarınızdan trace verilerini doğrudan Jaeger’a göndermek ilk bakışta cazip görünebilir. Ancak bu yaklaşım production ortamında intihardır. Jaeger geçici olarak ulaşılamaz olduğunda uygulamanızın bellek (heap) tüketimi tavan yapabilir veya ağ trafiğiniz optimize edilmemiş paketlerle dolabilir.

Doğru yaklaşım, her Kubernetes node’unda bir DaemonSet olarak veya merkezi bir Gateway olarak OpenTelemetry Collector konumlandırmaktır. Uygulamalar trace verilerini localhost üzerindeki Collector’a gönderir (çok düşük gecikme ile), Collector ise veriyi tamponlar (buffer), sıkıştırır (batch) ve asenkron olarak Jaeger’a iletir.

Adım Adım Kurulum: Production-Ready OTel Collector Konfigürasyonu

Aşağıda, yük altında ezilmeyecek, memory limiter ve batching mekanizmaları aktif edilmiş örnek bir otel-collector-config.yaml dosyası yer alıyor. Bu konfigürasyonu Kubernetes ortamında ConfigMap olarak tanımlayabilirsiniz:

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  memory_limiter:
    check_interval: 1s
    limit_percentage: 75
    spike_limit_percentage: 15
  batch:
    send_batch_size: 8192
    timeout: 5s
    send_batch_max_size: 10240

exporters:
  otlp/jaeger:
    endpoint: "jaeger-collector.observability.svc.cluster.local:4317"
    tls:
      insecure: true

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [otlp/jaeger]

Neden bu işlemcileri (processors) kullandık?

  • memory_limiter: Collector’ın bellek tüketimi belirlenen limitin (%75) üzerine çıktığında, çökmeyi (OOMKilled) önlemek için yeni gelen verileri dropping moduna alır. Güvenli limanda kalmanızı sağlar.
  • batch: Her trace span’ini tek tek ağ üzerinden göndermek yerine, bunları gruplayarak gönderir. CPU ve network overhead’ini dramatik ölçüde düşürür.

Uygulama Seviyesinde Context Propagation (Go Örneği)

OTel SDK’sını uygulamanıza entegre ederken en kritik nokta, bağlamın (context) kaybolmamasını sağlamaktır. Eğer bir HTTP çağrısı yapıyorsanız, HTTP istemcinizi OTel transport katmanı ile sarmalamanız gerekir. Aşağıdaki Go kod bloğu, gelen isteğin trace bağlamını alıp bir sonraki servise nasıl güvenli bir şekilde aktaracağınızı gösterir:

package main

import (
	"context"
	"net/http"

	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/trace"
)

func handleCheckout(w http.ResponseWriter, req *http.Request) {
	// Gelen istekten trace context'i çıkar ve yeni bir span başlat
	ctx := req.Context()
	tracer := otel.Tracer("checkout-service")
	ctx, span := tracer.Start(ctx, "ProcessPayment", trace.WithSpanKind(trace.SpanKindServer))
	defer span.End()

	// Ödeme servisine yapılacak HTTP çağrısını sarmala
	client := http.Client{Transport: otelhttp.NewTransport(http.DefaultTransport)}
	
	nextReq, _ := http.NewRequestWithContext(ctx, "POST", "http://payment-service/charge", nil)
	resp, err := client.Do(nextReq)
	if err != nil {
		span.RecordError(err)
		span.SetStatus(500, "Payment initiation failed")
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer resp.Body.Close()

	w.Write([]byte("Checkout successful"))
}

Bu kodda otelhttp.NewTransport, arka planda W3C standartlarına uygun traceparent header’ını giden HTTP isteğine otomatik olarak enjekte eder. Manuel müdahaleye gerek kalmaz.

Production SRE Pratikleri: Sampling Rate (Örnekleme) Ayarı

Saniyede 10.000 istek alan bir sistemde her bir isteğin trace verisini saklamak hem Jaeger depolama alanınızı (Elasticsearch/Cassandra) saniyeler içinde doldurur hem de ciddi bir maliyet kalemi oluşturur. Çözüm: Head-based veya Tail-based sampling uygulamaktır.

Uygulama seviyesinde (Head-based) sadece başarılı olan isteklerin %1’ini, hatalı (error status) olanların ise %100’ünü saklamak mantıklıdır. Ancak uygulamanın bir isteğin hata vereceğini en baştan bilmesi imkansızdır. Bu yüzden Collector seviyesinde Tail-based sampling yapılandırmak en profesyonel çözümdür. Collector, trace tamamlanana kadar veriyi belleğinde tutar; eğer trace içinde bir hata kodu veya yüksek gecikme saptanırsa trace’in tamamını saklar, aksi takdirde belirlediğiniz oranda eler.

Bunun için Collector konfigürasyonunuza şu işlemciyi ekleyebilirsiniz:

processors:
  tail_sampling:
    decision_wait: 10s
    num_traces: 10000
    expected_new_traces_per_sec: 2000
    policies:
      - name: filter_errors
        type: status_code
        status_code: { status_codes: [ ERROR ] }
      - name: filter_latency
        type: latency
        latency: { threshold_ms: 500 } # 500ms üzerindeki tüm trace'leri sakla
      - name: probabilistic_sample
        type: probabilistic
        probabilistic: { sampling_percentage: 5.0 } # Normal trafikten %5 örnek al

Jaeger Arayüzünde Darboğaz Analizi: Nereden Başlamalı?

Her şey kuruldu ve Jaeger UI’a girdiniz. Önünüzde yüzlerce span içeren karmaşık bir trace ağacı duruyor. SRE bakış açısıyla analiz yaparken şu üç altın kuralı unutmayın:

  1. Gap Analizi (Boşluklar): İki ardışık span arasında büyük bir zaman boşluğu varsa, bu durum ağ gecikmesine, kuyrukta bekleyen (message queue) mesajlara veya uygulama içindeki CPU-bound kilitlemelere (mutex contention) işaret eder.
  2. Database Span’leri (N+1 Sorgu Problemi): Eğer tek bir HTTP isteğinin altında yüzlerce ardışık SQL sorgu span’i görüyorsanız, yazılımcılarınız ORM kütüphanesini yanlış kullanmış ve N+1 query tuzağına düşmüş demektir.
  3. Baggage vs Span Attributes Ayrımı: Trace akışı boyunca tüm downstream servislere taşınmasını istediğiniz kritik meta verileri (örneğin tenant_id veya user_tier) “Baggage” olarak ekleyin. Sadece o servise ait verileri (örneğin SQL query string) ise “Span Attribute” olarak tutun.

Sonuç: Kör Noktaları Yok Edin

OpenTelemetry ve Jaeger yatırımı, ilk kurulumda kod değişikliği ve konfigürasyon yükü getirse de, production’da yaşanacak ilk büyük krizde kendini amorti eder. Servislerinizin birbiriyle nasıl konuştuğunu tahmin etmek yerine, onları canlı olarak izleyin. Unutmayın; ölçemediğiniz sistemi yönetemezsiniz.

Category: Genel | LEAVE A COMMENT
Ağustos 9 2024

Grafana Loki ile Merkezi Log Yönetimi: ELK’nın Hantallığından Kaçış Rehberi

Sistem yöneticilerinin ve DevOps mühendislerinin ortak rüyası nedir? Tabii ki gece yarısı “Disk Full” uyarısıyla uyanmamak. Ancak modern mikroservis mimarilerinde observability sağlamak ve devasa log yığınları arasında arama yapmak tam bir karın ağrısı olabiliyor. Yıllarca ELK (Elasticsearch, Logstash, Kibana) üçlüsünün JVM bellek canavarlığıyla mücadele ettikten sonra, hafif, dinamik ve bütçe dostu bir alternatif arıyorsanız doğru yerdesiniz. Bu rehberde, grafana ekosisteminin parlayan yıldızı loki ile nasıl ölçeklenebilir ve az maliyetli bir log management altyapısı kuracağımızı adım adım inceleyeceğiz.

Neden Loki? (Yani, Neden Elasticsearch Değil?)

Elasticsearch harika bir arama motorudur; bunu kimse inkar edemez. Ancak log yönetimi söz konusu olduğunda, Elasticsearch her şeyi (log satırının tamamını) indexler. Bu durum devasa index boyutlarına ve dolayısıyla korkunç bir RAM tüketimine yol açar. Kendi sunucularında ELK koşturanlar, Elasticsearch pod’unun 16GB RAM’i “kahvaltı niyetine” tükettiğini çok iyi bilir.

Loki ise farklı bir felsefe benimser: “Prometheus, ama loglar için.”

Loki, log içeriğinin tamamını indexlemez. Sadece logların metadata’sını (yani label/etiketlerini; örneğin app="nginx", env="production") indexler. Log satırlarının kendisini ise sıkıştırılmış chunk’lar halinde doğrudan nesne depolama servislerinde (AWS S3, Google Cloud Storage veya lokalde MinIO) saklar. Bu yaklaşım bize ne kazandırır?

  • Düşük Altyapı Maliyeti: RAM tüketimi MB’lar seviyesine düşer, disk maliyeti ise S3 kullanımı sayesinde neredeyse devede kulak kalır.
  • Kolay Korelasyon: Prometheus metriklerinizle aynı etiketleri (labels) kullanarak metrikten loga tek tıkla geçiş yapabilirsiniz.
  • Kolay Ölçeklenme: Loki’nin mikroservis mimarisi sayesinde yazma (ingester) ve okuma (querier) katmanlarını birbirinden bağımsız olarak ölçekleyebilirsiniz.

Sistem Mimarisi: Parçaları Birleştirelim

Loki tek başına çalışmaz. Log yönetim sistemimiz üç temel bileşenden oluşur:

  1. Promtail (Agent): Logları toplar, etiketler (labeling) ve Loki’ye push eder (Kubernetes dünyasında her node üzerinde bir DaemonSet olarak koşar).
  2. Loki (Veri Deposu): Gelen logları alır, indeksler ve depolar.
  3. Grafana (Görselleştirme): Loki’yi bir datasource olarak ekler, LogQL sorguları yazar ve şık dashboard’lar oluştururuz.

Adım Adım Kurulum: Kubernetes Üzerinde Loki ve Promtail

Lafı fazla uzatmadan klavyenin başına geçelim. Kurulum için en pratik yol Helm kullanmaktır. Kubernetes cluster’ınızın hazır olduğunu varsayarak işlemlere başlıyoruz.

1. Helm Depolarını Ekleme

İlk olarak Grafana’nın resmi Helm reposunu sistemimize ekleyelim ve güncelleyelim:

helm repo add grafana https://grafana.github.io/helm-charts
helm repo update

2. Loki Kurulumu

Üretim ortamı için Loki’yi S3 entegrasyonu ile kurmak en doğrusudur. Ancak bu rehberde pratiklik açısından lokal disk (StatefulSet) kullanan basit bir kurulum yapacağız. Aşağıdaki loki-values.yaml dosyasını oluşturalım:

loki:
  commonConfig:
    replication_factor: 1
  storage:
    type: 'filesystem'
  auth_enabled: false

singleBinary:
  replicas: 1

persistence:
  enabled: true
  size: 10Gi
  storageClassName: "standard" # Kendi storage class'ınızla değiştirin

Şimdi Loki’yi bu konfigürasyonla kuralım:

helm install loki grafana/loki -f loki-values.yaml --namespace logging --create-namespace

3. Promtail Kurulumu

Promtail, Kubernetes cluster’ındaki pod loglarını otomatik olarak keşfedecek şekilde konfigüre edilmiştir. promtail-values.yaml dosyamızı oluşturalım. Burada en kritik nokta Promtail’e Loki’nin adresini doğru vermektir:

config:
  clients:
    - url: http://loki-gateway.logging.svc.cluster.local/loki/api/v1/push

# Kubernetes namespace ve pod etiketlerini otomatik olarak loglara eklemesi için relabeling kuralları hazır gelir.

Promtail’i kuralım:

helm install promtail grafana/promtail -f promtail-values.yaml --namespace logging

LogQL 101: Loglar İçinde Kaybolmadan Arama Yapmak

Artık loglarımız Loki’ye akıyor. Peki bunları nasıl sorgulayacağız? Karşınızda LogQL. Eğer Prometheus’un sorgu dili olan PromQL’e aşinaysanız, LogQL size çocuk oyuncağı gelecektir. LogQL sorguları iki ana bölümden oluşur: Log Stream Selector ve Filter Expression.

Basit Filtreleme

Belirli bir namespace altındaki, belirli bir uygulamanın loglarını çekmek ve içinde “error” geçen satırları yakalamak için:

{namespace="production", app="payment-api"} |= "error"

Eğer “timeout” içeren ama “connection” içermeyen logları istiyorsak zincirleme filtre kullanabiliriz:

{app="nginx"} |= "timeout" != "connection"

Parser ve Yapılandırılmış (Structured) Loglar

Eğer uygulamanız JSON formatında log üretiyorsa, Loki bu logları çalışma anında (on-the-fly) ayrıştırabilir. Bu, Loki’nin en güçlü özelliklerinden biridir:

{app="auth-service"} | json | status = "500"

Yukarıdaki sorguda | json ifadesi, log satırını JSON olarak parse eder ve içerideki tüm JSON anahtarlarını geçici etiketlere (labels) dönüştürür. Sonrasında status = "500" filtresi ile sadece HTTP 500 hatası veren istekleri süzebiliriz.

Loglardan Metrik Üretmek

Diyelim ki son 5 dakikada Nginx loglarındaki HTTP 500 hatalarının saniyedeki oranını (rate) görmek istiyorsunuz. LogQL bunu yapabilir:

sum(rate({app="nginx"} | json | status =~ "5.." [5m]))

Bu sorguyu Grafana’da bir Graph panelinde çalıştırarak doğrudan loglardan türetilmiş canlı metrik grafikleri elde edebilirsiniz. Prometheus’a yük bindirmeden mükemmel bir alarm altyapısı!

Grafana ile Log Görselleştirme

Logları sorgulamak için Grafana arayüzünü kullanacağız. İşte izlemeniz gereken adımlar:

  1. Grafana arayüzüne gidin ve sol menüden Connections -> Data Sources yolunu izleyin.
  2. Add data source butonuna tıklayın ve listeden Loki‘yi seçin.
  3. Connection URL kısmına Loki servisinizin adresini yazın: http://loki-gateway.logging.svc.cluster.local (Eğer Grafana aynı cluster içindeyse).
  4. Sayfanın altındaki Save & Test butonuna tıklayın. Yeşil onay kodunu gördüyseniz bağlantı tamamdır!

Şimdi sol menüden Explore sekmesine gidin. Üst taraftan veri kaynağı olarak Loki‘yi seçin. Log browser üzerinden etiketlerinizi seçerek veya yukarıda paylaştığımız LogQL sorgularını yazarak loglarınızı canlı olarak (Live tailing) izlemeye başlayabilirsiniz.

Grafana Dashboard Visualization

Üretim Ortamı İçin Altın Tavsiyeler (Pro-Tips)

Loki’yi production ortamında koştururken canınızın sıkılmasını istemiyorsanız şu üç kurala mutlaka dikkat edin:

  1. Yüksek Cardinality’den Kaçının (High Cardinality): Loglarınıza asla user_id, ip_address veya request_id gibi binlerce benzersiz değere sahip dinamik etiketler (labels) eklemeyin. Bu, Loki’nin indeks yapısını şişirir ve sorgu performansını yerle bir eder. Bu tarz dinamik verileri log satırının içinde bırakın; aramak için LogQL filtrelerini (|= veya | json) kullanın.
  2. S3 Entegrasyonu: Lokal disk yerine her zaman AWS S3 veya MinIO gibi nesne depolama çözümlerini tercih edin. Bu sayede log retention (logların silinme süresi) politikanızı kolayca yönetebilir ve disk dolma riskini sıfıra indirebilirsiniz.
  3. Query Frontend Kullanımı: Yoğun log sorgulaması yapılan ortamlarda Loki’yi mikroservis modunda kurup query-frontend bileşenini aktif edin. Bu bileşen, büyük zaman aralıklarındaki sorguları parçalara ayırarak paralel çalıştırır ve caching mekanizması sunar.

Özet

Grafana Loki, modern DevOps pratiklerinde log yönetimi için ezber bozan bir araç. ELK’nın getirdiği o devasa operasyonel yük ve maliyet olmadan, sadece ihtiyacımız olanı indexleyerek harika bir observability altyapısı kurduk. Promtail ile logları topladık, LogQL ile derinlemesine analiz ettik ve Grafana ile taçlandırdık.

Siz de production ortamlarınızda kaynak tüketimini optimize etmek istiyorsanız, Loki’ye bir şans verin. Bir sonraki yazımızda görüşmek üzere, loglarınız temiz, sistemleriniz ayakta kalsın!

Category: Genel | LEAVE A COMMENT