Ekim 17 2026

Kubernetes’te ‘Ghost’ Resource’ları Bulmak: Zombie Pod Avcılığı

Modern bir devops ekibinin yönettiği multi-tenant production cluster’ında her şeyin dashboard üzerinde yemyeşil görünmesi, her şeyin yolunda olduğu anlamına gelmez. Kümeler büyüdükçe, microservice mimarileri dallanıp budaklandıkça ve continuous delivery hatları günde onlarca kez release çıktıkça arka planda sessiz bir felaket birikir: “Ghost” (hayalet) resource’lar. Sağlıklı bir resource-management stratejisi ve keskin observability pratikleri kurgulamadığınızda, kubernetes worker node’larınız aslında hiçbir iş yapmayan, hiçbir trafiğe yanıt vermeyen ama faturayı kabartan “zombie pod” sürüleriyle dolar taşar.

Bu yazıda 5+ yıl deneyimli bir mühendisin bildiği o temel kavramları bir kenara bırakıyoruz. Pod nedir, replica nedir anlatmayacağım. Doğrudan sahaya inip kümedeki hayaletleri avlayacağız; neden oluştuklarını analiz edecek, gerçek komutlar, PromQL sorguları ve otomasyon script’leriyle bu yükü kümeden kazıyacağız.

Zombie Pod Nedir ve Neden Türer?

Kubernetes ekosisteminde bir kaynağa “zombie” veya “ghost” dememiz için ille de CrashLoopBackOff hatası alması gerekmez. Hatta en tehlikelisi, cluster’a göre kusursuz çalışanlardır. Bir pod şu durumlarda zombie statüsüne düşer:

  • Lich Podlar (Stuck in Terminating): Kapatılma sinyali (SIGTERM) almış fakat unmount edilemeyen volume veya takılı kalan bir finalizer yüzünden günlerdir Terminating durumunda bekleyenler.
  • Avare (Orphaned/Headless) Podlar: Bir Service selector’ı ile eşleşmeyen, ingress rotası olmayan veya Helm release’i purge edildiği halde etcd tutarsızlığı nedeniyle silinmeyenler.
  • Sıfır Yüklü “Over-provisioned” Podlar: Geliştiricinin debug amacıyla ayağa kaldırdığı, arkasında 4 vCPU / 16 GiB request unuttuğu ve haftalardır 0.001 Core tüketen test artıkları.
  • StatefulSet Kalıntıları (Unbound/Dangling PVCs): StatefulSet scale down edildiğinde Kubernetes mimari gereği PVC’leri silmez. Pod gitmiştir ama arkasında pahalı SSD block storage’lar yatmaya devam eder.

1. Adım: Terminating State’te Çakılı Kalan Podları Avlamak

En sık karşılaşılan senaryo, storage unmount işlemlerinin node üzerinde kilitlenmesi veya custom controller’ların yazdığı finalizer’ların reconcile edilememesidir. Bu podlar CPU/RAM harcamasa bile IP pool’unu (özellikle AWS-VPC CNI veya Calico kullanıyorsanız) rezerve tutar ve node drain işlemlerini bloklar.

Kümedeki tüm namespace’ler genelinde 1 saatten uzun süredir Terminating durumunda olan podları bulalım:

kubectl get pods -A --field-selector=status.phase!=Running \
  -o json | jq -r '
  .items[] | 
  select(.metadata.deletionTimestamp != null) | 
  select((now - (.metadata.deletionTimestamp | fromdateiso8601)) > 3600) | 
  "Namespace: " + .metadata.namespace + " | Pod: " + .metadata.name + " | Waiting: " + ((now - (.metadata.deletionTimestamp | fromdateiso8601))/3600 | tostring) + " hours"'

Birçok mühendis burada hemen kubectl delete pod <pod-name> --force --grace-period=0 komutuna sarılır. Yapmayın. Neden? Çünkü arkasında asılı bir CSI volume kilidi bırakırsanız, ileride o node’a schedule edilecek yeni pod Multi-Attach error for volume hatasıyla patlayacaktır.

Bunun yerine pod’un neden takıldığını görmek için metadata finalizer alanına bakın:

kubectl get pod <zombie-pod-name> -n <namespace> -o jsonpath='{.metadata.finalizers}'

