Zabbix’te Anomaly Detection ile Akıllı İzleme
Klasik eşik tabanlı izlemenin en büyük sorunu şu: prod ortamında her şey “normal” görünürken sistem yavaş yavaş bozulur, siz de alarm almadığınız için farkında bile olmazsınız. CPU %80 eşiğiniz var, sistem %79’da takılı kalıyor, hiç alarm gelmiyor ama kullanıcılar “yavaşlık var” diye şikayet ediyor. İşte Zabbix’in anomaly detection özellikleri tam bu boşluğu doldurmak için var.
Bu yazıda Zabbix 6.x ve 7.x’te gelen akıllı izleme özelliklerini, baseline hesaplamalarını, trend prediction’ı ve bunları gerçek senaryolarda nasıl kullanacağınızı anlatacağım. Sadece belgeyi çevirmeyeceğim, üretim ortamında bizzat çalıştırdığım konfigürasyonları paylaşacağım.
Anomaly Detection Neden Gerekli?
Diyelim ki e-ticaret altyapısını yönetiyorsunuz. Cuma akşamı kampanya başlıyor, trafik 3 katına çıkıyor. Statik eşikleriniz bunu “anormal” olarak değerlendirip alarm yağmuruna tutuyor. Siz de alarm yorgunluğuna girip gerçek sorunları kaçırıyorsunuz.
Ya da tam tersi: Gece 02:00’de bir servis normalde hiç trafik almıyor ama bu gece küçük ama sürekli bir istek akışı var. Statik eşikleriniz bunu yakalamıyor çünkü rakamlar “küçük”. Ama bu, bir bot saldırısının ya da credential stuffing denemesinin başlangıcı olabilir.
Anomaly detection bu iki senaryoda da işe yarıyor çünkü “bu sistem için bu zaman diliminde bu değer normal mi?” sorusunu soruyor. Statik bir sayı yerine tarihsel bağlamı kullanıyor.
Zabbix’te Anomaly Detection Mekanizmaları
Zabbix’in bu konuda sunduğu araçlar birkaç katmanda toplanıyor:
- Baseline functions:
baselinewma(),trendstl()gibi fonksiyonlar - Anomaly detection trigger expressions:
anomalyrate(),changecount()fonksiyonları - Predictive trigger functions:
timeleft(),forecast()fonksiyonları - History functions:
avg(),percentile()ile karşılaştırmalı analizler
Bunları tek tek ele alalım.
Temel Baseline Hesaplaması
En basit yaklaşım geçmiş verilerle karşılaştırma yapmak. Örneğin şu anki değeri geçen haftanın aynı saatiyle kıyaslamak:
# Zabbix trigger expression - geçen haftanın aynı saatine göre %50 sapma
last(/hostname/system.cpu.util) > avg(/hostname/system.cpu.util,1h:now-1w) * 1.5
Bu expression şunu söylüyor: CPU kullanımı geçen hafta aynı saatteki 1 saatlik ortalamanın 1.5 katını geçerse alarm ver. Pazartesi sabahı 09:00’deki CPU spike’ını, Pazar gecesi 03:00’deki referans değeriyle değil, geçen Pazartesi 09:00’deki değerle karşılaştırıyorsunuz.
Daha sofistike bir yaklaşım için birden fazla hafta kullanabilirsiniz:
# 4 haftalık medyan ile karşılaştırma
last(/hostname/system.cpu.util) > (
avg(/hostname/system.cpu.util,1h:now-1w) +
avg(/hostname/system.cpu.util,1h:now-2w) +
avg(/hostname/system.cpu.util,1h:now-3w) +
avg(/hostname/system.cpu.util,1h:now-4w)
) / 4 * 1.4
Bu hesaplama tek bir haftanın anormal olduğu durumları filtreler. Geçen hafta zaten bir sorun yaşadıysanız o hafta referans almanız yanlış baseline oluşturur; 4 haftalık ortalama bunu dengeliyor.
forecast() Fonksiyonu ile Proaktif İzleme
Bu fonksiyon benim favorim. Mevcut trendi kullanarak gelecekte ne olacağını tahmin ediyor ve disk dolmadan, bellek tükenmeden önce uyarı veriyor.
# Disk 24 saat içinde dolacaksa alarm
timeleft(/hostname/vfs.fs.size[/data,pfree],1h,,0) < 86400
# 8 saat sonraki tahmini disk kullanımı %95'i geçecekse alarm
forecast(/hostname/vfs.fs.size[/,pfree],1h,8h) < 5
Bu iki expression arasında nüans var. İlki “kaç saniye kaldı sıfıra ulaşana kadar” diye soruyor, ikincisi “8 saat sonra değer ne olacak” diye. Ben genellikle disk izleme için timeleft() kullanıyorum çünkü lineer olmayan büyüme trendlerini daha iyi yakalıyor.
Gerçek bir üretim konfigürasyonu:
# Kritik: 6 saat içinde disk dolacak
timeleft(/db-server-01/vfs.fs.size[/var/lib/mysql,pfree],3h,,0) < 21600
# Warning: 24 saat içinde disk dolacak
timeleft(/db-server-01/vfs.fs.size[/var/lib/mysql,pfree],3h,,0) < 86400
Burada 3h önemli. Fonksiyona 3 saatlik trend verisini kullanmasını söylüyoruz. Çok kısa tutarsanız anlık spike’lar yanlış tahmin üretir, çok uzun tutarsanız son değişikliklere yavaş adapte oluyor.
trendstl() ile Seasonal Decomposition
Zabbix 6.2 ile gelen bu fonksiyon, zaman serisini trend, mevsimsellik ve artık bileşenlerine ayırıyor. STL (Seasonal and Trend decomposition using Loess) algoritmasını kullanıyor.
# Haftalık periyotla anomali skoru hesaplama
trendstl(/hostname/net.if.in[eth0],1h:now-90d,168h,percentile,99,10h)
Bu parametreleri açıklayayım:
1h:now-90d: Son 90 günün saatlik verisi168h: Periyot (168 saat = 1 hafta)percentile: Fit yöntemi99: 99. persentil10h: Öngörü penceresi
Pratikte bunu şöyle bir trigger’da kullanıyorum:
# Network trafiğinde haftalık pattern'e göre anomali
trendstl(/edge-router-01/net.if.in[eth0,bytes],1h:now-60d,168h,percentile,95,2h) >
avg(/edge-router-01/net.if.in[eth0,bytes],2h) * 2
changecount() ile Ani Değişim Tespiti
Değerin ne kadar olduğundan ziyade ne kadar hızlı değiştiği önemli olan senaryolarda changecount() işe yarıyor:
# Son 10 dakikada 5'ten fazla değer değişimi olduysa
changecount(/hostname/system.cpu.util,10m,,weak) > 5
Bu özellikle unstable servisleri yakalamak için kullanışlı. CPU değeri sürekli iniş çıkış yapıyor, ama ortalama “normal” görünüyor. changecount() bunu yakalıyor.
Makine Öğrenmesi Entegrasyonu: Zabbix + External ML
Zabbix’in native fonksiyonları iyi ama bazı senaryolarda yetmiyor. Özellikle multivariate anomaly detection, yani birden fazla metriğin birlikte değerlendirilmesi gerektiğinde dışarı çıkmak gerekiyor.
Bu iş için Python tabanlı bir harici script yaklaşımı:
#!/usr/bin/env python3
# /usr/local/bin/zabbix_anomaly_detector.py
import requests
import numpy as np
from sklearn.ensemble import IsolationForest
import json
import sys
ZABBIX_URL = "http://zabbix-server/api_jsonrpc.php"
ZABBIX_TOKEN = "your_api_token_here"
def get_history(host, key, hours=72):
"""Zabbix API'den geçmiş veri çek"""
payload = {
"jsonrpc": "2.0",
"method": "history.get",
"params": {
"output": "extend",
"hostids": get_hostid(host),
"itemids": get_itemid(host, key),
"history": 0,
"sortfield": "clock",
"sortorder": "DESC",
"limit": hours * 12 # 5 dakikalık intervaller
},
"auth": ZABBIX_TOKEN,
"id": 1
}
response = requests.post(ZABBIX_URL, json=payload)
return [float(item['value']) for item in response.json()['result']]
def detect_anomaly(values, contamination=0.05):
"""Isolation Forest ile anomali tespiti"""
X = np.array(values).reshape(-1, 1)
clf = IsolationForest(contamination=contamination, random_state=42)
clf.fit(X[:-1]) # Son değer hariç eğit
# Son değeri kontrol et
last_value = X[-1].reshape(1, -1)
prediction = clf.predict(last_value)
score = clf.score_samples(last_value)[0]
return prediction[0] == -1, score # -1 = anomali
def main():
hostname = sys.argv[1]
item_key = sys.argv[2]
values = get_history(hostname, item_key)
is_anomaly, score = detect_anomaly(values)
# Zabbix external check formatında çıktı
if is_anomaly:
print(f"1|{score:.4f}")
else:
print(f"0|{score:.4f}")
if __name__ == "__main__":
main()
Bu script’i Zabbix external check olarak tanımlamak için:
# /etc/zabbix/zabbix_server.conf
ExternalScripts=/usr/local/bin
# Zabbix item key
anomaly.detector[{HOST.NAME},system.cpu.util]
Gerçek Dünya Senaryosu: E-Ticaret Platformu İzleme
Bir e-ticaret platformunu düşünelim. Hafta içi/hafta sonu farkı büyük, kampanya dönemlerinde trafik patlaması var. Bu ortam için kurduğum izleme stratejisini paylaşayım.
Senaryo 1: Sipariş API yanıt süresi anomalisi
# Zabbix calculated item - 15 dakikalık rolling z-score
(last(/api-server-01/web.test.time[order_api,checkout,resp]) -
avg(/api-server-01/web.test.time[order_api,checkout,resp],1h:now-1w)) /
stddevpop(/api-server-01/web.test.time[order_api,checkout,resp],1h:now-1w)
Bu expression geçen haftanın aynı saatindeki standart sapmayı referans alarak z-score hesaplıyor. Z-score 3’ü geçerse, yani ortalamadan 3 standart sapma uzaklaşırsa alarm üretiyoruz:
# Trigger
last(/api-server-01/calculated.zscore.order_api) > 3
Senaryo 2: Gece yarısı anomali tespiti
Gece 02:00-04:00 arası neredeyse sıfır olması gereken bir endpoint’e trafik gelmeye başladı. Bu bir scraper, bot ya da internal bir runaway process olabilir.
# Time-based anomaly - gece saatlerinde beklenmedik trafik
(hour()>=2 and hour()<=4) and
last(/web-server-01/nginx.requests.per.second) >
avg(/web-server-01/nginx.requests.per.second,30m:now-1w) * 3 + 10
+ 10 kısmı önemli. Referans değer 0 veya çok küçük olduğunda 3 ile çarpma işe yaramıyor; absolute bir taban değer ekliyoruz.
Zabbix Preprocessing ile Veri Temizleme
Anomaly detection’ın kalitesi gelen verinin kalitesine bağlı. Gürültülü veriyle çalışıyorsanız önce temizlemeniz gerekiyor.
# Zabbix preprocessing - JavaScript ile outlier temizleme
var value = parseFloat(value);
var history = JSON.parse(params); // Önceki değerler
// Basit IQR tabanlı outlier filtresi
var sorted = history.sort((a, b) => a - b);
var q1 = sorted[Math.floor(sorted.length * 0.25)];
var q3 = sorted[Math.floor(sorted.length * 0.75)];
var iqr = q3 - q1;
var lower = q1 - 1.5 * iqr;
var upper = q3 + 1.5 * iqr;
if (value < lower || value > upper) {
return null; // Outlier'i discard et
}
return value;
Bu JavaScript preprocessing step’ini Zabbix item’larına ekleyerek ham veriyi temizleyebilirsiniz. Özellikle SNMP’den gelen counter resets, network timeouts sonrası sıfır değerler gibi sorunları bu şekilde çözüyorum.
Alarm Yorgunluğunu Önlemek için Hysteresis ve Flapping Detection
Anomaly detection kurduğunuzda en büyük tehlike alarm yorgunluğu. Sistem “anormal, normal, anormal, normal” diye sallanıyorsa bildirim patlaması yaşarsınız.
# PROBLEM durumu: Değer eşiği 2 kez arka arkaya geçtiyse
min(/hostname/system.cpu.util,#3) > avg(/hostname/system.cpu.util,1h:now-1w) * 1.6
# RECOVERY durumu: Değer 5 dakika boyunca normale döndüyse
max(/hostname/system.cpu.util,5m) < avg(/hostname/system.cpu.util,1h:now-1w) * 1.3
Problem ve recovery eşiklerini farklı tutmak (1.6 vs 1.3) hysteresis sağlıyor. Sistem eşiğin tam üstünde sallanıyorsa alarm üretmiyor.
Zabbix’in nodata() fonksiyonunu anomaly detection ile birleştirmek de güzel bir pattern:
# Veri gelmiyor VEYA anomali var
nodata(/hostname/app.requests,5m) or
last(/hostname/app.requests) > forecast(/hostname/app.requests,2h,1h) * 2
Sonuç
Anomaly detection mükemmel bir çözüm değil. Yeterli geçmiş veri olmadan (en az 2-4 hafta öneririm) baseline’lar güvenilmez oluyor. Sistemde büyük değişiklikler yaptığınızda (deployment, scale-out, hardware değişimi) historik verileri resetlemeniz ya da en azından bu değişim noktasını işaretlemeniz gerekiyor.
Başlangıç için şu sırayı öneririm: Önce forecast() ve timeleft() ile disk ve memory için proaktif uyarılar kurun, bunlar hemen değer üretiyor. Sonra kritik servisleriniz için haftalık baseline karşılaştırması ekleyin. En son olarak trendstl() ve external ML entegrasyonuna geçin; bunlar daha fazla tuning gerektiriyor.
Statik eşiklerden tamamen vazgeçin demiyorum. CPU %95 üstünde alarm vermek hala gerekli. Ama bu eşiklerin yanına “bu sistem için bu zaman diliminde bu değer alışılmadık mı?” sorusunu soran katmanı ekleyin. İki yaklaşım birbirini tamamlıyor; anomaly detection statik eşiklerin körlüğünü kapatıyor, statik eşikler de ML modelinin atlayabileceği açık hataları yakalıyor.
En önemli tavsiyem şu: Alarm sayısına değil, alarm kalitesine odaklanın. Günde 200 alarm üretip 190’ını kapatıyorsanız o 10 gerçek alarm da gömülüp gidiyor. Anomaly detection doğru kurulduğunda alarm sayısını düşürüyor, anlam ifade eden uyarıları öne çıkarıyor. Bu da on-call hayatını ciddi ölçüde yaşanılır kılıyor.
