Ağ Altyapısı Çöküşünde Felaket Kurtarma: BGP ve DNS Failover Senaryoları

Sabah 03:47’de telefonun çalması, hiçbir sysadmin’in duymak istemediği şeydir. “Sitemize ulaşamıyoruz, tüm kullanıcılar etkileniyor” mesajı geldiğinde, eğer önceden hazırlanmadıysanız o gece çok uzun geçecek demektir. Ağ altyapısı çöküşleri, disk arızaları veya uygulama hatalarından çok daha sinsi bir yapıya sahiptir çünkü etki alanı geniş, hata noktaları dağınık ve kurtarma süreci genellikle birden fazla katmanı kapsar. Bu yazıda BGP failover ve DNS failover senaryolarını gerçek dünya pratikleriyle ele alacağız.

Ağ Felaketinin Anatomisi

Bir ağ altyapısı çöküşü tek bir noktadan kaynaklanmıyor gibi görünse de genellikle birkaç temel senaryoya indirgenir:

  • Upstream ISP arızası: Transit sağlayıcınızın hatası, sizin tüm internet bağlantınızı koparır
  • BGP oturumu düşmesi: Router’larınız arasındaki BGP session’ları kapanır, route’lar çekilir
  • DNS altyapısı çöküşü: Authoritative DNS sunucularınız yanıt vermez, kullanıcılar sitenize ulaşamaz
  • DDoS sonrası blackhole: Upstream sağlayıcı sizi blackhole’a alır, siz de erişilemez olursunuz
  • Veri merkezi fiziksel arıza: Güç, soğutma veya fiber kesintisi tüm aktif ekipmanı etkiler

Bu senaryoların her biri farklı kurtarma mekanizması gerektirir. BGP failover genellikle routing katmanını, DNS failover ise uygulama erişilebilirliği katmanını korur.

BGP Failover: Temel Kavramlar ve Hazırlık

BGP failover’ı anlamak için önce neden birden fazla upstream bağlantısı gerektiğini netleştirmek lazım. Multihoming, yani birden fazla ISP’ye bağlı olmak, tek noktadan bağımlılığı ortadan kaldırır. Ama sadece iki kablo takmak yetmez; BGP politikalarını doğru yapılandırmak gerekir.

AS Path ve Local Preference Yapılandırması

Gerçek bir senaryoda şunu düşünelim: İki farklı ISP’niz var. ISP-A birincil, ISP-B yedek. Normal koşullarda tüm trafiği ISP-A üzerinden yönlendiriyorsunuz, ISP-A düştüğünde otomatik olarak ISP-B devreye girmeli.

# Cisco IOS benzeri yapılandırma (FRRouting/Quagga için de geçerli)
# /etc/frr/bgpd.conf

router bgp 65001
 bgp router-id 203.0.113.1
 
 neighbor 198.51.100.1 remote-as 65100
 neighbor 198.51.100.1 description ISP-A-PRIMARY
 neighbor 198.51.100.1 soft-reconfiguration inbound
 
 neighbor 203.0.114.1 remote-as 65200
 neighbor 203.0.114.1 description ISP-B-BACKUP
 neighbor 203.0.114.1 soft-reconfiguration inbound
 
 address-family ipv4 unicast
  network 192.0.2.0/24
  
  neighbor 198.51.100.1 route-map ISP-A-IN in
  neighbor 198.51.100.1 route-map ISP-A-OUT out
  
  neighbor 203.0.114.1 route-map ISP-B-IN in
  neighbor 203.0.114.1 route-map ISP-B-OUT out
 exit-address-family

route-map ISP-A-IN permit 10
 set local-preference 200

route-map ISP-B-IN permit 10
 set local-preference 100

route-map ISP-A-OUT permit 10
route-map ISP-B-OUT permit 10

Bu yapılandırmada ISP-A üzerinden gelen route’lara local-preference 200, ISP-B’den gelenlere 100 veriyoruz. BGP her zaman daha yüksek local-preference değerini tercih eder, dolayısıyla ISP-A aktif olduğu sürece trafik oradan akar.