Eğer storage driver çoktan node üzerinden çekildiyse ve controller yanıt vermiyorsa, finalizer’ı JSON merge patch ile söküp atın:

kubectl patch pod <zombie-pod-name> -n <namespace> \
  -p '{"metadata":{"finalizers":null}}' --type=merge

2. Adım: Arkasında Servis Olmayan “Sağır” Podları Yakalamak

CI/CD pipeline’ları çöktüğünde bazen Deployment silinir ancak pod bağımsız bir şekilde çalışmaya devam eder. Ya da deployment label’ları güncellenir fakat Service manifest’indeki selector güncellenmeyi unutulur. Sonuç: Pod CPU ve Memory tüketir ama gelen tek bir HTTP isteği dahi almaz.

Herhangi bir Service’in Endpoints veya modern Kubernetes kümelerinde EndpointSlice objesi tarafından hedeflenmeyen podları tespit etmek için bu pratik bash one-liner’ını çalıştırın:

#!/usr/bin/env bash
# Namespace bazlı hedefsiz pod kontrolü
NAMESPACE="production"

# Tüm geçerli endpoint IP'lerini al
VALID_IPS=$(kubectl get endpointslices -n "$NAMESPACE" -o jsonpath='{.items[*].endpoints[*].addresses[*]}' | tr ' ' '\n' | sort -u)

# Çalışan podların IP'lerini karşılaştır
for POD in $(kubectl get pods -n "$NAMESPACE" -o jsonpath='{range .items[?(@.status.phase=="Running")]}{.metadata.name}{":"}{.status.podIP}{"\n"}{end}'); do
    POD_NAME=$(echo "$POD" | cut -d: -f1)
    POD_IP=$(echo "$POD" | cut -d: -f2)

    if ! echo "$VALID_IPS" | grep -q "$POD_IP"; then
        # DaemonSet ve standalone jobları hariç tutmak isteyebilirsiniz
        OWNER=$(kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o jsonpath='{.metadata.ownerReferences[0].kind}')
        if [[ "$OWNER" != "DaemonSet" && "$OWNER" != "Job" ]]; then
            echo "[ZOMBIE DETECTED] Pod: $POD_NAME (IP: $POD_IP) - Hiçbir Service arkasında değil!"
        fi
    fi
done

Bu senaryo özellikle preview environment’larda çok yaşanır. Pull Request merge edilir, ingress kalkar, service kalkar ama pod orada sonsuza dek sessizce yaşar.

3. Adım: Observability ile Kaynak İsrafını Ortaya Çıkarma

FinOps dünyasında en tehlikeli ghost pod, “iş yapıyor gibi durup hiçbir şey yapmayan” poddur. Geliştirici deployment manifest’inde cpu: "2" ve memory: "4Gi" talep etmiştir. Kubernetes scheduler bu talebi garanti altına almak için node üzerinde ilgili kaynağı rezerve eder (allocatable space düşer). Ancak pod arka planda sadece idle beklemektedir.

Bu israfı yakalamak için Prometheus ve Thanos üzerinde çalıştıracağımız PromQL sorguları devreye girer. Son 7 gün boyunca talep ettiği CPU kaynağının %1’inden azını kullanan podları listeleyelim:

# Prometheus / Thanos PromQL
(
  sum(rate(container_cpu_usage_seconds_total{container!=""}[1h])) by (namespace, pod)
  /
  sum(kube_pod_container_resource_requests{resource="cpu"}) by (namespace, pod)
) * 100 < 1

Bu sorgunun çıktısı size direkt olarak kümenizdeki “zombi rezervasyonları” verir. Pod fiziki olarak ölü değildir, ama maliyet açısından tam bir hayalettir. Node Autoscaler (Karpenter veya Cluster Autoscaler) bu pod yüzünden yeni ec2/compute instance ayağa kaldırmak zorunda kalır.

4. Adım: Sahipsiz StatefulSet Kalıntıları (Ghost PVC’ler)

Storage israfı, compute israfından daha sinsi seyreder çünkü disk saatlik değil, provizyon edildiği toplam boyut üzerinden (GB/ay) faturalandırılır. Bir StatefulSet’in replica sayısını 5’ten 2’ye indirdiğinizde Kubernetes, veri kaybını önlemek adına 3 adet PVC’yi cluster içinde Bound statüsünde tutmaya devam eder.

