Zabbix’te LLD ile Otomatik Keşif ve İzleme
Bir üretim ortamında yüzlerce sunucu yönetirken, her disk bölümü veya ağ arayüzü için tek tek item oluşturmak hem zaman kaybı hem de sürdürülemez bir yaklaşım. Zabbix’in Low-Level Discovery (LLD) özelliği tam bu noktada devreye giriyor ve sistemdeki kaynakları otomatik olarak keşfedip izlemeye başlıyor. Bu yazıda LLD’nin nasıl çalıştığını, kendi discovery rule’larınızı nasıl yazacağınızı ve gerçek senaryolarda nasıl kullanacağınızı ele alacağız.
LLD Nedir ve Neden Önemlidir
Zabbix’te standart bir item oluşturduğunuzda, o item’ın neyi izleyeceğini tam olarak bilmeniz gerekir. /dev/sda1 mi, /dev/sdb1 mi, yoksa /dev/nvme0n1p1 mi? Sunucu sayısı arttıkça bu yaklaşım kabus olmaya başlar.
LLD, Zabbix’e “git, bu sistemde ne var, bul ve otomatik olarak izle” demeni sağlıyor. Bir discovery rule tanımlıyorsun, bu kural sistemi tarar ve bir JSON döndürür. Zabbix bu JSON’daki her eleman için otomatik olarak item, trigger ve graph oluşturur. Sunucuya yeni bir disk eklediğinde? Bir sonraki discovery döngüsünde otomatik olarak izlemeye alınır.
Zabbix’in kendi built-in discovery’leri şunları kapsar:
- vfs.fs.discovery: Dosya sistemlerini keşfeder
- net.if.discovery: Ağ arayüzlerini keşfeder
- system.cpu.discovery: CPU çekirdeklerini keşfeder
- proc.mem[,,,]: Çalışan processleri keşfeder
Ama asıl güç, kendi custom discovery’larini yazabilmende yatıyor.
LLD’nin Temel Bileşenleri
Bir LLD yapısı üç ana bileşenden oluşur:
Discovery Rule: Sistemi tarayıp JSON döndüren kural. Bu bir Zabbix agent item’ı, external check veya custom script olabilir.
Item Prototype: Keşfedilen her eleman için oluşturulacak item şablonu. {#FSNAME} gibi makrolar kullanılır, bunlar discovery’den gelen gerçek değerlerle doldurulur.
Trigger Prototype: Yine keşfedilen her eleman için otomatik oluşturulacak trigger şablonları.
LLD JSON formatı şöyle görünmeli:
{
"data": [
{"{#FSNAME}": "/", "{#FSTYPE}": "ext4"},
{"{#FSNAME}": "/boot", "{#FSTYPE}": "ext4"},
{"{#FSNAME}": "/var", "{#FSTYPE}": "xfs"}
]
}
Buradaki her key, büyük harfle yazılmış ve {# ile başlayan makrolardır. Bu makroları sonradan item prototype’larında kullanacaksın.
Basit Bir Custom Discovery Script Yazmak
Diyelim ki bir sistemde çalışan Nginx virtual host’larını otomatik olarak keşfetmek istiyorsun. Bunun için önce bir discovery script yazalım:
#!/bin/bash
# /etc/zabbix/scripts/discover_nginx_vhosts.sh
NGINX_CONF_DIR="/etc/nginx/sites-enabled"
output='{"data":['
first=1
for conf_file in "$NGINX_CONF_DIR"/*.conf; do
if [ -f "$conf_file" ]; then
server_name=$(grep -m1 "server_name" "$conf_file" | awk '{print $2}' | tr -d ';')
if [ -n "$server_name" ]; then
if [ $first -eq 0 ]; then
output="$output,"
fi
output="$output{"{#VHOST}":"$server_name","{#CONFFILE}":"$conf_file"}"
first=0
fi
fi
done
output="$output]}"
echo "$output"
Bu script, /etc/nginx/sites-enabled/ altındaki her config dosyasından server_name direktifini okuyup JSON formatında döndürüyor. Zabbix bu JSON’u alacak ve her virtual host için ayrı item’lar oluşturacak.
Scripti zabbix kullanıcısı ile çalıştırabilir olduğundan emin ol:
chmod +x /etc/zabbix/scripts/discover_nginx_vhosts.sh
chown zabbix:zabbix /etc/zabbix/scripts/discover_nginx_vhosts.sh
# Test et
sudo -u zabbix /etc/zabbix/scripts/discover_nginx_vhosts.sh
Zabbix Agent Konfigürasyonuna Eklemek
Custom discovery script’ini Zabbix Agent’a tanıtmak için zabbix_agentd.conf veya include dizinindeki bir dosyaya ekliyorsun:
# /etc/zabbix/zabbix_agentd.d/nginx_discovery.conf
UserParameter=nginx.vhost.discovery,/etc/zabbix/scripts/discover_nginx_vhosts.sh
UserParameter=nginx.vhost.status[*],curl -s -o /dev/null -w "%{http_code}" http://$1/nginx_status 2>/dev/null
UserParameter=nginx.vhost.response_time[*],curl -s -o /dev/null -w "%{time_total}" http://$1/ 2>/dev/null
Agent’ı yeniden başlatmayı unutma:
systemctl restart zabbix-agent
# veya agent2 kullanıyorsan:
systemctl restart zabbix-agent2
Sunucudan test etmek için:
zabbix_agentd -t nginx.vhost.discovery
Zabbix Frontend’de Discovery Rule Oluşturmak
Şimdi Zabbix arayüzünde bu discovery’yi aktif hale getirelim. Host ya da template üzerinde Configuration > Hosts > [Host] > Discovery Rules > Create discovery rule yolunu izle.
Temel ayarlar:
- Name: Nginx Virtual Host Discovery
- Type: Zabbix agent
- Key: nginx.vhost.discovery
- Update interval: 1h (saatte bir tarasın yeterli)
- Keep lost resources period: 7d (silinen vhost’ların item’ları 7 gün sonra silinsin)
Discovery rule oluşturduktan sonra sıra Item Prototype’lara geliyor. Discovery rule’un içine gir ve “Item prototypes” sekmesine tıkla.
Örnek bir item prototype:
- Name: Nginx {#VHOST} HTTP Status Code
- Type: Zabbix agent
- Key: nginx.vhost.status[{#VHOST}]
- Type of information: Numeric (unsigned)
- Update interval: 60s
Bir tane daha:
- Name: Nginx {#VHOST} Response Time
- Key: nginx.vhost.response_time[{#VHOST}]
- Type of information: Float
- Units: s
Trigger Prototype Yazmak
Item’lar oluştuktan sonra trigger prototype’lar eklemek gerekiyor. Trigger prototype’lar da aynı makroları kullanır:
{Hostname:nginx.vhost.status[{#VHOST}].last()}<>200
Bu trigger, her keşfedilen virtual host için otomatik olarak oluşturulur. Eğer /etc/nginx/sites-enabled/ altına yeni bir site eklenirse, bir sonraki discovery döngüsünde hem item hem trigger hem de graph otomatik olarak yaratılır.
Daha gelişmiş bir trigger expression örneği:
{Hostname:nginx.vhost.response_time[{#VHOST}].avg(5m)}>2
Bu trigger, son 5 dakikada ortalama response time 2 saniyenin üzerine çıktığında alarm üretir.
Python ile Daha Güçlü Discovery Script’leri
Bash bazen yetersiz kalabiliyor, özellikle kompleks JSON manipülasyonu gerektiğinde. Python ile yazılmış bir örnek:
#!/usr/bin/env python3
# /etc/zabbix/scripts/discover_mysql_databases.py
import subprocess
import json
import sys
def get_mysql_databases():
try:
result = subprocess.run(
['mysql', '-u', 'zabbix_monitor',
'--password=MonitorPass123!',
'-e', 'SHOW DATABASES;',
'--batch', '--skip-column-names'],
capture_output=True, text=True, timeout=10
)
if result.returncode != 0:
sys.exit(1)
# Sistem veritabanlarını filtrele
system_dbs = {'information_schema', 'performance_schema',
'mysql', 'sys'}
databases = []
for line in result.stdout.strip().split('n'):
db_name = line.strip()
if db_name and db_name not in system_dbs:
databases.append({
"{#DBNAME}": db_name
})
return {"data": databases}
except subprocess.TimeoutExpired:
return {"data": []}
except Exception as e:
return {"data": []}
if __name__ == "__main__":
print(json.dumps(get_mysql_databases()))
Bu script MySQL’e bağlanıp kullanıcı veritabanlarını listeliyor ve sistem veritabanlarını filtreliyor. Zabbix bu çıktıyı alıp her veritabanı için otomatik olarak item’lar oluşturacak.
Disk I/O İzleme için Özel LLD
Üretim ortamında sık karşılaştığım bir senaryo: Farklı sunucularda farklı sayıda disk olması. Bazılarında 2 disk, bazılarında 8 disk, SSD/HDD karışık. Standart built-in discovery yeterli olmadığında custom yaklaşım kullanıyorum:
#!/bin/bash
# /etc/zabbix/scripts/discover_block_devices.sh
output='{"data":['
first=1
# Sadece fiziksel disk ve SSD'leri bul, partition değil
while IFS= read -r device; do
# loop device ve ram disk'leri atla
if [[ "$device" =~ ^loop || "$device" =~ ^ram ]]; then
continue
fi
dev_path="/dev/$device"
# Disk tipi belirle (0=HDD, 1=SSD)
rotational=$(cat /sys/block/$device/queue/rotational 2>/dev/null)
disk_type="HDD"
[ "$rotational" = "0" ] && disk_type="SSD"
# Disk boyutu
size_bytes=$(cat /sys/block/$device/size 2>/dev/null)
size_gb=$(echo "scale=0; $size_bytes * 512 / 1024 / 1024 / 1024" | bc 2>/dev/null)
if [ $first -eq 0 ]; then
output="$output,"
fi
output="$output{"{#DEVNAME}":"$device","{#DEVPATH}":"$dev_path","{#DISKTYPE}":"$disk_type","{#DISKSIZE}":"${size_gb}GB"}"
first=0
done < <(ls /sys/block/)
output="$output]}"
echo "$output"
Bu script her disk için {#DEVNAME}, {#DEVPATH}, {#DISKTYPE} ve {#DISKSIZE} makrolarını döndürüyor. Sonra bu makroları item prototype’larda kullanabilirsin:
# zabbix_agentd.conf'a eklenecek
UserParameter=disk.io.read[*],cat /sys/block/$1/stat | awk '{print $3}'
UserParameter=disk.io.write[*],cat /sys/block/$1/stat | awk '{print $7}'
UserParameter=disk.io.await[*],iostat -x $1 1 1 2>/dev/null | grep $1 | awk '{print $10}'
Discovery Filter Kullanımı
Her şeyi izlemek zorunda değilsin. LLD’de filtreler tanımlayarak discovery sonuçlarını kısıtlayabilirsin. Örneğin loop device’leri veya tmpfs dosya sistemlerini izlemek istemiyorsun:
Zabbix frontend’de Discovery Rule içindeki Filters sekmesine git:
- Macro: {#FSTYPE}
- Regular expression: @File systems for discovery
Veya kendi regex’ini yazabilirsin:
- Macro: {#FSNAME}
- Regular expression:
^/(dev|proc|sys|run) - Filter type: Does not match
Bu şekilde /proc, /sys, /dev gibi sanal dosya sistemleri discovery’den dışlanır.
Zabbix’in built-in regex’lerini de kullanabilirsin. Global Regular Expressions bölümünde kendi pattern’larını tanımlayıp @Pattern Adı şeklinde filtre olarak kullanabilirsin.
LLD Makrolarını Template Düzeyinde Override Etmek
Bu özelliği az kişi biliyor ama çok işe yarıyor. Bir template tanımladın, içinde discovery rule var. Ama bazı host’larda discovery interval’ını değiştirmek istiyorsun, diğerlerinde Keep Lost Resources Period’unu. Bunu host düzeyinde override ederek yapabilirsin.
Host > Discovery Rules’a git, ilgili discovery rule’u bul, Override sekmesine tıkla. Burada host’a özel değerler tanımlayabilirsin:
- Update interval: 30m (bu host için daha sık tarasın)
- Keep lost resources period: 1d (bu host için kayıp resource’ları hemen sil)
Bu özellik, aynı template’i farklı ortamlarda (prod, test, dev) kullanırken çok hayat kurtarıyor.
Zabbix API ile Programatik LLD Yönetimi
Büyük ortamlarda bazen discovery kurallarını script ile yönetmek gerekiyor. Zabbix API bunu mümkün kılıyor:
#!/usr/bin/env python3
# Zabbix API ile discovery rule durumunu kontrol et
import requests
import json
ZABBIX_URL = "http://zabbix.sirketim.local/zabbix/api_jsonrpc.php"
ZABBIX_USER = "Admin"
ZABBIX_PASS = "zabbix_admin_pass"
def zabbix_api_call(method, params, auth=None):
payload = {
"jsonrpc": "2.0",
"method": method,
"params": params,
"auth": auth,
"id": 1
}
response = requests.post(ZABBIX_URL,
json=payload,
headers={"Content-Type": "application/json"})
return response.json()
# Login
auth_response = zabbix_api_call("user.login", {
"user": ZABBIX_USER,
"password": ZABBIX_PASS
})
auth_token = auth_response["result"]
# Tüm discovery rule'larını listele
rules_response = zabbix_api_call("discoveryrule.get", {
"output": ["name", "key_", "status", "delay"],
"limit": 100
}, auth=auth_token)
for rule in rules_response["result"]:
status = "AKTIF" if rule["status"] == "0" else "DEVRE DISI"
print(f"Rule: {rule['name']} | Key: {rule['key_']} | Durum: {status} | Interval: {rule['delay']}")
Sorun Giderme: LLD Çalışmıyorsa
Prodüksiyon ortamında LLD ile ilgili karşılaştığım yaygın sorunlar ve çözümleri:
Discovery JSON boş geliyor:
# Agent üzerinde test et
zabbix_agentd -t custom.discovery.key
# Agent logunu kontrol et
tail -f /var/log/zabbix/zabbix_agentd.log | grep -i discovery
# Script'i manuel çalıştır
sudo -u zabbix /path/to/script.sh
Makrolar item’larda görünmüyor:
Discovery rule’un en az bir kez başarıyla çalışmış olması gerekir. Monitoring > Latest Data’dan host’u seç ve discovery key’ini ara. Eğer veri yoksa script hata döndürüyordur.
Keep Lost Resources süresi dolmadan item’lar siliniyor:
Zabbix’in varsayılan MaxHousekeeperDelete değeri 500’dür. Çok fazla item silinmesi gerekiyorsa bu limit nedeniyle işlem birden fazla döngüye yayılabilir.
# zabbix_server.conf
MaxHousekeeperDelete=5000
Permission hatası:
# Sudo ile çalıştırma gerekiyorsa
# /etc/sudoers.d/zabbix dosyasına ekle:
zabbix ALL=(ALL) NOPASSWD: /path/to/privileged_script.sh
Gerçek Dünya Senaryosu: Docker Container Discovery
Dinamik ortamlarda en çok işe yarayan kullanım alanlarından biri Docker container izleme. Hangi container’ların çalıştığı sürekli değişiyor, LLD bu dinamizmi mükemmel şekilde yönetiyor:
#!/bin/bash
# /etc/zabbix/scripts/discover_docker_containers.sh
if ! command -v docker &> /dev/null; then
echo '{"data":[]}'
exit 0
fi
output='{"data":['
first=1
while IFS='|' read -r container_id container_name image; do
if [ $first -eq 0 ]; then
output="$output,"
fi
output="$output{"{#CONTAINER_ID}":"${container_id:0:12}","{#CONTAINER_NAME}":"$container_name","{#IMAGE}":"$image"}"
first=0
done < <(docker ps --format "{{.ID}}|{{.Names}}|{{.Image}}" 2>/dev/null)
output="$output]}"
echo "$output"
Bu discovery ile birlikte container CPU, memory ve network kullanımını izleyen item prototype’lar oluşturabilirsin:
# zabbix_agentd.conf
UserParameter=docker.container.cpu[*],docker stats --no-stream --format "{{.CPUPerc}}" $1 2>/dev/null | tr -d '%'
UserParameter=docker.container.mem[*],docker stats --no-stream --format "{{.MemPerc}}" $1 2>/dev/null | tr -d '%'
UserParameter=docker.container.status[*],docker inspect --format='{{.State.Running}}' $1 2>/dev/null
Container ismi her ortamda farklı olduğu için manuel item yazmak yerine LLD ile bu iş tamamen otomatize oluyor.
Sonuç
LLD, Zabbix’i gerçek anlamda güçlü bir izleme platformuna dönüştüren özelliğin ta kendisi. Yüzlerce sunucuyu yönetirken hangi servislerin, disklerin veya container’ların mevcut olduğunu tek tek takip etmek yerine bir kez iyi yazılmış bir discovery rule ile her şeyi otomatize edebiliyorsun.
En önemli tavsiyem: Discovery script’lerini her zaman önce komut satırından zabbix kullanıcısıyla test et, sonra Zabbix’e ekle. Aksi halde neden çalışmadığını anlamak için saatler harcarsın. Filtreleri akıllıca kullan, her şeyi izlemek monitörünü gürültüye boğar. Ve “Keep Lost Resources Period” değerini ortamına göre ayarla, çok kısa bırakırsan geçici olarak kaybolan resource’ların item geçmişi silinir.
Zabbix 6.x ve sonrasında LLD’nin performansı önemli ölçüde iyileştirildi, özellikle büyük ortamlarda discovery döngüsü artık çok daha hızlı tamamlanıyor. Eğer hâlâ 4.x veya 5.x kullanıyorsan, bir an önce güncel sürüme geçmeyi düşün.