BGP Session Monitoring Script’i

BGP oturumlarını izlemek ve düşme anında otomatik aksiyon almak için bir monitoring script’i şart:

#!/bin/bash
# /usr/local/bin/bgp_monitor.sh

BGP_NEIGHBORS=("198.51.100.1" "203.0.114.1")
ALERT_EMAIL="[email protected]"
LOG_FILE="/var/log/bgp_monitor.log"
SLACK_WEBHOOK="https://hooks.slack.com/services/XXXXX"

check_bgp_session() {
    local neighbor=$1
    local status=$(vtysh -c "show bgp neighbor $neighbor" 2>/dev/null | grep "BGP state" | awk '{print $4}' | tr -d ',')
    echo $status
}

send_alert() {
    local message=$1
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $message" >> $LOG_FILE
    
    # Slack bildirimi
    curl -s -X POST $SLACK_WEBHOOK 
        -H 'Content-type: application/json' 
        -d "{"text":"🚨 BGP ALARM: $message"}" > /dev/null
    
    # Email bildirimi
    echo "$message" | mail -s "BGP Session Alert" $ALERT_EMAIL
}

for neighbor in "${BGP_NEIGHBORS[@]}"; do
    status=$(check_bgp_session $neighbor)
    
    if [ "$status" != "Established" ]; then
        send_alert "BGP session DOWN - Neighbor: $neighbor, Status: $status"
        
        # Failover script'ini tetikle
        /usr/local/bin/bgp_failover.sh $neighbor
    fi
done

Otomatik Failover Script’i

#!/bin/bash
# /usr/local/bin/bgp_failover.sh

FAILED_NEIGHBOR=$1
PRIMARY_ISP="198.51.100.1"
BACKUP_ISP="203.0.114.1"
LOG_FILE="/var/log/bgp_failover.log"

log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a $LOG_FILE
}

if [ "$FAILED_NEIGHBOR" == "$PRIMARY_ISP" ]; then
    log "Primary ISP DOWN. Activating backup route via $BACKUP_ISP"
    
    # Backup ISP local-preference'ını yükselt
    vtysh << EOF
configure terminal
route-map ISP-B-IN permit 10
 set local-preference 300
end
clear ip bgp $BACKUP_ISP soft in
write memory
EOF
    
    log "Failover completed. Traffic shifted to backup ISP."
    
    # Recovery script'i background'da başlat
    /usr/local/bin/bgp_recovery_monitor.sh &
    
elif [ "$FAILED_NEIGHBOR" == "$BACKUP_ISP" ]; then
    log "Backup ISP DOWN. Primary still active, no action needed."
    log "Monitoring primary ISP health..."
fi

DNS Failover: Stratejiler ve Uygulamalar

BGP failover routing katmanını korurken, DNS failover uygulama erişilebilirliğini sağlar. İki farklı yaklaşım var: passive DNS failover ve active health-check based DNS failover.

GeoDNS ve Anycast Temelli Yaklaşım

Büyük ölçekli yapılarda Anycast DNS kullanmak, tek nokta arızasını neredeyse imkansız kılar. Bind9 ile temel bir Anycast kurulumu:

# /etc/bind/named.conf.options

options {
    directory "/var/cache/bind";
    
    # Anycast IP adresimiz
    listen-on { 192.0.2.53; 127.0.0.1; };
    listen-on-v6 { 2001:db8::53; ::1; };
    
    # Recursive query'leri kapat (authoritative için)
    recursion no;
    
    # DNSSEC
    dnssec-validation auto;
    
    # Rate limiting - DDoS koruması
    rate-limit {
        responses-per-second 10;
        window 5;
        log-only no;
    };
    
    # Transfer izinleri
    allow-transfer { 
        198.51.100.0/24;  # Secondary nameserver subnet
    };
    
    # Minimal responses
    minimal-responses yes;
};

# Health check zone
zone "healthcheck.example.com" {
    type master;
    file "/etc/bind/zones/healthcheck.example.com.zone";
    allow-query { any; };
};

Aktif Sağlık Kontrolü ile DNS Failover