Peki bu diskleri kullanan aktif bir pod var mı? Görelim:

kubectl get pvc -A -o json | jq -r '
  .items[] | 
  select(.status.phase == "Bound") | 
  select(.metadata.annotations["volume.kubernetes.io/selected-node"] == null and .metadata.ownerReferences == null) | 
  "Namespace: " + .metadata.namespace + " | Unmounted PVC: " + .metadata.name + " | Size: " + .spec.resources.requests.storage'

Burada listelenen PVC’ler, Kubernetes API server düzeyinde kilitli kalmış, pod’u çoktan yok edilmiş disklere işaret eder. Eğer Retain politikasına sahip bir StorageClass kullanıyorsanız, deployment tamamen silinse bile AWS EBS veya GCP Persistent Disk konsolunda faturayı şişirmeye devam eder.

Otomasyon: Zombie Avını Sürekli Kılmak

Her hafta terminal başına geçip bu script’leri çalıştırmak sürdürülebilir bir SRE yaklaşımı değildir. Bunu engellemenin en temiz yolu, kuralları policy engine seviyesinde cluster’a enjekte etmektir.

Completed (başarıyla bitmiş) job podlarının cluster’da günlerce yer kaplamasını engellemek için manifest’lerinize mutlaka ttlSecondsAfterFinished ekleyin veya bir Kyverno policy’si ile bunu zorunlu kılın:

apiVersion: batch/v1
kind: Job
metadata:
  name: nightly-backup-sync
spec:
  # Pod tamamlandıktan 300 saniye (5 dk) sonra kendini imha eder
  ttlSecondsAfterFinished: 300
  template:
    spec:
      containers:
      - name: sync-worker
        image: custom-sync:v1.2
      restartPolicy: OnFailure

Aynı şekilde Kyverno veya OPA Gatekeeper kullanarak, her Deployment için memory/cpu limit ve request oranının belirli bir çarpanı (örneğin request limitin %50’sinden az olamaz kuralı) aşmasını admission webhook aşamasında engelleyebilirsiniz.

Sonuç: FinOps Salt Fatura İncelemek Değildir

DevOps dünyasında maliyet optimizasyonu (FinOps), ay sonunda AWS veya GCP faturasındaki grafikleri inceleyip “Compute Engine çok harcamış” demekten ibaret değildir. Asıl optimizasyon, runtime seviyesinde etcd ve scheduler üzerindeki gereksiz yükleri ayıklamaktır.

Terminating state’te kalan podlar IP tüketir, selector’ı kopmuş podlar gereksiz compute harcar, dangling PVC’ler storage faturasını patlatır. Kümelerinizde periyodik olarak bu kontrolleri pipeline’larınıza veya cron job’larınıza entegre edin. Ghost resource’ları temizlemek sadece faturayı düşürmekle kalmaz, scheduler gecikmelerini (scheduling latency) ve control plane üzerindeki memory overhead’ini doğrudan azaltır.

Category: Genel | LEAVE A COMMENT
Ekim 2 2026

Ölçeklenen Altyapılarda Observability Maliyetini Yönetme: FinOps ve Sampling Stratejileri

Ay başında Datadog, New Relic veya AWS CloudWatch faturası geldiğinde dashboard’lardaki p99 gecikme grafiklerinden daha hızlı yükselen tek bir metrik vardır: Finans direktörünün nabzı. Modern bir DevOps mimarisinde mikroservislerin sayısı ve trafik hacmi arttıkça, observability altyapısının maliyeti çoğu zaman production ortamının compute maliyetini geride bırakır. Sistemin kör noktalarını kapatmak isterken bütçeyi tüketmek sürdürülebilir değil. Doğru bir FinOps yaklaşımı ve akıllı log yönetimi stratejileriyle, sistem görünürlüğünden ödün vermeden veri hacmini %60 ila %80 oranında düşürmek mümkün.

Fatura Neden Şişer? Görünmez Maliyet Kalemleri

Kıdemli mühendislerin sıkça düştüğü tuzak şudur: “Her şeyi loglayalım, OpenSearch veya Loki’ye basalım; lazım olursa grep’leriz.” Trafiğiniz saniyede 100 istek seviyesindeyken bu kabul edilebilir bir tembelliktir. Ancak saniyede 50.000 request aldığınız anda bu alışkanlık bir bütçe felaketine dönüşür.

