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.
