Unbound ile Anycast DNS Yapılandırması
Büyük ölçekli bir DNS altyapısı kurarken en çok zorlanan konulardan biri, coğrafi olarak dağıtık sunucuları tek bir IP adresi üzerinden yönetilebilir hale getirmektir. Anycast bu sorunu çözen en zarif yöntemlerden biri ve Unbound ile birleştiğinde ciddi bir güç ortaya çıkıyor. Bu yazıda teorik anlatımdan çok, gerçek bir kurulum senaryosu üzerinden ilerleyeceğiz.
Anycast DNS Nedir ve Neden Unbound?
Anycast, aynı IP adresinin birden fazla fiziksel konumda duyurulması prensibine dayanır. BGP yönlendirme protokolü üzerinden çalışan bu yaklaşımda, istemci paketi gönderdiğinde ağ altyapısı otomatik olarak en yakın (en düşük maliyetli) sunucuya yönlendirir. DNS için bu yaklaşım son derece güçlüdür çünkü hem yük dengeleme hem de coğrafi yönlendirme hem de yüksek erişilebilirlik tek seferde çözülmüş olur.
Unbound’u bu senaryoda tercih etmemizin birkaç somut sebebi var. Birincisi, DNSSEC doğrulamasını kutudan çıkar çıkmaz destekliyor. İkincisi, cache yönetimi BIND’a kıyasla çok daha öngörülebilir davranıyor. Üçüncüsü ise yapılandırma dosyaları okunabilir ve modüler, büyük kurulumları yönetmek için ideal.
Altyapı Gereksinimleri
Bu kurulum için aşağıdaki bileşenlere ihtiyacınız var:
- En az iki ayrı lokasyonda sunucu (örneğin İstanbul ve Ankara veri merkezleri)
- Her iki lokasyonda da BGP konuşabilen bir router ya da yazılım tabanlı BGP daemon (BIRD veya FRRouting)
- Anycast IP bloğu için bir ASN ve IP prefix’i
- Her sunucuda Unbound kurulu olacak
Ağ topolojisi açısından şöyle düşünebilirsiniz: 203.0.113.0/24 bloğunuz var ve bu bloğun içinden 203.0.113.53 adresini anycast DNS IP’si olarak kullanacaksınız. Her iki lokasyon da bu /24’ü BGP üzerinden duyuruyor.
Unbound Kurulumu
Debian/Ubuntu tabanlı sistemlerde kurulum oldukça basit:
apt update && apt install -y unbound unbound-anchor
# DNSSEC root key'i güncelle
unbound-anchor -a /var/lib/unbound/root.key
# Servis durumunu kontrol et
systemctl status unbound
RHEL/Rocky Linux sistemler için:
dnf install -y unbound
# Root hints dosyasını indir
curl -o /etc/unbound/root.hints https://www.internic.net/domain/named.cache
systemctl enable --now unbound
Anycast IP Adresini Loopback Arayüzüne Bağlama
Anycast kurulumunun püf noktası burada başlıyor. Her sunucuda anycast IP adresini loopback arayüzüne ya da ayrı bir dummy arayüze atamanız gerekiyor. Bu arayüz üzerinden servis duyurulacak, BGP bu arayüzün durumuna göre prefix’i duyurup duyurmamaya karar verecek.
# Dummy modülünü yükle
modprobe dummy
# Kalıcı hale getir
echo "dummy" >> /etc/modules-load.d/dummy.conf
# Anycast arayüzü oluştur
ip link add anycast0 type dummy
ip addr add 203.0.113.53/32 dev anycast0
ip link set anycast0 up
# Systemd-networkd ile kalıcı yapılandırma
cat > /etc/systemd/network/10-anycast0.netdev << 'EOF'
[NetDev]
Name=anycast0
Kind=dummy
EOF
cat > /etc/systemd/network/10-anycast0.network << 'EOF'
[Match]
Name=anycast0
[Address]
Address=203.0.113.53/32
[Link]
RequiredForOnline=no
EOF
systemctl restart systemd-networkd
Unbound Yapılandırması
Şimdi Unbound’u bu anycast IP üzerinden dinleyecek şekilde yapılandıralım. Büyük kurulumlar için yapılandırmayı modüler tutmak önemli, bu yüzden conf.d dizin yapısı kullanacağız.
# Ana yapılandırma dosyası
cat > /etc/unbound/unbound.conf << 'EOF'
server:
# Anycast IP ve standart DNS portunu dinle
interface: 203.0.113.53
interface: 127.0.0.1
port: 53
# Performans ayarları - modern donanım için
num-threads: 4
msg-cache-slabs: 8
rrset-cache-slabs: 8
infra-cache-slabs: 8
key-cache-slabs: 8
# Cache boyutları
msg-cache-size: 512m
rrset-cache-size: 1024m
# DNSSEC doğrulama
auto-trust-anchor-file: "/var/lib/unbound/root.key"
# Erişim kontrolü - recursive sorgu için
access-control: 127.0.0.0/8 allow
access-control: 10.0.0.0/8 allow
access-control: 172.16.0.0/12 allow
access-control: 192.168.0.0/16 allow
access-control: 0.0.0.0/0 refuse
# Root hints
root-hints: "/etc/unbound/root.hints"
# Güvenlik ayarları
hide-identity: yes
hide-version: yes
harden-glue: yes
harden-dnssec-stripped: yes
use-caps-for-id: yes
harden-large-queries: yes
# Prefetch ile cache ısınması
prefetch: yes
prefetch-key: yes
# Log ayarları
logfile: "/var/log/unbound/unbound.log"
log-queries: no
log-replies: no
verbosity: 1
# SO_REUSEPORT aktif et
so-reuseport: yes
# TCP ve buffer ayarları
tcp-upstream: no
outgoing-range: 8192
num-queries-per-thread: 4096
include: "/etc/unbound/conf.d/*.conf"
EOF
mkdir -p /var/log/unbound /etc/unbound/conf.d
chown unbound:unbound /var/log/unbound
BGP ile Prefix Duyurusu – FRRouting Yapılandırması
FRRouting (FRR) açık kaynak BGP implementasyonu için günümüzde en yaygın tercih. Sunucuda doğrudan BGP konuşturmak için kuruyoruz.
# FRR kurulumu (Debian/Ubuntu)
curl -s https://deb.frrouting.org/frr/keys.asc | apt-key add -
echo "deb https://deb.frrouting.org/frr $(lsb_release -s -c) frr-stable" > /etc/apt/sources.list.d/frr.list
apt update && apt install -y frr frr-pythontools
# BGP daemon'ı aktif et
sed -i 's/bgpd=no/bgpd=yes/' /etc/frr/daemons
systemctl restart frr
FRR BGP yapılandırması için vtysh üzerinden:
vtysh << 'EOF'
configure terminal
router bgp 65001
bgp router-id 203.0.113.1
bgp log-neighbor-changes
neighbor 198.51.100.1 remote-as 65000
neighbor 198.51.100.1 description "Upstream Router Istanbul"
neighbor 198.51.100.1 password BGP_SECRET_PASS
address-family ipv4 unicast
network 203.0.113.0/24
neighbor 198.51.100.1 activate
neighbor 198.51.100.1 soft-reconfiguration inbound
exit-address-family
ip route 203.0.113.0/24 Null0
exit
write memory
EOF
Sağlık Kontrolü ve Otomatik BGP Geri Çekme
Bu kısım anycast kurulumunun en kritik bölümü. Unbound servis dışı kaldığında ya da sağlıksız hale geldiğinde, o lokasyonun BGP prefix’ini otomatik olarak geri çekmesi gerekiyor. Aksi halde istemciler sağlıksız bir sunucuya yönlendirilmeye devam eder.
Bunun için basit ama etkili bir sağlık kontrolü scripti yazalım:
cat > /usr/local/bin/dns-healthcheck.sh << 'SCRIPT'
#!/bin/bash
ANYCAST_IP="203.0.113.53"
TEST_DOMAIN="example.com"
FRR_ASN="65001"
UPSTREAM_NEIGHBOR="198.51.100.1"
LOG_FILE="/var/log/unbound/healthcheck.log"
STATE_FILE="/var/run/dns-anycast-state"
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}
check_dns() {
# dig ile sorgu yap, 2 saniye timeout
result=$(dig @${ANYCAST_IP} ${TEST_DOMAIN} A +time=2 +tries=1 2>&1)
if echo "$result" | grep -q "NOERROR|NXDOMAIN"; then
return 0
fi
return 1
}
withdraw_prefix() {
log "WARN: DNS sağlıksız, prefix geri çekiliyor"
vtysh -c "configure terminal"
-c "router bgp ${FRR_ASN}"
-c "address-family ipv4 unicast"
-c "no network 203.0.113.0/24"
-c "exit-address-family"
-c "exit"
-c "write memory" 2>/dev/null
echo "withdrawn" > "$STATE_FILE"
}
announce_prefix() {
log "INFO: DNS sağlıklı, prefix duyuruluyor"
vtysh -c "configure terminal"
-c "router bgp ${FRR_ASN}"
-c "address-family ipv4 unicast"
-c "network 203.0.113.0/24"
-c "exit-address-family"
-c "exit"
-c "write memory" 2>/dev/null
echo "announced" > "$STATE_FILE"
}
# Mevcut durumu oku
current_state=$(cat "$STATE_FILE" 2>/dev/null || echo "unknown")
if check_dns; then
if [ "$current_state" != "announced" ]; then
announce_prefix
fi
else
if [ "$current_state" != "withdrawn" ]; then
withdraw_prefix
fi
fi
SCRIPT
chmod +x /usr/local/bin/dns-healthcheck.sh
# Cron ile her 30 saniyede bir çalıştır
cat > /etc/cron.d/dns-healthcheck << 'EOF'
* * * * * root /usr/local/bin/dns-healthcheck.sh
* * * * * root sleep 30 && /usr/local/bin/dns-healthcheck.sh
EOF
Lokasyon Bazlı Yapılandırma Farklılıkları
Anycast kurulumunda her lokasyonun yapılandırması neredeyse aynı olacak, ancak bazı ince farklılıklar olabilir. Bunu yönetmek için bir yapılandırma şablonu sistemi kullanmak mantıklı:
# Lokasyon tanımlayıcısı - her sunucuda farklı
cat > /etc/unbound/conf.d/local-identity.conf << 'EOF'
server:
# Bu sunucunun coğrafi kimliği (CHAOS sorguları için)
# identity: "istanbul-dc1"
# Lokasyona özel forwarder (isteğe bağlı)
# forward-zone:
# name: "internal.company.com"
# forward-addr: 10.1.0.53
# Lokasyona özel erişim kontrolü
access-control: 10.100.0.0/16 allow # Istanbul iç ağ
EOF
Ankara lokasyonu için aynı dosyada sadece subnet adresi değişecek:
# Ankara DC'ye özel yapılandırma
cat > /etc/unbound/conf.d/local-identity.conf << 'EOF'
server:
access-control: 10.200.0.0/16 allow # Ankara iç ağ
EOF
Performans Testi ve Doğrulama
Kurulum tamamlandıktan sonra doğrulama adımları kritik önem taşıyor.
# Anycast IP'ye DNS sorgusu at
dig @203.0.113.53 google.com A +stats
# Hangi sunucudan cevap geldiğini anlayabilmek için traceroute
traceroute 203.0.113.53
# DNSSEC doğrulamasını test et
dig @203.0.113.53 dnssec-failed.org A
# Cache durumunu sorgula
unbound-control stats_noreset | grep -E "total.|cache."
# Bağlantı sayısı ve sorgu istatistikleri
unbound-control stats | grep "total.queries"
# Sorgu gecikmesini ölç - 1000 sorgu gönder
for i in $(seq 1 1000); do
dig @203.0.113.53 example.com A +time=1 +tries=1 > /dev/null 2>&1
done
# unbound-control ile detaylı istatistik
unbound-control stats_noreset
Gerçek Dünya Senaryosu: E-Ticaret Platformu Örneği
Yakın zamanda üzerinde çalıştığım bir e-ticaret platformunda bu yapıyı hayata geçirdik. Platformun yaklaşık 2 milyon günlük aktif kullanıcısı vardı ve DNS çözümleme süreleri performans metrikleri içinde ciddi bir yer tutuyordu.
İstanbul ve İzmir’de birer, Ankara’da iki node olmak üzere toplam dört Unbound sunucusu anycast üzerinden çalışmaya başladı. Anycast öncesinde ortalama DNS çözümleme süresi 45ms civarındaydı. Anycast sonrasında bu 8ms’ye düştü. Nedenini anlamak kolay: İstanbul kullanıcıları artık her seferinde Ankara’daki tek DNS sunucusuna gitmek yerine yanı başlarındaki lokasyona bağlanıyor.
Bir ölçüm yapmak isteyenler için basit bir script:
#!/bin/bash
# DNS gecikme ölçüm aracı
DOMAINS=("google.com" "youtube.com" "twitter.com" "github.com")
DNS_SERVER="203.0.113.53"
ITERATIONS=10
for domain in "${DOMAINS[@]}"; do
total=0
for i in $(seq 1 $ITERATIONS); do
latency=$(dig @${DNS_SERVER} ${domain} A +stats 2>&1 | grep "Query time" | awk '{print $4}')
total=$((total + latency))
done
avg=$((total / ITERATIONS))
echo "Domain: ${domain} | Ortalama gecikme: ${avg}ms"
done
Dikkat Edilmesi Gereken Noktalar
BGP convergence süresi: Bir node’un prefix’ini çekip başka bir node’un devralması anlık değil. BGP convergence süresi yapılandırmanıza bağlı olarak 30 saniye ile birkaç dakika arasında sürebilir. Bu süre zarfında o lokasyona gelen DNS sorguları başarısız olabilir. Bu yüzden DNS client’larda retry mekanizması mutlaka aktif olmalı.
Asimetrik ağ: Anycast ile gelen sorgu bir lokasyona gidebilir, ama bazı nadir durumlarda cevap farklı bir yoldan geçebilir. UDP tabanlı DNS için bu genellikle sorun yaratmaz ama TCP DNS (büyük cevaplar, DoT) için dikkat edin.
Cache tutarsızlığı: Her lokasyonun kendi cache’i var. Bu istemciler arasında farklı TTL davranışları görebilirsiniz. Production ortamında bunu kabul etmek gerekiyor; anycast ile tutarlı cache mümkün değil.
Monitoring: Her node’u ayrı ayrı izlemeniz şart. Prometheus + Unbound exporter kombinasyonu bu iş için birebir:
# Unbound Prometheus exporter kurulumu
wget https://github.com/letsencrypt/unbound_exporter/releases/latest/download/unbound_exporter-linux-amd64
chmod +x unbound_exporter-linux-amd64
mv unbound_exporter-linux-amd64 /usr/local/bin/unbound_exporter
# unbound-control socket erişimi için
cat > /etc/systemd/system/unbound-exporter.service << 'EOF'
[Unit]
Description=Unbound Prometheus Exporter
After=unbound.service
[Service]
ExecStart=/usr/local/bin/unbound_exporter
-unbound.host unix:///var/run/unbound/unbound.ctl
-web.listen-address=":9167"
Restart=always
User=unbound
[Install]
WantedBy=multi-user.target
EOF
systemctl enable --now unbound-exporter
Rate limiting: Anycast yapısında bir node’un önüne gelen trafik ani olarak artabilir. Unbound’un kendi rate limiting özelliğini aktif etmek iyi bir uygulama:
# /etc/unbound/conf.d/ratelimit.conf
cat > /etc/unbound/conf.d/ratelimit.conf << 'EOF'
server:
# Saniyede 1000 sorgu limiti
ratelimit: 1000
ratelimit-size: 4m
ratelimit-factor: 10
ratelimit-for-domain: example.com 100
EOF
systemctl reload unbound
Sonuç
Unbound ile Anycast DNS yapısı kurmak görünürde karmaşık ama adım adım ilerlediğinizde son derece anlaşılır bir mimari ortaya çıkıyor. Kritik nokta şu: Ağ katmanı (anycast/BGP) ve uygulama katmanı (Unbound) birbirinden bağımsız ama birbirini tamamlayan şekilde çalışıyor. BGP prefix yönetimini Unbound’un sağlık durumuna bağladığınız anda sistem kendini yönetebilir hale geliyor.
Bu yapının size katacağı en büyük değer, coğrafi dağıtıklık ile yüksek erişilebilirliği aynı anda elde etmeniz. Tek bir DNS sunucusuna bağlı kalmak, özellikle Türkiye’nin farklı şehirlerindeki kullanıcıları düşündüğünüzde ciddi gecikme farklılıkları yaratıyor. Anycast bu farkı minimize ediyor.
Başlarken iki node ile küçük tutun, monitoring altyapısını çok erkenden kurun ve BGP convergence davranışını test ortamında iyice anlayın. Production’a geçmeden önce bir node’u kapatıp trafiğin ne kadar sürede diğerine geçtiğini bizzat gözlemleyin. O test size sistemin davranışı hakkında hiçbir dökümanın veremeyeceği kadar değerli bilgi verecektir.
