Elasticsearch ve Kibana ile Anomali Tespiti

Üretim ortamında saatler geçirmiş biri olarak şunu söyleyeyim: logları toplamak kolay, onlardan anlam çıkarmak zor. ELK Stack kurulumu yaptıktan sonra herkes bir süre Kibana’nın Discover sekmesinde log satırlarına bakıp “bu sistemde ne olduğunu anlayabilirim” diye düşünüyor. Sonra 2 milyon log satırıyla yüzleşince gerçeklik devreye giriyor. İşte tam bu noktada anomali tespiti hayat kurtarıcı oluyor.

Bu yazıda Elasticsearch ve Kibana’nın Machine Learning özelliklerini ve daha erişilebilir yöntemlerle anomali tespitini nasıl yapabileceğinizi, gerçek senaryolar üzerinden anlatacağım.

Anomali Tespiti Neden Bu Kadar Kritik?

Klasik eşik bazlı uyarılar çalışır, evet. CPU %90’ı geçince uyarı gönder, disk dolunca mail at. Ama bu yaklaşımın kör noktaları var. Sisteminiz normalde gece yarısı %60 CPU kullanıyorsa ve aniden %45’e düşüyorsa bu da bir anomalidir. Ya da her Pazartesi sabahı pik yapan bir servis, bir Pazartesi sabahı sessizce ölmüşse ve kimse fark etmemişse?

Anomali tespiti, sistemin kendi geçmiş davranışını baz alarak “bu normal mi değil mi?” sorusuna cevap verir. Bu yaklaşım özellikle şu senaryolarda klasik uyarı sistemlerinden çok daha iyi sonuç verir:

  • Mevsimsel veya saatlik örüntüler içeren metrikler
  • Yavaş yavaş kötüleşen performans sorunları (boiling frog problemi)
  • Birden fazla metriğin kombinasyonundan ortaya çıkan sorunlar
  • Güvenlik alanında olağandışı kullanıcı davranışları

Ortam Hazırlığı ve Veri Yapısı

Anomali tespitine geçmeden önce loglarınızın düzgün yapılandırılmış olması şart. Ham metin logları üzerinde makine öğrenmesi çalıştırmak isteyemezsiniz.

Önce Logstash tarafında doğru bir pipeline kuruyoruz:

# /etc/logstash/conf.d/nginx-metrics.conf

input {
  beats {
    port => 5044
  }
}

filter {
  grok {
    match => {
      "message" => '%{IPORHOST:client_ip} - %{USER:ident} [%{HTTPDATE:timestamp}] "%{WORD:method} %{URIPATH:request_path} HTTP/%{NUMBER:http_version}" %{NUMBER:status_code:int} %{NUMBER:bytes_sent:int} "%{DATA:referrer}" "%{DATA:user_agent}" %{NUMBER:request_time:float}'
    }
  }
  
  date {
    match => ["timestamp", "dd/MMM/yyyy:HH:mm:ss Z"]
    target => "@timestamp"
  }
  
  mutate {
    convert => {
      "status_code" => "integer"
      "bytes_sent" => "integer"
      "request_time" => "float"
    }
    add_field => {
      "hour_of_day" => "%{+HH}"
      "day_of_week" => "%{+EEE}"
    }
  }
}

output {
  elasticsearch {
    hosts => ["localhost:9200"]
    index => "nginx-metrics-%{+YYYY.MM.dd}"
  }
}

Index mapping’ini de baştan doğru tanımlamak önemli. Özellikle sayısal alanlar için:

curl -X PUT "localhost:9200/_index_template/nginx-metrics-template" 
  -H 'Content-Type: application/json' 
  -d '{
    "index_patterns": ["nginx-metrics-*"],
    "template": {
      "mappings": {
        "properties": {
          "@timestamp": { "type": "date" },
          "request_time": { "type": "float" },
          "bytes_sent": { "type": "long" },
          "status_code": { "type": "integer" },
          "client_ip": { "type": "ip" },
          "request_path": { "type": "keyword" },
          "method": { "type": "keyword" }
        }
      }
    }
  }'

Kibana Machine Learning ile Anomali Tespiti

