Felaket Kurtarma Tatbikatı: Test Planı ve Değerlendirme Rehberi

Yıllarca “yedeğimiz var, sorun olmaz” diyerek rahat uyuyan sistem yöneticilerinin sabahlarını mahveden şey genellikle aynıdır: gerçek bir felaket anında yedeğin çalışmadığını fark etmek. Ya da yedeğin çalıştığını ama geri yükleme sürecinin 4 saat sürdüğünü, oysa SLA’nın 2 saat olduğunu görmek. İşte bu yüzden felaket kurtarma tatbikatları, sadece bir kutu işaretleme egzersizi değil, sistemlerinizin gerçek sınavıdır.

Bu yazıda sıfırdan bir DR (Disaster Recovery) tatbikatı nasıl planlanır, nasıl yürütülür ve en önemlisi nasıl değerlendirilir, bunları gerçek dünya senaryolarıyla ele alacağız.

Tatbikat Öncesi: Temelleri Doğru Atmak

RTO ve RPO Değerlerini Netleştirin

Tatbikata başlamadan önce ekibinizle masaya oturmanız gereken iki kritik metrik var. RTO (Recovery Time Objective) ve RPO (Recovery Point Objective). Bu değerleri bilmeden tatbikat yapmak, hedefe kurşun atmadan önce gözleri kapatmak gibidir.

  • RTO: Sistemin felaketten kaç saat/dakika içinde ayağa kalkması gerekiyor?
  • RPO: Maksimum ne kadar veri kaybı kabul edilebilir? Son 1 saat mi, 4 saat mi, 24 saat mi?

Bu değerleri belirledikten sonra tatbikat senaryonuzu bu hedeflere göre şekillendirirsiniz. Bir e-ticaret sitesi için RTO 2 saat, RPO 15 dakika olabilirken; iç kullanım için bir ERP sistemi için RTO 8 saat, RPO 4 saat makul kabul edilebilir.

Tatbikat Kapsamını Belirleyin

Her tatbikat her şeyi test etmek zorunda değil. Üç farklı tatbikat türü var:

  • Masa başı tatbikatı (Tabletop Exercise): Gerçek sistemlere dokunmadan senaryo üzerinden konuşursunuz. Hızlı, düşük riskli, ama gerçekçi değil.
  • Kısmi tatbikat (Partial Failover): Sadece belirli bileşenleri test edersiniz. Örneğin sadece veritabanı failover’ı.
  • Tam tatbikat (Full Failover Test): Production’ı gerçekten DR ortamına taşırsınız. Riskli ama en değerlisi.

Bu yazıda ağırlıklı olarak kısmi ve tam tatbikat senaryolarına odaklanacağız.

Tatbikat Ortamını Hazırlamak

Kontrol Listesi Oluşturma

Tatbikat başlamadan önce sistemin mevcut durumunu kayıt altına alın. Bunu otomatikleştiren bir script işinizi çok kolaylaştırır:

#!/bin/bash
# pre_dr_snapshot.sh - Tatbikat öncesi sistem durumu kaydı

REPORT_FILE="/var/log/dr_test/pre_test_$(date +%Y%m%d_%H%M%S).log"
mkdir -p /var/log/dr_test

echo "=== DR Tatbikat Öncesi Durum Raporu ===" > $REPORT_FILE
echo "Tarih: $(date)" >> $REPORT_FILE
echo "" >> $REPORT_FILE

echo "--- Servis Durumları ---" >> $REPORT_FILE
for service in nginx mysql postgresql redis; do
    STATUS=$(systemctl is-active $service 2>/dev/null || echo "bulunamadi")
    echo "$service: $STATUS" >> $REPORT_FILE
done

echo "" >> $REPORT_FILE
echo "--- Disk Kullanımı ---" >> $REPORT_FILE
df -h >> $REPORT_FILE

echo "" >> $REPORT_FILE
echo "--- Aktif Bağlantılar ---" >> $REPORT_FILE
ss -tuln >> $REPORT_FILE

echo "" >> $REPORT_FILE
echo "--- Son Yedek Bilgisi ---" >> $REPORT_FILE
ls -lth /backup/ | head -10 >> $REPORT_FILE

echo "Rapor oluşturuldu: $REPORT_FILE"

Yedek Bütünlüğünü Doğrulama

Tatbikata geçmeden önce yedeklerinizin gerçekten sağlam olduğunu doğrulayın. Bozuk bir yedekle tatbikat yapmanın anlamı yok:

#!/bin/bash
# verify_backups.sh - Yedek dosyalarının bütünlük kontrolü