Maliyeti patlatan üç ana faktör vardır:

  • Sağlık Kontrolleri (Health Checks): Kubernetes liveness/readiness probları ve ALB ping’leri log havuzunuzun %30’unu oluşturabilir. HTTP 200 GET /healthz satırının size hata anında hiçbir faydası yoktur.
  • Yüksek Kardinalite (High Cardinality): Metrik etiketlerine (label) user_id, order_id gibi sınırsız varyasyonu olan değerler eklemek, time-series veritabanınızın (Prometheus/Mimir) bellek tüketimini ve ingest maliyetini katlar.
  • Head-Based Sampling Hataları: Tracing verisinde rastgele %5 sampling aldığınızda, sistemdeki asıl yakalamak istediğiniz 500 hatalarının ve p99 outlier’larının da %95’ini çöpe atarsınız. Geriye sadece gürültü kalır.

Log Yönetimi: Vector ile Edge Düzeyinde Filtreleme ve Dönüştürme

Logları merkezi sisteme göndermeden önce daemonset katmanında filtrelemek en ucuz ve en etkili çözümdür. Logstash veya Fluentd gibi JVM/Ruby tabanlı hantal çözümler yerine Rust ile yazılmış düşük ayak izli Vector bu iş için biçilmiş kaftandır.

Aşağıdaki Vector konfigürasyonunda üç kritik optimizasyon yapıyoruz: Başarılı health check’leri drop ediyoruz, hassas verileri tokenize ediyoruz ve kalan debug loglarını örnekliyoruz (sampling).

cat << 'EOF' > /etc/vector/vector.yaml
sources:
  kubernetes_logs:
    type: kubernetes_logs

transforms:
  filter_health_checks:
    type: filter
    inputs:
      - kubernetes_logs
    # HTTP 200 dönen healthz loglarını doğrudan çöpe at
    condition: '!includes(["/healthz", "/readyz", "/livez"], .message)'

  drop_noisy_debugs:
    type: filter
    inputs:
      - filter_health_checks
    # Log seviyesi DEBUG olanları %90 oranında sample et
    condition: |
      if .level == "debug" {
        random() < 0.1
      } else {
        true
      }

  sanitize_and_prune:
    type: remap
    inputs:
      - drop_noisy_debugs
    source: |
      # Gereksiz Kubernetes metadata alanlarını temizle
      del(.kubernetes.pod_labels)
      del(.kubernetes.pod_annotations)
      
      # Kredi kartı gibi hassas verileri maskele
      .message = replace(.message, r'[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{4}', "[REDACTED_CC]")

sinks:
  elasticsearch_out:
    type: elasticsearch
    inputs:
      - sanitize_and_prune
    endpoints:
      - "https://elasticsearch.internal:9200"
    mode: "bulk"
EOF

Neden böyle yaptık? Kubernetes metadata’sındaki pod annotation’ları production’da devasa JSON payload’ları üretir. Bunları edge katmanında del() fonksiyonu ile silmek, taşınan ve indekslenen veri boyutunu anında %25 azaltır.

Tracing Verisinde Tail-Based Sampling Stratejisi

Geleneksel distributed tracing araçları trace’in başlangıcında karar verir (Head-based sampling). Bir istek geldiğinde yazı tura atılır: %1 seçilirse tüm akış kaydedilir. Fakat istek sırasında bir mikroservis patlarsa ve siz o isteği sample etmediyseniz, elinizde hiçbir şey kalmaz.

Tail-based sampling, isteğin tamamlanmasını bekler. İstek hata aldıysa veya belirlenen latency eşiğini aştıysa trace’i %100 saklar; sorunsuz HTTP 200 yanıtlarını ise agresif bir şekilde eler.

Bunu OpenTelemetry (OTel) Collector katmanında şu şekilde konfigüre ediyoruz:

cat << 'EOF' > /etc/otelcol/config.yaml
receivers:
  otlp:
    protocols:
      grpc:
      http:

processors:
  tail_sampling:
    decision_wait: 10s
    num_traces: 10000
    expected_new_traces_per_sec: 2000
    policies:
      # 1. Kural: Status code ERROR olan tüm trace'leri eksiksiz tut
      - name: drop_errors_policy
        type: status_code
        status_code: { status_codes: [ ERROR ] }

      # 2. Kural: Süresi 1.5 saniyeyi aşan tüm yavaş istekleri yakala
      - name: latency_policy
        type: latency
        latency: { threshold_ms: 1500 }

      # 3. Kural: Kalan normal akıştan sadece %1 örnek al
      - name: probabilistic_policy
        type: probabilistic
        probabilistic: { sampling_percentage: 1.0 }

exporters:
  otlp/backend:
    endpoint: "tempo.monitoring.svc.cluster.local:4317"
    tls:
      insecure: true

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [tail_sampling]
      exporters: [otlp/backend]
EOF

Bu konfigürasyonda OTel Collector, trace parçalarını decision_wait: 10s süresince bellekte tutar. Span ağacı tamamlandığında kuralları yukarıdan aşağıya değerlendirir. İstisna veya lag varsa diskte saklanır; yoksa veri hacmi anında %99 bastırılır.

Metriklerin Sessiz Katili: Kardinaliteyi Düşürme (Metric Relabeling)

Datadog veya Prometheus faturalarında metrik maliyetlerinin patlamasının temel nedeni “Custom Metrics” ve time-series patlamalarıdır. Bir metriğin kaç time-series ürettiğini şu formülle hesaplarız:

Total Time Series = (Değer_Sayısı_Label_A) * (Değer_Sayısı_Label_B) * (Değer_Sayısı_Label_C)

Eğer bir yazılımcı HTTP istek metriğine user_id veya rastgele UUID’ler içeren path eklediyse, o metrik saniyeler içinde 500.000 time-series üreterek bellek limitlerini vurur. Prometheus scrape konfigürasyonunda metric_relabel_configs kullanarak bu etiketleri ingest anında silmelisiniz:

scrape_configs:
  - job_name: 'api-gateway'
    kubernetes_sd_configs:
      - role: pod
    metric_relabel_configs:
      # Belirli yüksek kardinaliteli dinamik labelları drop et
      - action: labeldrop
        regex: '(user_id|client_ip|session_token)'
      
      # REST URL path'lerindeki UUID'leri regex ile normalize et
      - source_labels: [__name__, path]
        regex: 'http_requests_total;/api/v1/orders/[a-f0-9\-]+'
        target_label: path
        replacement: '/api/v1/orders/:id'

Yukarıdaki işlem yapılmadığında /api/v1/orders/a1b2-c3d4 ve /api/v1/orders/e5f6-g7h8 iki ayrı metrik olarak depolanır. Regex normalizasyonu sayesinde her iki istek de /api/v1/orders/:id altında birleşir; metrik tekilleşir ve time-series sayısı tek hanelere iner.

Veri Yaşam Döngüsü: Tiering ve Soğuk Depolama

SRE ekiplerinin yaptığı analizler, log sorgularının %92’sinin son 48 saat içinde yazılan verilere yapıldığını gösteriyor. 30 gün önceki bir INFO logunu pahalı NVMe disklerde tutmak safi kaynak israfıdır.

Loki veya Elasticsearch kullanırken iki katmanlı (hot/cold) saklama stratejisi kurgulayın:

  • Hot Tier (0-7 Gün): Hızlı sorgulama için NVMe diskler veya SSD blok depolama. Tam indeksleme açık.
  • Warm/Cold Tier (7-30 Gün): İndeksleri küçültülmüş, read-only modda çalışan uygun maliyetli HDD veya S3/GCS object storage katmanı.
  • Deep Archive (30+ Gün): Yalnızca regülasyon (KVKK/GDPR/PCI-DSS) gereği tutulması gereken veriler için S3 Glacier / GCS Archive lifecycle kuralları. İndeks yok, yalnızca ham gzip blokları.

Özet: FinOps Mühendisliği Bir Süreçtir

Observability sistemlerini optimize etmek, kör uçuş yapmak anlamına gelmez. Tam aksine, doğru sinyali yakalayıp gürültüyü (noise) elimine etmektir. Edge katmanında Vector ile gürültüyü kesin, APM tarafında tail-based sampling ile hatalara odaklanın ve scraping aşamasında kardinaliteyi regex ile dizginleyin. Bu üç adımı attığınızda, faturanız düşerken platformunuzun asıl sorunları tespit etme kabiliyeti katlanarak artacaktır.

Category: Genel | LEAVE A COMMENT
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