Kibana’nın ML özelliği Elastic lisansı gerektiriyor, ancak basic lisansla da bazı özellikler kullanılabiliyor. Aşağıda önce ML yaklaşımını, sonra açık kaynak alternatifleri anlatacağım.

Single Metric Job Oluşturma

En basit senaryo ile başlayalım: Nginx’in saniyedeki istek sayısını izleyen bir ML job.

curl -X PUT "localhost:9200/_ml/anomaly_detectors/nginx-request-rate" 
  -H 'Content-Type: application/json' 
  -d '{
    "description": "Nginx istek hizi anomali tespiti",
    "analysis_config": {
      "bucket_span": "5m",
      "detectors": [
        {
          "detector_description": "Toplam istek sayisi",
          "function": "count",
          "detector_index": 0
        }
      ],
      "influencers": []
    },
    "data_description": {
      "time_field": "@timestamp",
      "time_format": "epoch_ms"
    },
    "analysis_limits": {
      "model_memory_limit": "10mb"
    },
    "data_feeds": {},
    "model_plot_config": {
      "enabled": true
    }
  }'

Datafeed tanımlamak da gerekiyor:

curl -X PUT "localhost:9200/_ml/datafeeds/datafeed-nginx-request-rate" 
  -H 'Content-Type: application/json' 
  -d '{
    "job_id": "nginx-request-rate",
    "indices": ["nginx-metrics-*"],
    "query": {
      "bool": {
        "must": [
          {
            "range": {
              "@timestamp": {
                "gte": "now-24h"
              }
            }
          }
        ]
      }
    },
    "scroll_size": 1000,
    "delayed_data_check_config": {
      "enabled": true,
      "check_window": "15m"
    }
  }'

Multi-metric Job ile Daha Derin Analiz

Tek bir metriği izlemek çoğu zaman yeterli değil. Gerçek hayatta “istek sayısı normalken response time neden bu kadar yüksek?” sorusunu sorabilmek için birden fazla metriği birlikte analiz etmek gerekiyor:

curl -X PUT "localhost:9200/_ml/anomaly_detectors/nginx-multi-metric" 
  -H 'Content-Type: application/json' 
  -d '{
    "description": "Nginx cok metrikli anomali analizi",
    "analysis_config": {
      "bucket_span": "10m",
      "detectors": [
        {
          "function": "high_mean",
          "field_name": "request_time",
          "detector_description": "Yuksek ortalama yanit suresi"
        },
        {
          "function": "high_count",
          "over_field_name": "client_ip",
          "detector_description": "IP basina yuksek istek sayisi"
        },
        {
          "function": "high_sum",
          "field_name": "bytes_sent",
          "detector_description": "Yuksek bant genisligi kullanimi"
        },
        {
          "function": "rare",
          "by_field_name": "status_code",
          "detector_description": "Nadir HTTP durum kodlari"
        }
      ],
      "influencers": ["client_ip", "request_path", "status_code"]
    },
    "data_description": {
      "time_field": "@timestamp"
    },
    "model_plot_config": {
      "enabled": true,
      "annotations_enabled": true
    }
  }'

Watcher ile Anomali Uyarıları

ML job’ları anomali bulduğunda ne olacak? Kibana arayüzünde görmek güzel, ama uyarı sistemiyle entegre etmek şart. Elasticsearch Watcher burada devreye giriyor:

curl -X PUT "localhost:9200/_watcher/watch/anomali-uyarisi" 
  -H 'Content-Type: application/json' 
  -d '{
    "trigger": {
      "schedule": {
        "interval": "5m"
      }
    },
    "input": {
      "search": {
        "request": {
          "indices": [".ml-anomalies-*"],
          "body": {
            "query": {
              "bool": {
                "must": [
                  {
                    "range": {
                      "timestamp": {
                        "gte": "now-10m"
                      }
                    }
                  },
                  {
                    "range": {
                      "anomaly_score": {
                        "gte": 75
                      }
                    }
                  },
                  {
                    "term": {
                      "job_id": "nginx-multi-metric"
                    }
                  }
                ]
              }
            },
            "sort": [
              {
                "anomaly_score": {
                  "order": "desc"
                }
              }
            ],
            "size": 5
          }
        }
      }
    },
    "condition": {
      "compare": {
        "ctx.payload.hits.total.value": {
          "gt": 0
        }
      }
    },
    "actions": {
      "slack_bildirim": {
        "webhook": {
          "scheme": "https",
          "host": "hooks.slack.com",
          "port": 443,
          "method": "post",
          "path": "/services/YOUR/SLACK/WEBHOOK",
          "params": {},
          "headers": {
            "Content-Type": "application/json"
          },
          "body": "{"text": "ANOMALI ALGILANDI! Skor: {{ctx.payload.hits.hits.0._source.anomaly_score}} - Detay: {{ctx.payload.hits.hits.0._source.detector_description}}"}"
        }
      }
    }
  }'