PowerDNS ile health-check tabanlı failover için bir Python script’i:

#!/usr/bin/env python3
# /usr/local/bin/dns_failover_manager.py

import requests
import subprocess
import time
import logging
from datetime import datetime

logging.basicConfig(
    filename='/var/log/dns_failover.log',
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

POWERDNS_API = "http://localhost:8081/api/v1"
POWERDNS_KEY = "gizli-api-anahtari"
ZONE = "example.com."

ENDPOINTS = {
    "primary": {
        "ip": "203.0.113.10",
        "health_url": "http://203.0.113.10/health",
        "weight": 100
    },
    "secondary": {
        "ip": "198.51.100.20", 
        "health_url": "http://198.51.100.20/health",
        "weight": 0
    }
}

def check_endpoint_health(url, timeout=5):
    try:
        response = requests.get(url, timeout=timeout)
        return response.status_code == 200
    except Exception as e:
        logging.warning(f"Health check failed for {url}: {e}")
        return False

def update_dns_record(name, ip_address, ttl=30):
    headers = {
        "X-API-Key": POWERDNS_KEY,
        "Content-Type": "application/json"
    }
    
    data = {
        "rrsets": [{
            "name": f"{name}.{ZONE}",
            "type": "A",
            "ttl": ttl,
            "changetype": "REPLACE",
            "records": [{"content": ip_address, "disabled": False}]
        }]
    }
    
    response = requests.patch(
        f"{POWERDNS_API}/servers/localhost/zones/{ZONE}",
        json=data,
        headers=headers
    )
    
    if response.status_code == 204:
        logging.info(f"DNS record updated: {name} -> {ip_address}")
        return True
    else:
        logging.error(f"DNS update failed: {response.text}")
        return False

def main():
    current_active = "primary"
    consecutive_failures = 0
    FAILURE_THRESHOLD = 3
    
    logging.info("DNS Failover Manager started")
    
    while True:
        primary_healthy = check_endpoint_health(ENDPOINTS["primary"]["health_url"])
        secondary_healthy = check_endpoint_health(ENDPOINTS["secondary"]["health_url"])
        
        if not primary_healthy:
            consecutive_failures += 1
            logging.warning(f"Primary endpoint unhealthy. Consecutive failures: {consecutive_failures}")
            
            if consecutive_failures >= FAILURE_THRESHOLD and current_active == "primary":
                if secondary_healthy:
                    logging.critical("Initiating DNS FAILOVER to secondary!")
                    success = update_dns_record("www", ENDPOINTS["secondary"]["ip"])
                    if success:
                        current_active = "secondary"
                        consecutive_failures = 0
                else:
                    logging.critical("BOTH endpoints unhealthy! No failover possible!")
        else:
            if current_active == "secondary" and consecutive_failures == 0:
                logging.info("Primary recovered. Failing back...")
                update_dns_record("www", ENDPOINTS["primary"]["ip"])
                current_active = "primary"
            consecutive_failures = 0
        
        time.sleep(10)

if __name__ == "__main__":
    main()

Düşük TTL Stratejisi ve Tuzakları

DNS failover’ın çalışması için TTL değerlerini felaket anından önce düşürmeniz gerekir. Ama burada önemli bir noktaya dikkat etmek lazım: TTL’yi düşürmek DNS sunucularınıza gelen sorgu yükünü artırır.

# TTL'yi kademeli olarak düşürmek için bir script
# Felaket beklenen durumlarda (planlı bakım vs.) kullanılır

#!/bin/bash
# /usr/local/bin/lower_ttl.sh

ZONE="example.com"
PDNS_API="http://localhost:8081/api/v1"
PDNS_KEY="gizli-api-anahtari"

# Mevcut TTL'yi kaydet
CURRENT_TTL=$(dig +short SOA $ZONE | awk '{print $7}')
echo "Current TTL: $CURRENT_TTL" >> /var/log/ttl_changes.log
echo "$(date) - Original TTL: $CURRENT_TTL" >> /var/log/ttl_changes.log

# TTL'yi kademeli olarak düşür: 3600 -> 300 -> 60
for ttl in 300 60; do
    echo "Setting TTL to $ttl seconds..."
    
    curl -s -X PATCH "$PDNS_API/servers/localhost/zones/$ZONE." 
        -H "X-API-Key: $PDNS_KEY" 
        -H "Content-Type: application/json" 
        -d "{
            "rrsets": [{
                "name": "www.$ZONE.",
                "type": "A",
                "ttl": $ttl,
                "changetype": "REPLACE",
                "records": [{"content": "203.0.113.10", "disabled": false}]
            }]
        }"
    
    echo "TTL set to $ttl, waiting for propagation..."
    sleep $((ttl * 2))
