Unbound DNS Sunucusunda Cache Ayarları ve Performans Optimizasyonu

DNS sunucunuzun cache ayarlarını doğru yapılandırmazsanız, günde milyonlarca sorgu işleyen bir sistemde bile performansın beklenenden çok daha kötü olduğunu fark edersiniz. Bunu ilk kez bir e-ticaret müşterisinde yaşadım: Unbound kuruluydu, çalışıyordu, ama yük altında yanıt süreleri 200-300ms’ye çıkıyordu. Sorun cache değildi, cache’in yanlış boyutlandırılmasıydı. Bu yazıda Unbound’da cache mekanizmasını, doğru boyutlandırmayı ve gerçek anlamda fark yaratan optimizasyon parametrelerini ele alacağım.

Unbound Cache Nasıl Çalışır?

Unbound’un cache sistemi birkaç farklı bileşenden oluşur ve her birinin ayrı bir rolü vardır. Bunu anlamadan yapılandırmaya başlamak, karanlıkta ayar yapmaya benzer.

rrset-cache: DNS kayıt setlerini (A, AAAA, MX, vb.) depolar. Bu cache’in boyutu, ne kadar çok farklı domain’i bellekte tutabileceğinizi doğrudan belirler.

msg-cache: Tam DNS mesajlarını saklar. Bir sorguya gelen tüm yanıt paketini önbelleğe alır. Genellikle rrset-cache’in yarısı kadar boyutlandırılır.

key-cache: DNSSEC doğrulaması için kullanılan anahtarları tutar. DNSSEC kullanıyorsanız bu cache kritik önem taşır.

neg-cache: NXDOMAIN ve NOERROR yanıtlarını, yani “bu domain yok” cevaplarını saklar. Özellikle spam/botnet trafiğini bertaraf etmede çok işe yarar.

Unbound bu cache’leri varsayılan olarak oldukça muhafazakâr boyutlarda açar. Üretim ortamında bu varsayılanlarla gidilmez.

Temel Cache Parametrelerini Anlamak

/etc/unbound/unbound.conf dosyasına bakmadan önce mevcut cache kullanımını görmek iyi bir başlangıç noktası:

# Mevcut cache istatistiklerini görüntüle
unbound-control stats_noreset | grep -E "cache|num."

# Veya daha okunabilir formatta
unbound-control stats_noreset | grep cache

Şimdi parametrelerin ne anlama geldiğine bakalım:

rrset-cache-size: RRset önbelleğinin boyutu. Bayt cinsinden veya m/k/g sonekleriyle girilir. Varsayılan 4m, üretim için genellikle yetersiz.

msg-cache-size: Mesaj önbelleğinin boyutu. Kural olarak rrset-cache-size’ın yarısı yapılır.

key-cache-size: DNSSEC anahtar önbelleği. DNSSEC aktifse ihmal etmeyin.

cache-min-ttl: Bir kaydın cache’de kalacağı minimum süre. Çok düşük TTL’li domainler için bant genişliği tasarrufu sağlar.

cache-max-ttl: Maksimum cache süresi. Authoritative sunucuların verdiği TTL’den uzun tutmak istemezsiniz genellikle.

cache-max-negative-ttl: Negatif yanıtların (NXDOMAIN) maksimum cache süresi.

serve-expired: Cache’deki süresi dolmuş kayıtları TTL yenilenirken geçici olarak sunmaya devam etme özelliği. Latency açısından muazzam fark yaratır.

prefetch: TTL’nin %10’u kalmışken kaydı arka planda yenilemeye başlar. serve-expired ile birlikte kullanılınca çok güçlüdür.

Temel Yapılandırma Dosyası

Küçük-orta ölçekli bir ortam için başlangıç noktası olarak şu yapılandırmayı kullanabilirsiniz:

# /etc/unbound/unbound.conf - Temel cache optimizasyonu

server:
    # Cache boyutları
    rrset-cache-size: 256m
    msg-cache-size: 128m
    key-cache-size: 32m
    
    # Negatif cache
    cache-max-negative-ttl: 3600
    
    # TTL sınırları
    cache-min-ttl: 300
    cache-max-ttl: 86400
    
    # Prefetch ve expired serve
    prefetch: yes
    prefetch-key: yes
    serve-expired: yes
    serve-expired-ttl: 3600
    serve-expired-reply-ttl: 30
    
    # Thread ve slice ayarları
    num-threads: 4
    msg-cache-slabs: 8
    rrset-cache-slabs: 8
    infra-cache-slabs: 8
    key-cache-slabs: 8

Slab sayısını thread sayısının iki katı yapmanız, lock contention’ı azaltır. 4 thread için 8 slab idealdir.

Cache Boyutlandırma Hesabı