Açık Kaynak Yaklaşım: Elasticsearch Aggregation ile Z-Score Anomali Tespiti

ML lisansı olmayan ortamlar için de güçlü bir yaklaşım var. İstatistiksel Z-Score yöntemi Elasticsearch aggregation’ları ile uygulanabilir. Bu yaklaşım basit ama şaşırtıcı derecede etkili.

curl -X GET "localhost:9200/nginx-metrics-*/_search" 
  -H 'Content-Type: application/json' 
  -d '{
    "size": 0,
    "query": {
      "range": {
        "@timestamp": {
          "gte": "now-7d",
          "lte": "now"
        }
      }
    },
    "aggs": {
      "per_hour": {
        "date_histogram": {
          "field": "@timestamp",
          "calendar_interval": "hour"
        },
        "aggs": {
          "avg_response_time": {
            "avg": {
              "field": "request_time"
            }
          },
          "stddev_response_time": {
            "extended_stats": {
              "field": "request_time",
              "sigma": 2
            }
          },
          "error_rate": {
            "filter": {
              "range": {
                "status_code": {
                  "gte": 500
                }
              }
            }
          },
          "error_percentage": {
            "bucket_script": {
              "buckets_path": {
                "errors": "error_rate._count",
                "total": "_count"
              },
              "script": "params.total > 0 ? (params.errors / params.total) * 100 : 0"
            }
          }
        }
      },
      "overall_stats": {
        "extended_stats": {
          "field": "request_time"
        }
      }
    }
  }'

Bu sorgudan gelen std_deviation_bounds değerlerini kullanarak anomali eşiklerini dinamik olarak hesaplayabilirsiniz. Bir sonraki adım bunu Python ile otomatize etmek:

# anomali_tespit.py - Basit Z-Score bazli anomali tespiti
# Crontab ile her 5 dakikada bir calistirin: */5 * * * * python3 /opt/scripts/anomali_tespit.py

#!/usr/bin/env python3
"""
Elasticsearch Z-Score Anomali Tespiti
Gereksinimler: pip install elasticsearch requests
"""

from elasticsearch import Elasticsearch
import json
import requests
from datetime import datetime

ES_HOST = "localhost"
ES_PORT = 9200
SLACK_WEBHOOK = "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
Z_SCORE_ESIGI = 3.0

es = Elasticsearch([{"host": ES_HOST, "port": ES_PORT, "scheme": "http"}])

def baseline_getir(index_pattern, metrik_alani, gecmis_gun=7):
    """Son N gunun istatistiklerini hesapla"""
    sorgu = {
        "size": 0,
        "query": {
            "range": {
                "@timestamp": {
                    "gte": f"now-{gecmis_gun}d",
                    "lte": "now-1h"
                }
            }
        },
        "aggs": {
            "stats": {
                "extended_stats": {
                    "field": metrik_alani
                }
            }
        }
    }
    
    sonuc = es.search(index=index_pattern, body=sorgu)
    stats = sonuc["aggregations"]["stats"]
    
    return {
        "ortalama": stats["avg"],
        "std_sapma": stats["std_deviation"],
        "min": stats["min"],
        "max": stats["max"],
        "count": stats["count"]
    }