done

echo "TTL lowering complete. Current effective TTL: 60 seconds"

Gerçek Dünya Senaryosu: Çift ISP Arızası

Geçmişte yaşanan bir senaryoyu aktarayım. Bir e-ticaret firmasında çalışıyorduk, Black Friday öncesi gece ISP-A fiber kesintisi yaşadı. BGP failover otomatik devreye girdi, ISP-B trafiği devraldı. Güzel. Ama kimsenin hesaba katmadığı şey şuydu: ISP-B’nin bant genişliği limiti ISP-A’nın yarısıydı ve Black Friday trafiği bu limiti aştı. Sonuç: kısmi erişim, yüksek gecikme, sepet abandon oranında patlama.

Bu tür senaryolar için kapasite planlaması şarttır. BGP failover testini gerçekçi trafik yüküyle yapmak gerekir.

Kapasite Testi ve Failover Doğrulama

#!/bin/bash
# /usr/local/bin/failover_test.sh
# Planlı failover testi - üretim saatlerinde ÇALIŞTIRILMAZ

TEST_LOG="/var/log/failover_test_$(date +%Y%m%d_%H%M%S).log"
PRIMARY_GATEWAY="198.51.100.1"
BACKUP_GATEWAY="203.0.114.1"

log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a $TEST_LOG
}

# Pre-test kontrolleri
log "=== FAILOVER TEST BAŞLIYOR ==="
log "Primary BGP durumu:"
vtysh -c "show bgp summary" | grep $PRIMARY_GATEWAY | tee -a $TEST_LOG

log "Mevcut routing tablosu (ilk 20 satır):"
vtysh -c "show ip route" | head -20 | tee -a $TEST_LOG

# Trafik istatistiklerini kaydet
log "Mevcut trafik istatistikleri:"
ifstat -i eth0 1 5 | tee -a $TEST_LOG

# Simüle edilmiş failover
log "Primary ISP simulate ediliyor - BGP oturumu kapatılıyor..."
vtysh -c "configure terminal" -c "router bgp 65001" -c "neighbor $PRIMARY_GATEWAY shutdown"

sleep 5

log "Failover sonrası routing durumu:"
vtysh -c "show ip route" | head -20 | tee -a $TEST_LOG

log "Backup ISP BGP durumu:"
vtysh -c "show bgp summary" | grep $BACKUP_GATEWAY | tee -a $TEST_LOG

# Erişilebilirlik testi
log "Dış erişilebilirlik testi:"
for target in "8.8.8.8" "1.1.1.1" "208.67.222.222"; do
    result=$(ping -c 3 -W 2 $target 2>&1 | tail -1)
    log "Ping $target: $result"
done

# Recovery
log "Primary ISP geri açılıyor..."
vtysh -c "configure terminal" -c "router bgp 65001" -c "no neighbor $PRIMARY_GATEWAY shutdown"

sleep 10

log "=== TEST TAMAMLANDI ==="
log "Test logu: $TEST_LOG"

DNS Önbellek Zehirlenmesi Sonrası Kurtarma

Felaket senaryoları arasında DNS önbellek zehirlenmesi de var. Bu durumda sadece failover değil, aktif temizleme gerekir.

#!/bin/bash
# /usr/local/bin/dns_cache_flush.sh

log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a /var/log/dns_emergency.log
}

log "DNS önbellek temizleme başlatıldı"

# BIND9 için
if systemctl is-active --quiet bind9; then
    log "BIND9 önbelleği temizleniyor..."
    rndc flush
    rndc reload
    log "BIND9 flush tamamlandı"