“Kaç MB yeter?” sorusunun cevabı sunucunuzun profiline göre değişir ama bir formül verebilirim. Önce şunu çalıştırın:

# Son 24 saatin unique domain sayısını tahmin etmek için
unbound-control stats_noreset | grep "total.num.queries"
unbound-control stats_noreset | grep "cache.count"

# Cache hit oranını hesapla
unbound-control stats_noreset | grep -E "cache_hits|cache_miss"

Genel kural şu: Her cached RRset ortalama 100-200 byte yer kaplar. 1 milyon unique domain tutmak istiyorsanız yaklaşık 150-200MB rrset-cache yeterli olur. Ancak her ortam farklı sorgu dağılımına sahiptir.

Sunucunuzdaki RAM durumunu da hesaba katın:

# Sistem bellek durumu
free -h

# Unbound'un şu an ne kadar bellek kullandığı
ps aux | grep unbound | awk '{print $6}'

# Daha detaylı
cat /proc/$(pgrep unbound)/status | grep -E "VmRSS|VmSize"

Pratik önerim: Sunucunuzdaki toplam RAM’in %20-25’ini Unbound cache’e ayırın. 16GB RAM’li bir sunucuda 3-4GB cache tamamen makul bir hedeftir.

İleri Seviye Cache Optimizasyonu

Temel ayarların ötesine geçelim. Bu parametreler çoğu belgede bahsedilmez ama fark yaratan detaylardır.

Serve-Expired ile Sıfır Kesinti

serve-expired özelliği Unbound’un en güçlü cache özelliklerinden biridir. TTL süresi dolduğunda Unbound normalde yeni sorgu yapana kadar bekler. serve-expired açıkken eski kaydı hemen döner, arka planda yenileme yapar:

server:
    serve-expired: yes
    serve-expired-ttl: 86400        # Süresi dolan kayıt kaç saniye daha sunulsun
    serve-expired-reply-ttl: 30     # Client'a döndürülen TTL değeri
    serve-expired-client-timeout: 0 # 0 = hemen eski kaydı ver, background'da yenile

serve-expired-client-timeout parametresine dikkat edin. 0 yaparsanız Unbound hiç beklemeden eski kaydı döner. Pozitif bir değer (örneğin 1800 ms) girerseniz, o süre içinde yeni yanıt gelirse yeni kaydı, gelmezse eski kaydı döner. Latency öncelikli ortamlarda 0 tercih edilir.

Prefetch Davranışını İncelemek

# Prefetch istatistiklerini izle
watch -n 5 "unbound-control stats_noreset | grep prefetch"

# Toplam istatistikler
unbound-control stats | grep -E "total.|cache."

Prefetch’in cache hit oranına etkisini görmek için bunu birkaç saat izleyin. Popüler domainlerin yüksek trafikte neredeyse hiç miss vermediğini göreceksiniz.

Infrastructure Cache Ayarları

Infra-cache, upstream DNS sunucularının yanıt süreleri, RTT değerleri ve güvenilirlik bilgilerini tutar. Bu cache iyi boyutlandırılmazsa Unbound her seferinde kötü bir upstream’e gidip zaman kaybedebilir:

server:
    infra-cache-numhosts: 100000    # Cache'de tutulacak maksimum host sayısı
    infra-cache-slabs: 8
    infra-host-ttl: 900             # Host bilgisinin cache süresi (saniye)
    infra-cache-min-rtt: 50         # Minimum RTT tahmini (ms)

infra-cache-numhosts değerini çok düşük bırakmak, Unbound’un upstream sunucu seçimini bozabilir. Büyük ağlarda bu değeri 500000’e kadar çıkarmanız gerekebilir.

Gerçek Dünya Senaryosu: Yüksek Yük Altında Optimizasyon

Bir ISP altyapısında Unbound’u optimize etmek durumunda kaldım. Sunucu günde yaklaşık 50 milyon sorgu işliyordu ve cache hit oranı %62 civarındaydı. Hedef %85’ti.

Sorun analizi:

# Detailed stats dump
unbound-control stats_noreset > /tmp/before_opt.txt

# Cache'de ne kadar yer dolduğuna bak
unbound-control stats | grep "cache.count"

# Query type dağılımı
unbound-control stats | grep "num.query.type"

Cache count, max kapasiteye yakındı. Yani cache doluydu ve yeni kayıtlar eski kayıtları dışarı atıyordu (eviction). Çözüm cache boyutunu artırmaktı ama önce neyin cache’de yer kapladığına bakmak gerekiyordu.

AAAA sorguları toplam sorguların %38’ini oluşturuyordu ama network’te IPv6 yoktu. Bu sorgular NXDOMAIN veya hata dönüyordu. cache-max-negative-ttl 60 saniyeydi, yani bu başarısız AAAA sorguları sürekli upstream’e gidiyordu. Çözüm:

server:
    # IPv6 yoksa AAAA sorgularını optimize et
    cache-max-negative-ttl: 3600    # 60'tan 3600'e çıkardık
    
    # Do-not-query IPv6
    do-ip6: no                      # IPv6 yoksa sorgu atmayı durdur
    
    # Cache boyutunu artır
    rrset-cache-size: 1024m
    msg-cache-size: 512m

Bu değişiklikten sonra cache hit oranı 48 saat içinde %91’e çıktı. Upstream sorgular neredeyse üçte ikisi azaldı.

Thread ve Slab Optimizasyonu

Modern sunucularda çok çekirdek kullanmak kritik öneme sahiptir. Unbound varsayılan olarak tek thread açar ve bu büyük bir kayıptır:

# CPU çekirdek sayısını öğren
nproc

# Unbound için thread sayısını ayarla
# unbound.conf içinde:
server:
    num-threads: 8           # Fiziksel çekirdek sayısı kadar
    
    # Slab sayıları - thread sayısının katları olmalı
    msg-cache-slabs: 16      # 2x thread sayısı
    rrset-cache-slabs: 16
    infra-cache-slabs: 16
    key-cache-slabs: 16
    
    # Her thread için SO_REUSEPORT
    so-reuseport: yes        # Linux 3.9+ gerekli

so-reuseport parametresi kernel seviyesinde yük dengeleme sağlar. Bu olmadan threadler arasında bağlantı dağılımı dengesiz olabilir.

Buffer ve Network Optimizasyonu

Cache’in yanı sıra network katmanında da optimizasyon yapılabilir:

server:
    # Socket buffer boyutları
    so-rcvbuf: 8m
    so-sndbuf: 8m
    
    # UDP ve TCP için
    udp-upstream-without-downstream: no
    
    # Outgoing port çeşitliliği - güvenlik ve performans
    outgoing-range: 8192
    num-queries-per-thread: 4096
    
    # EDNS buffer
    edns-buffer-size: 1232    # RFC 8085 önerisi

so-rcvbuf ve so-sndbuf için kernel sınırlarını da artırmanız gerekebilir:

# Sistem seviyesinde kernel buffer sınırlarını artır
echo 'net.core.rmem_max=16777216' >> /etc/sysctl.conf
echo 'net.core.wmem_max=16777216' >> /etc/sysctl.conf
echo 'net.core.rmem_default=8388608' >> /etc/sysctl.conf
echo 'net.core.wmem_default=8388608' >> /etc/sysctl.conf
sysctl -p

# Unbound'un bu limitlere ulaşıp ulaşmadığını kontrol et
unbound-control stats | grep "mem.streamwait"

Cache İzleme ve Alarm Kurulumu

Optimizasyonu yapıp bırakmak olmaz. Sürekli izleme şart:

#!/bin/bash
# /usr/local/bin/unbound-cache-monitor.sh

STATS=$(unbound-control stats_noreset)

TOTAL_QUERIES=$(echo "$STATS" | grep "total.num.queries=" | cut -d= -f2)
CACHE_HITS=$(echo "$STATS" | grep "total.num.cachehits=" | cut -d= -f2)
CACHE_MISS=$(echo "$STATS" | grep "total.num.cachemiss=" | cut -d= -f2)

if [ "$TOTAL_QUERIES" -gt 0 ]; then
    HIT_RATIO=$(echo "scale=2; $CACHE_HITS * 100 / $TOTAL_QUERIES" | bc)
    echo "Cache Hit Ratio: %${HIT_RATIO}"
    
    # %70 altına düşerse uyar
    THRESHOLD=70
    HIT_INT=$(echo "$HIT_RATIO" | cut -d. -f1)
    
    if [ "$HIT_INT" -lt "$THRESHOLD" ]; then
        echo "UYARI: Cache hit orani dusuk: %${HIT_RATIO}"
        # Buraya mail/slack bildirimi eklenebilir
        logger -t unbound-monitor "WARN: Cache hit ratio below threshold: ${HIT_RATIO}%"
    fi
fi

# Cache doluluk oranı
RRSET_COUNT=$(echo "$STATS" | grep "rrset.cache.count=" | cut -d= -f2)
echo "RRset cache kayit sayisi: $RRSET_COUNT"

Bu scripti cron’a ekleyin:

chmod +x /usr/local/bin/unbound-cache-monitor.sh
echo "*/5 * * * * root /usr/local/bin/unbound-cache-monitor.sh >> /var/log/unbound-cache.log 2>&1" >> /etc/crontab

Cache Temizleme ve Güncelleme

Bazen belirli bir domain’in cache’ini temizlemeniz gerekir. Örneğin DNS değişikliği yaptınız ama Unbound eski kaydı tutmaya devam ediyor:

# Belirli bir domain'i cache'den sil
unbound-control flush example.com

# Bir domain ve tüm alt domainlerini sil
unbound-control flush_zone example.com

# Tüm cache'i temizle (dikkatli kullanın!)
unbound-control flush_all

# Belirli bir tip için cache'i temizle
unbound-control flush_type example.com A

# Negative cache'i temizle
unbound-control flush_negative

# Cache'in içeriğini dump et (debug için)
unbound-control dump_cache > /tmp/cache_dump.txt

# Cache'i geri yükle (restart sonrası cache ısınma için)
unbound-control load_cache < /tmp/cache_dump.txt

load_cache ve dump_cache ikilisi özellikle restart senaryolarında çok değerlidir. Servis yeniden başlatılsa bile cache’i kaybetmezsiniz, bu büyük sistemlerde kritik öneme sahiptir.

DNSSEC ile Cache Optimizasyonu

DNSSEC aktif ortamlarda cache yönetimi biraz daha özel dikkat ister:

server:
    # DNSSEC doğrulama
    auto-trust-anchor-file: "/var/lib/unbound/root.key"
    
    # DNSSEC cache optimizasyonu
    key-cache-size: 64m
    key-cache-slabs: 8
    prefetch-key: yes           # Anahtarları da önceden getir
    
    # Aggressive NSEC - cache'i daha verimli kullan
    aggressive-nsec: yes        # NSEC kayıtlarından negatif yanıt üret
    
    # NSEC3 için
    val-nsec3-keysize-iterations: "1024 150 2048 500 4096 2500"

aggressive-nsec parametresi özellikle dikkat çekici: NSEC kayıtlarını kullanarak cache’deki bilgilerden negatif yanıt üretir, upstream’e gitmeden. DNSSEC imzalı zone’larda upstream sorgularını önemli ölçüde azaltır.

Tam Optimizasyon Yapılandırması

Tüm bunları bir araya getiren production-ready bir yapılandırma:

server:
    # Temel ağ ayarları
    interface: 0.0.0.0
    port: 53
    do-ip4: yes
    do-ip6: no
    do-udp: yes
    do-tcp: yes
    
    # Thread ve slab - sunucuya göre ayarla
    num-threads: 8
    msg-cache-slabs: 16
    rrset-cache-slabs: 16
    infra-cache-slabs: 16
    key-cache-slabs: 16
    so-reuseport: yes
    
    # Cache boyutları - RAM'e göre ayarla
    rrset-cache-size: 512m
    msg-cache-size: 256m
    key-cache-size: 64m
    infra-cache-numhosts: 200000
    
    # TTL yönetimi
    cache-min-ttl: 300
    cache-max-ttl: 86400
    cache-max-negative-ttl: 3600
    
    # Prefetch ve serve-expired
    prefetch: yes
    prefetch-key: yes
    serve-expired: yes
    serve-expired-ttl: 86400
    serve-expired-reply-ttl: 30
    serve-expired-client-timeout: 0
    
    # Network buffers
    so-rcvbuf: 8m
    so-sndbuf: 8m
    outgoing-range: 8192
    num-queries-per-thread: 4096
    edns-buffer-size: 1232
    
    # DNSSEC
    auto-trust-anchor-file: "/var/lib/unbound/root.key"
    aggressive-nsec: yes
    
    # Logging - production'da minimal tut
    verbosity: 1
    log-queries: no
    log-replies: no

Bu yapılandırmayı uyguladıktan sonra mutlaka test edin:

# Yapılandırma doğruluğunu kontrol et
unbound-checkconf /etc/unbound/unbound.conf

# Servisi yeniden başlat
systemctl restart unbound

# İlk istatistikleri al
sleep 60
unbound-control stats_noreset

Sonuç

Unbound cache optimizasyonu tek seferlik bir iş değildir. Önce mevcut durumu ölçün, sonra değişiklik yapın, ardından tekrar ölçün. Cache boyutu, thread sayısı, prefetch ve serve-expired ayarları tek başlarına bile büyük fark yaratır ama asıl güç bunların birlikte doğru yapılandırılmasından gelir.

Pratikte en çok etkiyi yaratan değişiklikler sırasıyla şunlardır: serve-expired açmak, cache-max-negative-ttl‘yi artırmak, slab sayılarını doğru ayarlamak ve cache boyutunu gerçekçi olarak boyutlandırmak. Bunları yaptıktan sonra izleme altyapısını kurun, cache hit oranını takip edin ve zaman içinde sisteminizin profiline göre ince ayar yapmaya devam edin.

Unbound’un istatistik sistemi oldukça zengindir. O verileri düzenli okuyorsanız sisteminizin size ne söylediğini anlayabilirsiniz. Söylemiyorsa, veriye bakmıyorsunuzdur.

Bir yanıt yazın

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