def guncel_deger_getir(index_pattern, metrik_alani, dakika=5):
    """Son N dakikanin ortalama degerini getir"""
    sorgu = {
        "size": 0,
        "query": {
            "range": {
                "@timestamp": {
                    "gte": f"now-{dakika}m"
                }
            }
        },
        "aggs": {
            "guncel": {
                "avg": {
                    "field": metrik_alani
                }
            }
        }
    }
    
    sonuc = es.search(index=index_pattern, body=sorgu)
    return sonuc["aggregations"]["guncel"]["value"]

def z_skor_hesapla(deger, ortalama, std_sapma):
    if std_sapma == 0:
        return 0
    return abs((deger - ortalama) / std_sapma)

def slack_bildirim_gonder(mesaj):
    payload = {"text": mesaj}
    requests.post(SLACK_WEBHOOK, json=payload)

def anomali_kontrol():
    kontroller = [
        {
            "ad": "Nginx Yanit Suresi",
            "index": "nginx-metrics-*",
            "metrik": "request_time"
        },
        {
            "ad": "Aktif Baglanti Sayisi",
            "index": "system-metrics-*",
            "metrik": "system.network.tcp.established"
        }
    ]
    
    for kontrol in kontroller:
        try:
            baseline = baseline_getir(kontrol["index"], kontrol["metrik"])
            guncel = guncel_deger_getir(kontrol["index"], kontrol["metrik"])
            
            if guncel is None or baseline["std_sapma"] is None:
                continue
                
            z_skor = z_skor_hesapla(guncel, baseline["ortalama"], baseline["std_sapma"])
            
            print(f"{kontrol['ad']}: Guncel={guncel:.3f}, Ortalama={baseline['ortalama']:.3f}, Z-Skor={z_skor:.2f}")
            
            if z_skor >= Z_SCORE_ESIGI:
                mesaj = (
                    f"ANOMALI TESPIT EDILDI!n"
                    f"Metrik: {kontrol['ad']}n"
                    f"Guncel Deger: {guncel:.3f}n"
                    f"7 Gunluk Ortalama: {baseline['ortalama']:.3f}n"
                    f"Z-Skoru: {z_skor:.2f} (Esik: {Z_SCORE_ESIGI})n"
                    f"Zaman: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
                )
                slack_bildirim_gonder(mesaj)
                
        except Exception as e:
            print(f"Hata - {kontrol['ad']}: {str(e)}")

if __name__ == "__main__":
    anomali_kontrol()

Gerçek Hayat Senaryosu: E-ticaret Platformunda Alışveriş Saati Anomalisi

Bir e-ticaret platformunda karşılaştığım gerçek bir senaryoyu paylaşayım. Her akşam 20:00-22:00 arası yoğun trafik beklenen platformda, bir gün sipariş sayısı normalde bu saatlerde 400-500/dakika iken aniden 12’ye düştü. CPU, RAM, network hepsi normaldi. Klasik eşik uyarıları tetiklenmedi. Ama ML job bunu 8 dakika içinde yakaladı.

İşte bu tür business metric anomalilerini yakalayan bir Kibana lens sorgusu:

# Kibana Dev Tools'da calistirin
# Saat bazinda siparis sayisi ve anomali esiklerini hesaplar

GET /orders-*/_search
{
  "size": 0,
  "query": {
    "range": {
      "@timestamp": {
        "gte": "now-30d"
      }
    }
  },
  "aggs": {
    "saat_bazli": {
      "date_histogram": {
        "field": "@timestamp",
        "calendar_interval": "hour"
      },
      "aggs": {
        "siparis_sayisi": {
          "value_count": {
            "field": "order_id"
          }
        },
        "basarili_siparis": {
          "filter": {
            "term": {
              "status": "completed"
            }
          }
        },
        "basarisiz_siparis": {
          "filter": {
            "terms": {
              "status": ["failed", "cancelled"]
            }
          }
        },
        "basarisizlik_orani": {
          "bucket_script": {
            "buckets_path": {
              "basarisiz": "basarisiz_siparis._count",
              "toplam": "siparis_sayisi.value"
            },
            "script": "params.toplam > 0 ? (params.basarisiz / params.toplam) * 100 : 0"
          }
        },
        "hareketli_ortalama": {
          "moving_avg": {
            "buckets_path": "siparis_sayisi",
            "window": 24,
            "model": "ewma",
            "settings": {
              "alpha": 0.3
            }
          }
        }
      }
    }
  }
}