BACKUP_DIR="/backup/daily"
LOG_FILE="/var/log/dr_test/backup_verify.log"

echo "Yedek Doğrulama Başladı: $(date)" | tee -a $LOG_FILE

for backup_file in $BACKUP_DIR/*.tar.gz; do
    echo -n "Kontrol ediliyor: $backup_file ... " | tee -a $LOG_FILE
    
    if tar -tzf "$backup_file" > /dev/null 2>&1; then
        SIZE=$(du -sh "$backup_file" | cut -f1)
        echo "TAMAM ($SIZE)" | tee -a $LOG_FILE
    else
        echo "BOZUK - KRITIK HATA!" | tee -a $LOG_FILE
        # Uyarı maili gönder
        echo "Bozuk yedek: $backup_file" | mail -s "DR Test: Bozuk Yedek Tespit Edildi" [email protected]
    fi
done

# PostgreSQL dump doğrulama
for dump_file in $BACKUP_DIR/*.sql.gz; do
    echo -n "SQL dump kontrol: $dump_file ... " | tee -a $LOG_FILE
    if gzip -t "$dump_file" 2>/dev/null; then
        echo "TAMAM" | tee -a $LOG_FILE
    else
        echo "BOZUK!" | tee -a $LOG_FILE
    fi
done

echo "Doğrulama tamamlandı: $(date)" | tee -a $LOG_FILE

Senaryo 1: Veritabanı Sunucusu Çöktü

Bu en klasik ve en sık test edilen senaryodur. Birincil PostgreSQL sunucunuz çöktü, DR sunucusuna failover yapmanız gerekiyor.

Tatbikat Adımları

Önce mevcut replikasyon durumunu kontrol edin:

#!/bin/bash
# check_replication_status.sh - Replikasyon gecikmesini ölç

MASTER_HOST="db-primary"
REPLICA_HOST="db-replica"

echo "=== Replikasyon Durum Kontrolü ==="
echo "Zaman: $(date)"

# Primary'de çalıştır
MASTER_LSN=$(psql -h $MASTER_HOST -U postgres -t -c "SELECT pg_current_wal_lsn();" 2>/dev/null | tr -d ' ')

# Replica'da çalıştır
REPLICA_LSN=$(psql -h $REPLICA_HOST -U postgres -t -c "SELECT pg_last_wal_receive_lsn();" 2>/dev/null | tr -d ' ')

echo "Primary LSN: $MASTER_LSN"
echo "Replica LSN: $REPLICA_LSN"

# Gecikmeyi hesapla (byte cinsinden)
LAG=$(psql -h $REPLICA_HOST -U postgres -t -c "
SELECT EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))::INT as lag_seconds;
" 2>/dev/null | tr -d ' ')

echo "Replikasyon Gecikmesi: ${LAG} saniye"

if [ "$LAG" -gt 60 ]; then
    echo "UYARI: Replikasyon gecikmesi 60 saniyenin üzerinde!"
fi

Failover tetiklendiğinde replica’yı primary’ye promote edin:

#!/bin/bash
# failover_postgresql.sh - PostgreSQL manuel failover

REPLICA_HOST="db-replica"
START_TIME=$(date +%s)

echo "=== PostgreSQL Failover Başladı ==="
echo "Zaman: $(date)"

# Replica üzerinde standby modundan çıkar
ssh $REPLICA_HOST "pg_ctl promote -D /var/lib/postgresql/14/main"

# Primary olarak ayağa kalkmasını bekle
echo "Primary'ye geçiş bekleniyor..."
for i in {1..30}; do
    if psql -h $REPLICA_HOST -U postgres -c "SELECT 'primary' WHERE NOT pg_is_in_recovery();" 2>/dev/null | grep -q primary; then
        END_TIME=$(date +%s)
        ELAPSED=$((END_TIME - START_TIME))
        echo "Failover BAŞARILI! Geçen süre: ${ELAPSED} saniye"
        break
    fi
    echo "Bekleniyor... ($i/30)"
    sleep 5
done

# Uygulama connection string'ini güncelle
sed -i "s/DB_HOST=db-primary/DB_HOST=db-replica/" /etc/app/config.env
systemctl restart app-service

echo "Uygulama yeni veritabanına yönlendirildi."

Süre Ölçümü ve Kayıt

Tatbikat boyunca her adımın ne kadar sürdüğünü kayıt altına alın. Bu veriler değerlendirme aşamasında kritik öneme sahip:

#!/bin/bash
# dr_timer.sh - Tatbikat süre kaydı

LOG_FILE="/var/log/dr_test/timeline_$(date +%Y%m%d).log"

log_event() {
    local EVENT=$1
    local TIMESTAMP=$(date +%H:%M:%S)
    echo "[$TIMESTAMP] $EVENT" | tee -a $LOG_FILE
}

# Kullanım örnekleri:
# log_event "Felaket tespit edildi"
# log_event "Failover kararı alındı"
# log_event "Veritabanı promote edildi"
# log_event "Uygulama servisleri yeniden başlatıldı"
# log_event "Sistem tamamıyla ayakta"

log_event "DR Tatbikatı başladı"

Senaryo 2: Tam Veri Merkezi Kaybı

Bu daha kapsamlı ve stresli bir senaryo. Birincil veri merkeziniz tamamen erişilemez durumda, DR lokasyonunu devreye almanız gerekiyor.

Ağ Yapılandırmasını Devreye Alma

#!/bin/bash
# activate_dr_site.sh - DR lokasyonu aktivasyonu

DR_SITE_IP="192.168.100.0/24"
HAPROXY_CONFIG="/etc/haproxy/haproxy.cfg"
DNS_ZONE_FILE="/etc/bind/zones/sirket.com.zone"

echo "=== DR Lokasyon Aktivasyonu ==="
date

# HAProxy backend'ini DR'a çevir
echo "HAProxy yapılandırması güncelleniyor..."
cp $HAPROXY_CONFIG "${HAPROXY_CONFIG}.backup_$(date +%Y%m%d)"

sed -i 's/server primary 10.0.1.100:80 check/server primary 10.0.1.100:80 check backup/' $HAPROXY_CONFIG
sed -i 's/server dr_site 192.168.100.10:80 check backup/server dr_site 192.168.100.10:80 check/' $HAPROXY_CONFIG

# HAProxy'yi yeniden yükle (kesintisiz)
haproxy -f $HAPROXY_CONFIG -c && systemctl reload haproxy
echo "HAProxy güncellendi: $(systemctl is-active haproxy)"

# DNS TTL zaten düşük tutulmuş olmalı (300 saniye)
echo "DNS değişikliği için zone seri numarasını güncelle"
CURRENT_SERIAL=$(grep "Serial" $DNS_ZONE_FILE | awk '{print $1}')
NEW_SERIAL=$(date +%Y%m%d%H)
sed -i "s/$CURRENT_SERIAL/$NEW_SERIAL/" $DNS_ZONE_FILE
systemctl reload bind9

echo "DNS güncellendi. Propagasyon 5 dakika sürebilir."

Tatbikat Sırasında İzleme

Tatbikat boyunca sistemin nasıl davrandığını canlı olarak izlemeniz gerekiyor. Basit ama etkili bir izleme scripti:

#!/bin/bash
# dr_monitoring.sh - Tatbikat sırasında gerçek zamanlı izleme

TARGET_HOST="${1:-localhost}"
CHECK_INTERVAL=10
REPORT_FILE="/var/log/dr_test/monitoring_$(date +%Y%m%d_%H%M%S).csv"

echo "timestamp,http_status,response_time_ms,db_connection,disk_usage" > $REPORT_FILE

while true; do
    TIMESTAMP=$(date +%Y-%m-%d_%H:%M:%S)
    
    # HTTP yanıt süresi ve kodu
    HTTP_RESPONSE=$(curl -s -o /dev/null -w "%{http_code},%{time_total}" 
        --max-time 10 "http://$TARGET_HOST/health" 2>/dev/null)
    HTTP_STATUS=$(echo $HTTP_RESPONSE | cut -d',' -f1)
    RESPONSE_TIME=$(echo $HTTP_RESPONSE | cut -d',' -f2 | awk '{printf "%.0f", $1*1000}')
    
    # Veritabanı bağlantısı
    DB_STATUS=$(pg_isready -h $TARGET_HOST -q 2>/dev/null && echo "ok" || echo "fail")
    
    # Disk kullanımı
    DISK_USAGE=$(df -h / | awk 'NR==2{print $5}')
    
    # CSV'ye yaz
    echo "$TIMESTAMP,$HTTP_STATUS,$RESPONSE_TIME,$DB_STATUS,$DISK_USAGE" >> $REPORT_FILE
    
    # Konsola renkli çıktı
    if [ "$HTTP_STATUS" = "200" ]; then
        echo -e "33[32m[$TIMESTAMP] HTTP:$HTTP_STATUS | Yanıt:${RESPONSE_TIME}ms | DB:$DB_STATUS33[0m"
    else
        echo -e "33[31m[$TIMESTAMP] HTTP:$HTTP_STATUS | Yanıt:${RESPONSE_TIME}ms | DB:$DB_STATUS - SORUN!33[0m"
    fi
    
    sleep $CHECK_INTERVAL
done

Değerlendirme Aşaması: Asıl İş Burada Başlıyor

Tatbikat bitti, sistemler ayakta. Şimdi en kritik aşamaya geliyoruz: neyin iyi gittiğini, neyin kötü gittiğini dürüstçe değerlendirmek.

Metrik Toplama ve Hesaplama

#!/bin/bash
# calculate_dr_metrics.sh - Tatbikat metriklerini hesapla

LOG_FILE="/var/log/dr_test/timeline_$(date +%Y%m%d).log"
MONITORING_CSV="/var/log/dr_test/monitoring_$(date +%Y%m%d)*.csv"

echo "=== DR Tatbikat Değerlendirme Raporu ==="
echo "Tarih: $(date)"
echo ""

# Toplam kesinti süresi
INCIDENT_START=$(grep "Felaket tespit edildi" $LOG_FILE | head -1 | cut -d']' -f1 | tr -d '[')
SYSTEM_RESTORED=$(grep "Sistem tamamıyla ayakta" $LOG_FILE | head -1 | cut -d']' -f1 | tr -d '[')

echo "Felaket Tespit: $INCIDENT_START"
echo "Sistem Kurtarıldı: $SYSTEM_RESTORED"
echo ""

# HTTP başarı oranı
TOTAL_CHECKS=$(wc -l < $MONITORING_CSV)
SUCCESS_CHECKS=$(grep ",200," $MONITORING_CSV | wc -l)
SUCCESS_RATE=$(awk "BEGIN {printf "%.1f", ($SUCCESS_CHECKS/$TOTAL_CHECKS)*100}")

echo "HTTP Kontrol Sayısı: $TOTAL_CHECKS"
echo "Başarılı Yanıt: $SUCCESS_CHECKS"
echo "Başarı Oranı: %${SUCCESS_RATE}"
echo ""

# Ortalama yanıt süresi (tatbikat sırasında)
AVG_RESPONSE=$(awk -F',' 'NR>1 {sum+=$3; count++} END {printf "%.0f", sum/count}' $MONITORING_CSV)
echo "Ortalama Yanıt Süresi: ${AVG_RESPONSE}ms"

Geri Bildirim Toplama

Teknik metrikler önemli ama yeterli değil. Tatbikata katılan herkesin geri bildirimi de kritik:

  • Tatbikat koordinatörüne sorular: Adımlar dokümantasyona uygun yürütüldü mü? Beklenmyen durumlar çıktı mı?
  • Operasyon ekibine sorular: Hangi adımda tıkandılar? Dokümanlara erişmekte sorun yaşadılar mı?
  • İletişim ekibine sorular: Stakeholder’lar doğru zamanda bilgilendirilebildi mi?

Eksiklik Tespiti

Tatbikat sırasında çıkan sorunları kategorize edin:

  • Kritik eksiklikler: RTO/RPO hedeflerine ulaşılmasını engelleyen sorunlar. Örneğin yedek şifre bilgisinin tek kişide olması, o kişi ulaşılabilir değilse süreç durdu.
  • Önemli eksiklikler: Süreci yavaşlatan ama hedefe ulaşmayı engellemeyenler. Örneğin bazı servis bağımlılıklarının dokümanda eksik olması.
  • Küçük eksiklikler: İyileştirme önerileri. Örneğin komutların runbook’ta daha açık yazılması.

Tatbikat Sonrası Sistem Durumunu Doğrulama

DR’dan geri döndükten sonra, yani production’a failback yaptıktan sonra her şeyin düzgün çalıştığını doğrulayın:

#!/bin/bash
# post_dr_validation.sh - Tatbikat sonrası sistem doğrulama

echo "=== Tatbikat Sonrası Sistem Doğrulama ==="
echo "Tarih: $(date)"
PASS=0
FAIL=0

check_service() {
    local SERVICE=$1
    local STATUS=$(systemctl is-active $SERVICE 2>/dev/null)
    if [ "$STATUS" = "active" ]; then
        echo "  [TAMAM] $SERVICE çalışıyor"
        PASS=$((PASS+1))
    else
        echo "  [HATA] $SERVICE durumu: $STATUS"
        FAIL=$((FAIL+1))
    fi
}

check_endpoint() {
    local NAME=$1
    local URL=$2
    local HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 $URL)
    if [ "$HTTP_CODE" = "200" ]; then
        echo "  [TAMAM] $NAME ($URL) yanıt veriyor"
        PASS=$((PASS+1))
    else
        echo "  [HATA] $NAME ($URL) HTTP: $HTTP_CODE"
        FAIL=$((FAIL+1))
    fi
}

echo ""
echo "Servis Kontrolleri:"
check_service nginx
check_service postgresql
check_service redis
check_service app-service

echo ""
echo "Endpoint Kontrolleri:"
check_endpoint "Web Uygulaması" "http://localhost/health"
check_endpoint "API" "http://localhost/api/status"
check_endpoint "Admin Paneli" "http://localhost/admin/ping"

echo ""
echo "=== Sonuç ==="
echo "Başarılı: $PASS"
echo "Başarısız: $FAIL"

if [ $FAIL -eq 0 ]; then
    echo "Tüm kontroller geçti. Sistem normal operasyona hazır."
else
    echo "UYARI: $FAIL kontrol başarısız! Müdahale gerekiyor."
fi

Tatbikat Sıklığı ve Olgunluk Modeli

Felaket kurtarma tatbikatları tek seferlik yapılacak bir şey değil. Bunları bir olgunluk modeline oturtmak gerekiyor:

  • Yeni başlayan (0-1 yıl): Yılda bir kez masa başı tatbikatı yapın. Temel süreçleri belgelendirin. En azından yedeklerin restore edildiğini test edin.
  • Gelişmekte olan (1-3 yıl): Yılda iki kez, biri kısmi biri tam failover tatbikatı. Otomasyon scriptlerini geliştirin. RTO/RPO değerlerini ölçmeye başlayın.
  • Olgun (3+ yıl): Üç ayda bir tatbikat. Kaos mühendisliği prensiplerini uygulayın. Tatbikatları çalışanlara haber vermeden yapın (dikkatli planlayın). Tamamen otomatik failover testleri.

Netflix’in “Chaos Monkey” yaklaşımını küçük ölçekte uygulamak bile büyük farklar yaratıyor. Production’da rastgele servis öldüren bir script çalıştırmak cesaret ister ama sistemlerinizin gerçek dayanıklılığını ortaya koyar.

Sık Yapılan Hatalar

Yıllar içinde gördüğüm en yaygın DR tatbikat hatalarını paylaşmak istiyorum:

  • Tatbikatı sanal yapmak: “Senaryo: veritabanı çöktü, siz ne yapardınız?” yerine gerçekten çökertmeden test etmek anlamsız. Gerçek ortamda test etme imkanınız yoksa staging ortamı kurun.
  • Sadece teknik ekiple tatbikat yapmak: DR sadece teknik bir süreç değil. İK, finans, müşteri hizmetleri de dahil olmalı. Onların ihtiyaçları olmadan RTO hesaplamaları eksik kalır.
  • Başarılı tatbikatı raporlayıp geçmek: “Her şey yolunda” raporuyla tatbikatı kapatırsanız iyileştirme fırsatını kaçırırsınız. Her tatbikattan en az 3 iyileştirme maddesi çıkması beklenir.
  • Dokümantasyonu güncellemeden geçmek: Tatbikat sırasında öğrendiklerinizi runbook’a yansıtmazsanız bir sonraki tatbikat için hiçbir şey değişmez.
  • Yedeği test etmemek: “Yedek alındı” ile “yedekten geri yükleme çalışıyor” çok farklı şeyler. Yedek alma işlemi başarılı görünse de restore test edilmemişse kağıt üzerinde bir garantiniz var.

Sonuç

Felaket kurtarma tatbikatı; planı çekmeceden çıkarıp gerçeklikle yüzleştirme eylemidir. Bir tatbikattan “her şey mükemmeldi” sonucu çıkıyorsa ya tatbikatı yeterince zorlamadınız ya da gerçekten çok iyi bir hazırlık süreciniz var ki bu durumda da tebrikler.

Tatbikatların değeri, başarı sayısından değil, öğrenilen derslerden ölçülür. Failover’ın 47 dakika yerine 2 saatte tamamlandığını tatbikata kadar bilmiyordunuz; artık biliyorsunuz ve düzeltebilirsiniz. Runbook’un 3. adımında kritik bir bilginin eksik olduğunu canlıda değil tatbikatta keşfetmek, belki saatler hatta günler kurtarmış olabilir.

Bugün yapabileceğiniz en küçük adım: yedeğinizden bir dosyayı gerçekten geri yüklemek. Sadece bu bile başlangıç için yeterli. Oradan yavaş yavaş kapsamı genişletirsiniz. Sonunda, gerçek bir felaket geldiğinde, paniklemek yerine daha önce defalarca yaptığınız bir prosedürü uyguladığınızı fark edersiniz. Ve o an, tüm tatbikat zahmetinin karşılığını almış olursunuz.

Bir yanıt yazın

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