fi

# PowerDNS Recursor için
if systemctl is-active --quiet pdns-recursor; then
    log "PowerDNS Recursor önbelleği temizleniyor..."
    rec_control wipe-cache .
    log "PowerDNS Recursor flush tamamlandı"
fi

# Unbound için
if systemctl is-active --quiet unbound; then
    log "Unbound önbelleği temizleniyor..."
    unbound-control flush_zone .
    unbound-control flush_bogus
    log "Unbound flush tamamlandı"
fi

# Sistem DNS önbelleği (systemd-resolved)
if systemctl is-active --quiet systemd-resolved; then
    log "systemd-resolved önbelleği temizleniyor..."
    systemd-resolve --flush-caches
    log "systemd-resolved flush tamamlandı"
fi

log "Tüm DNS önbellekleri temizlendi"

# Doğrulama
log "DNS doğrulama:"
dig +short example.com A | tee -a /var/log/dns_emergency.log
dig +short example.com A @8.8.8.8 | tee -a /var/log/dns_emergency.log

Felaket Kurtarma Planı: Runbook Şablonu

Tüm bu teknik önlemlerin yanında bir runbook olmadan hiçbir şey işe yaramaz. Gece 03:47’de panik içinde nereye bakacağınızı bilmiyorsanız, en iyi script’ler bile işe yaramaz.

Runbook’unuzda mutlaka şunlar bulunmalı:

  • Triage süreci: Sorunun tam olarak ne olduğunu anlamak için ilk 5 dakikada yapılacaklar
  • Eskalasyon matrisi: Kim ne zaman aranır, yetki sınırları nelerdir
  • Rollback prosedürleri: Her değişikliği geri almak için adım adım talimatlar
  • İletişim şablonları: Müşterilere, yönetime ve teknik ekibe ne söyleyeceğiniz
  • Test edilmiş kontaklar: ISP NOC numaraları, upstream sağlayıcı acil hatları, domain registrar destek

Runbook’un PDF versiyonunu yazıcıdan çıkarıp fiziksel olarak saklayın. Ağ çöktüğünde online wiki’ye ulaşamazsınız.

Test ve Simülasyon Takvimi

Felaket kurtarma planı test edilmemiş bir plandır. Yılda en az iki kez planlı failover testi yapın:

  • Aylık: BGP monitoring script çıktılarını gözden geçirme, DNS TTL değerlerini doğrulama
  • Üç ayda bir: Düşük trafikli gece yarısı saatinde kısmi failover testi, backup ISP üzerinden trafik akışını doğrulama
  • Altı ayda bir: Tam failover simülasyonu, RTO ve RPO değerlerini ölçme, ekip tatbikatı
  • Yıllık: Tam DR senaryosu, yeni personelin katılımı, runbook güncellemesi

Her testin ardından bir post-mortem yazın. Sadece ne yanlış gitti değil, ne doğru çalıştı da belgelenmeli. Bu belgeler ileride ekibe katılacak mühendisler için paha biçilmez olacak.

Sonuç

BGP ve DNS failover, ağ altyapısı felaket kurtarmanın iki temel direğidir. BGP failover routing katmanında çalışarak trafik akışını yönlendirirken, DNS failover uygulama katmanında kullanıcıların doğru sunuculara ulaşmasını sağlar. İkisi birbirini tamamlar; sadece birini uygulamak yeterli değildir.

Gerçek bir felaket anında soğukkanlı kalabilmek için önceden hazırlık şarttır. Script’lerinizi yazın, test edin, runbook’unuzu hazırlayın ve en önemlisi düzenli olarak tatbikat yapın. O gece 03:47 geldiğinde, hazır olduğunuz için şükredeceksiniz.

Sorularınız veya kendi senaryolarınız varsa yorumlarda paylaşabilirsiniz. Özellikle çok veri merkezli BGP anycast yapılandırmaları veya cloud-hybrid DNS failover senaryoları hakkında ayrı yazılar da planlıyorum.

Bir yanıt yazın

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