Anomali Tespit Sonuçlarını Kibana’da Görselleştirme

ML sonuçlarını anlamlı bir dashboard’a dönüştürmek için bazı pratik notlar:

Kibana’nın Anomaly Explorer sekmesi zaten ML job sonuçlarını harika görselleştiriyor. Ama özel dashboard istiyorsanız .ml-anomalies-* indeksini direkt sorgulayabilirsiniz.

Kibana’da TSVB (Time Series Visual Builder) ile anomali bantlarını normale göre renklendirmek için şu yaklaşımı kullanın:

# .ml-anomalies-* indeksinden anomali skorlarini ceken sorgu
# Bu sorguyu Kibana Lens Data View olarak kullanin

GET .ml-anomalies-*/_search
{
  "size": 0,
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "job_id": "nginx-multi-metric"
          }
        },
        {
          "term": {
            "result_type": "bucket"
          }
        },
        {
          "range": {
            "timestamp": {
              "gte": "now-24h"
            }
          }
        }
      ]
    }
  },
  "aggs": {
    "zaman_bazli": {
      "date_histogram": {
        "field": "timestamp",
        "fixed_interval": "10m"
      },
      "aggs": {
        "maks_anomali_skor": {
          "max": {
            "field": "anomaly_score"
          }
        },
        "ciddiyet_kategorisi": {
          "range": {
            "field": "anomaly_score",
            "ranges": [
              { "key": "dusuk", "from": 0, "to": 25 },
              { "key": "orta", "from": 25, "to": 75 },
              { "key": "yuksek", "from": 75, "to": 100 }
            ]
          }
        }
      }
    }
  }
}

Sık Yapılan Hatalar ve Kaçınma Yolları

Anomali tespiti kurulumlarında en çok şu hataları görüyorum:

  • Yetersiz eğitim verisi: ML modelin anlamlı pattern öğrenmesi için en az 2 haftalık, tercihen 4 haftalık veri gerekiyor. Yeni kurduğunuz sistemde hemen anomali beklemeyin.
  • Yanlış bucket_span seçimi: 1 dakikalık bucket_span ile günlük trafik örüntüsünü yakalamaya çalışmak işe yaramaz. Metriğinizin doğal frekansına uygun seçin: operasyonel metrikler için 5-15 dakika, iş metrikleri için 1 saat genellikle iyi başlangıç noktasıdır.
  • Çok sayıda detector bir arada: 20 farklı detector içeren bir job hem yavaş hem de yorumlanması güç sonuçlar üretiyor. İş odaklı 3-5 detector ile başlayın.
  • Model plot’u her job’da açık bırakmak: model_plot_config.enabled: true storage’ı ciddi miktarda artırıyor. Sadece kritik job’larda açın.
  • Anomali skorunu yanlış yorumlamak: 100 üzerinden 75 skor “kesinlikle sorun var” anlamına gelmiyor, “bu normalden çok sapkın” anlamına geliyor. İlk kurulumda false positive’lerle boğuşmak normaldir, model zamanla kalibre olur.

Sonuç

Elasticsearch ve Kibana ile anomali tespiti, kurulumu zaman alan ama getirisi yüksek bir yatırım. Özellikle şunu vurgulamak istiyorum: anomali tespiti, mevcut monitoring’inizi değil tamamlar. Prometheus alertleriniz, Grafana dashboard’larınız, uptime monitörleriniz olacak ve bunların üzerine ML bazlı anomali tespiti ekleyeceksiniz.

Lisans konusunda kafanız karışmasın. Temel Elasticsearch kurulumunda bile aggregation bazlı Z-Score yaklaşımı oldukça güçlü sonuçlar veriyor. Elastic ML özelliklerini denemek istiyorsanız 30 günlük trial ile başlayıp hangi job’ların gerçekten değer kattığını görün.

En önemli tavsiyem: başlangıçta mükemmel bir sistem kurmaya çalışmayın. Bir index, bir ML job, bir Watcher. Sisteminizi öğrenin, false positive’leri ayıklayın, sonra genişletin. Anomali tespiti de anomali üretiyor bazen, buna hazırlıklı olun.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir