Felaket Kurtarma Planı Nedir: RTO ve RPO Temelleri
Bir gece yarısı telefon çalıyor. Üretim veritabanınız çökmüş, e-ticaret siteniz yanıt vermiyor, her dakika binlerce lira kaybediyorsunuz. Panik içinde ne yapacağınızı bilmiyorsunuz çünkü hiç felaket kurtarma planı yapmadınız. Bu senaryo kulağa abartılı gelebilir ama sektörde çalışan herkes benzer bir anı yaşamıştır ya da birisinden dinlemiştir. İşte tam bu yüzden RTO ve RPO kavramlarını anlamak, bir sysadmin için teknik bilgi kadar kritik.
Felaket Kurtarma Planı Nedir?
Felaket kurtarma planı (Disaster Recovery Plan – DRP), bir BT altyapısının beklenmedik bir olay sonrasında nasıl ayağa kaldırılacağını tanımlayan dokümante edilmiş süreçler bütünüdür. Buradaki “felaket” kelimesi sizi yanıltmasın. Doğal afet olmak zorunda değil. Yanlış çalıştırılan bir rm -rf, bozulan bir RAID dizisi, fidye yazılımı saldırısı ya da veri merkezindeki bir güç kesintisi de felaket kapsamında değerlendirilir.
Bir DRP’nin temel amacı şudur: Sistemler çöktüğünde herkesin ne yapacağını önceden bilmesini sağlamak. Panik anında doğru kararlar vermek son derece zordur. İyi bir plan, o panik anını minimize eder ve süreci mekanik hale getirir.
Ancak bir DRP hazırlamadan önce iki soruyu cevaplamanız gerekir:
- Ne kadar süre kesintiye tahammül edebilirsiniz?
- Ne kadar veri kaybına tahammül edebilirsiniz?
Bu iki sorunun cevabı sizi RTO ve RPO kavramlarına götürür.
RTO Nedir? (Recovery Time Objective)
RTO, bir sistemin felaketten sonra ne kadar süre içinde tekrar çalışır hale gelmesi gerektiğini tanımlar. Türkçeye “Kurtarma Süresi Hedefi” olarak çevrilebilir.
Diyelim ki bir e-ticaret şirketindesiniz. Yöneticiler “sistemler en fazla 2 saat kapalı kalabilir, ondan sonra iş durur” diyorsa, RTO’nuz 2 saattir. Bu süre iş gereksinimlerine göre belirlenir, teknik kapasiteye göre değil. Teknik ekip bu süreye uymak zorundadır.
RTO düşüldükçe maliyet artar. Sıfıra yakın RTO hedefleri için aktif-aktif yüksek erişilebilirlik kümelerine, otomatik failover mekanizmalarına ve çoğaltılmış altyapıya ihtiyaç duyarsınız.
Pratik bir örnek verelim. PostgreSQL veritabanınızın ne kadar sürede ayağa kalkabileceğini test etmek için şu şekilde bir zaman ölçümü yapabilirsiniz:
#!/bin/bash
# RTO test scripti - PostgreSQL recovery süresi ölçümü
START_TIME=$(date +%s)
echo "[$(date)] Kurtarma başlatılıyor..."
# Servisi durdur (simülasyon için)
systemctl stop postgresql
# Yedekten geri yükleme başlat
pg_restore -h localhost -U postgres -d mydb /backup/mydb_latest.dump
# Servis başlatma
systemctl start postgresql
# Servis hazır olana kadar bekle
until pg_isready -h localhost -U postgres; do
echo "PostgreSQL bekleniyor..."
sleep 2
done
END_TIME=$(date +%s)
ELAPSED=$((END_TIME - START_TIME))
echo "[$(date)] Kurtarma tamamlandı. Geçen süre: ${ELAPSED} saniye"
echo "RTO Hedefi: 7200 saniye (2 saat)"
if [ $ELAPSED -le 7200 ]; then
echo "DURUM: RTO hedefi karşılandı"
else
echo "DURUM: RTO hedefi AŞILDI! Planı gözden geçirin."
fi
Bu scripti düzenli aralıklarla çalıştırıp sonuçları kaydetmek, RTO hedefinizin gerçekçi olup olmadığını anlamanızı sağlar.
RPO Nedir? (Recovery Point Objective)
RPO, bir felaket durumunda ne kadar veri kaybının kabul edilebilir olduğunu tanımlar. Türkçeye “Kurtarma Noktası Hedefi” olarak çevrilebilir.
Günde bir kez yedek alıyorsanız ve felaket yedek alımından 23 saat sonra gerçekleşirse, 23 saatlik veriyi kaybedersiniz. Eğer işletme “en fazla 1 saatlik veri kaybı kabul edilebilir” diyorsa, RPO’nuz 1 saattir ve günlük yedekleme bu ihtiyacı karşılamaz.
RPO’yu belirlerken şu soruları sormak faydalıdır:
- Bu sistemde saatte kaç işlem gerçekleşiyor?
- Kaybolan veriler yeniden oluşturulabilir mi?
- Veri kaybının yasal ya da finansal sonuçları var mı?
- Müşterilere etkisi ne olur?
Saatlik snapshot alan bir sistem için RPO durumunu kontrol eden basit bir script:
#!/bin/bash
# RPO kontrol scripti - Son yedek zamanını kontrol et
BACKUP_DIR="/backup/snapshots"
MAX_RPO_MINUTES=60 # RPO hedefi: 60 dakika
# En son yedek dosyasını bul
LATEST_BACKUP=$(ls -t "$BACKUP_DIR"/*.snap 2>/dev/null | head -1)
if [ -z "$LATEST_BACKUP" ]; then
echo "KRITIK: Hiç yedek bulunamadı!"
exit 2
fi
# Son yedek zamanını al
BACKUP_TIME=$(stat -c %Y "$LATEST_BACKUP")
CURRENT_TIME=$(date +%s)
DIFF_MINUTES=$(( (CURRENT_TIME - BACKUP_TIME) / 60 ))
echo "Son yedek: $(date -d @$BACKUP_TIME '+%Y-%m-%d %H:%M:%S')"
echo "Geçen süre: $DIFF_MINUTES dakika"
echo "RPO hedefi: $MAX_RPO_MINUTES dakika"
if [ $DIFF_MINUTES -gt $MAX_RPO_MINUTES ]; then
echo "UYARI: RPO ihlali! Son yedek $DIFF_MINUTES dakika önce alındı."
# Alarm gönder
mail -s "RPO İhlali Uyarısı" [email protected] <<<
"Son yedek $DIFF_MINUTES dakika önce alındı. RPO hedefi aşıldı!"
exit 1
else
echo "TAMAM: RPO hedefi karşılanıyor."
exit 0
fi
Bu scripti cron’a ekleyerek her 15 dakikada bir çalıştırabilirsiniz:
# crontab -e
*/15 * * * * /opt/scripts/rpo_check.sh >> /var/log/rpo_check.log 2>&1
RTO ve RPO Arasındaki İlişki
Bu iki kavram birbirinden bağımsız değildir. Düşük RPO hedefi genellikle daha sık ve karmaşık yedekleme mekanizmaları gerektirir ki bu da kurtarma sürecini etkileyebilir. Öte yandan çok fazla yedekleme noktası, hangi noktaya dönüleceğine karar vermeyi zorlaştırır.
Gerçek dünya deneyiminden bir örnek: Bir müşteri için PostgreSQL replikasyon kurulumu yaparken hem RTO hem de RPO için agresif hedefler belirlenmişti. Streaming replikasyon ile RPO neredeyse sıfıra indirilmişti ama failover süreci tam otomatize edilmediğinden RTO hala yüksekti. Sorun, teknik kapasite değil operasyonel süreçti.
Patroni ile otomatik failover kurulumu bu dengesizliği gidermek için mükemmel bir çözümdür:
# Patroni konfigürasyonu - /etc/patroni/patroni.yml
cat > /etc/patroni/patroni.yml << 'EOF'
scope: postgres-cluster
namespace: /db/
name: postgresql-node1
restapi:
listen: 0.0.0.0:8008
connect_address: 192.168.1.10:8008
etcd:
hosts: 192.168.1.20:2379,192.168.1.21:2379,192.168.1.22:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576 # 1MB - RPO kontrolü
initdb:
- encoding: UTF8
- data-checksums
postgresql:
listen: 0.0.0.0:5432
connect_address: 192.168.1.10:5432
data_dir: /var/lib/postgresql/14/main
bin_dir: /usr/lib/postgresql/14/bin
parameters:
wal_level: replica
hot_standby: "on"
max_wal_senders: 10
max_replication_slots: 10
wal_log_hints: "on"
tags:
nofailover: false
noloadbalance: false
EOF
systemctl enable patroni
systemctl start patroni
Buradaki maximum_lag_on_failover parametresi özellikle önemli. Bu değer, bir standby sunucunun ne kadar geride kalabileceğini tanımlar ve RPO’nuzla doğrudan ilişkilidir.
Felaket Kurtarma Kategorileri
Tüm sistemlerin aynı RTO ve RPO hedefine ihtiyacı yoktur. Bu yüzden sistemleri kritiklik seviyelerine göre sınıflandırmak akıllıca bir yaklaşımdır.
Tier 0 – Kritik Sistemler:
- RTO: Dakikalar (0-30 dakika)
- RPO: Sıfır veya saniyeler
- Örnek: Finansal işlem sistemleri, hasta takip sistemleri
- Yöntem: Aktif-aktif replikasyon, otomatik failover
Tier 1 – Önemli Sistemler:
- RTO: 1-4 saat
- RPO: 15 dakika – 1 saat
- Örnek: CRM, ERP, e-posta sunucuları
- Yöntem: Aktif-pasif replikasyon, düzenli snapshot
Tier 2 – Normal Sistemler:
- RTO: 4-24 saat
- RPO: 1-4 saat
- Örnek: Geliştirme ortamları, raporlama sistemleri
- Yöntem: Günlük yedekleme, periyodik snapshot
Tier 3 – Düşük Öncelikli Sistemler:
- RTO: 24-72 saat
- RPO: 24 saat
- Örnek: Test sistemleri, arşiv sistemleri
- Yöntem: Haftalık veya aylık yedekleme
Pratik: RTO/RPO Hedeflerinizi Test Etmek
Bir DRP’nin en değersiz hali test edilmemiş olanıdır. “Teoride çalışıyor” diye bir felaket kurtarma planı yoktur. Planınızın gerçekten çalıştığını kanıtlamanız gerekir.
Aşağıdaki script, bir MySQL veritabanı için tam kurtarma testi yapıp süreleri ölçer:
#!/bin/bash
# Felaket Kurtarma Test Scripti - MySQL
# Kullanim: ./dr_test.sh [backup_file]
set -e
BACKUP_FILE="${1:-/backup/mysql/latest_full.sql.gz}"
TEST_DB="dr_test_$(date +%Y%m%d_%H%M%S)"
LOG_FILE="/var/log/dr_test_$(date +%Y%m%d).log"
RPO_THRESHOLD=3600 # saniye cinsinden RPO hedefi
RTO_THRESHOLD=7200 # saniye cinsinden RTO hedefi
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
check_backup_age() {
local backup_time=$(stat -c %Y "$BACKUP_FILE")
local now=$(date +%s)
local age=$((now - backup_time))
log "Yedek dosyası yaşı: $age saniye (RPO hedefi: $RPO_THRESHOLD saniye)"
if [ $age -gt $RPO_THRESHOLD ]; then
log "HATA: RPO ihlali! Yedek çok eski."
return 1
fi
log "TAMAM: RPO kontrolü geçti."
return 0
}
restore_test() {
local start=$(date +%s)
log "Test veritabanı oluşturuluyor: $TEST_DB"
mysql -u root -p"$MYSQL_ROOT_PASS" -e "CREATE DATABASE $TEST_DB;"
log "Geri yükleme başlatılıyor..."
gunzip -c "$BACKUP_FILE" | mysql -u root -p"$MYSQL_ROOT_PASS" "$TEST_DB"
local end=$(date +%s)
local elapsed=$((end - start))
log "Geri yükleme süresi: $elapsed saniye (RTO hedefi: $RTO_THRESHOLD saniye)"
if [ $elapsed -gt $RTO_THRESHOLD ]; then
log "HATA: RTO ihlali! Kurtarma çok uzun sürdü."
mysql -u root -p"$MYSQL_ROOT_PASS" -e "DROP DATABASE $TEST_DB;"
return 1
fi
log "TAMAM: RTO kontrolü geçti."
return 0
}
verify_data() {
log "Veri bütünlüğü kontrol ediliyor..."
local table_count=$(mysql -u root -p"$MYSQL_ROOT_PASS" -se
"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='$TEST_DB';")
log "Geri yüklenen tablo sayısı: $table_count"
if [ "$table_count" -eq 0 ]; then
log "HATA: Hiç tablo bulunamadı!"
return 1
fi
log "TAMAM: Veri bütünlüğü doğrulandı."
return 0
}
cleanup() {
log "Test veritabanı temizleniyor..."
mysql -u root -p"$MYSQL_ROOT_PASS" -e "DROP DATABASE IF EXISTS $TEST_DB;" 2>/dev/null
log "Temizlik tamamlandı."
}
# Ana akış
log "========== DR Test Başlıyor =========="
trap cleanup EXIT
check_backup_age || exit 1
restore_test || exit 1
verify_data || exit 1
log "========== DR Test BAŞARILI =========="
Bu testi aylık olarak çalıştırıp sonuçlarını kaydetmek, ekibinizin DRP’yi ne kadar iyi uyguladığını izlemenizi sağlar.
Gerçek Dünya Senaryosu: E-Ticaret Platformu
Somut bir örnek üzerinden gidelim. 50.000 günlük sipariş işleyen bir e-ticaret platformu düşünün.
İş gereksinimleri:
- Sistemin 2 saatten fazla kapalı kalması durumunda SLA cezası devreye giriyor (RTO: 2 saat)
- Sipariş verilerinin kaybı yasal sorun yaratır ve müşteri şikayeti getirir (RPO: 15 dakika)
Bu gereksinimlere göre altyapı planlaması şöyle yapılabilir. WAL arşivleme ile PostgreSQL konfigürasyonu:
# postgresql.conf güncellemeleri
cat >> /etc/postgresql/14/main/postgresql.conf << 'EOF'
# WAL Arşivleme - RPO için kritik
wal_level = replica
archive_mode = on
archive_command = 'rsync -a %p backup-server:/wal-archive/%f'
archive_timeout = 900 # Her 15 dakikada bir WAL segmenti zorla arşivle
# Replikasyon ayarları
max_wal_senders = 5
wal_keep_size = 1GB
synchronous_commit = on
# Checkpoint ayarları
checkpoint_completion_target = 0.9
max_wal_size = 4GB
EOF
# Konfigürasyonu yeniden yükle
systemctl reload postgresql
archive_timeout = 900 ayarı, en fazla 15 dakikada bir WAL dosyasının arşivlenmesini garantiler. Bu da RPO’nuzu yaklaşık 15 dakikaya sabitler. Felaket anında point-in-time recovery (PITR) ile tam olarak 15 dakika önceki duruma dönebilirsiniz.
Otomatik Uyarı Sistemi Kurmak
RTO ve RPO hedeflerinizi belirledikten sonra bu hedeflere yaklaşıldığında uyarı almanız şarttır. Bir şey ters gittiğinde haberi olmak yerine haberi olmak tercih edilir.
#!/bin/bash
# Kapsamlı DRP izleme scripti
# /opt/scripts/drp_monitor.sh
SMTP_SERVER="smtp.sirket.com"
ALERT_EMAIL="[email protected]"
SLACK_WEBHOOK="https://hooks.slack.com/services/XXXXX"
send_alert() {
local severity="$1"
local message="$2"
# E-posta uyarısı
echo "$message" | mail -s "[$severity] DRP Uyarısı - $(hostname)"
-S smtp="$SMTP_SERVER" "$ALERT_EMAIL"
# Slack uyarısı
curl -s -X POST "$SLACK_WEBHOOK"
-H 'Content-type: application/json'
--data "{"text":"[$severity] $(hostname): $message"}"
logger -t drp_monitor "[$severity] $message"
}
check_replication_lag() {
# PostgreSQL replikasyon gecikmesini kontrol et
local lag_bytes=$(psql -U postgres -t -c
"SELECT COALESCE(pg_wal_lsn_diff(pg_current_wal_lsn(),
replay_lsn), 0) FROM pg_stat_replication LIMIT 1;" 2>/dev/null | tr -d ' ')
local lag_mb=$((lag_bytes / 1024 / 1024))
if [ "$lag_mb" -gt 100 ]; then
send_alert "KRITIK" "Replikasyon gecikmesi kritik seviyede: ${lag_mb}MB"
elif [ "$lag_mb" -gt 50 ]; then
send_alert "UYARI" "Replikasyon gecikmesi yüksek: ${lag_mb}MB"
fi
echo "Replikasyon gecikmesi: ${lag_mb}MB"
}
check_backup_freshness() {
local backup_dirs=("/backup/daily" "/backup/hourly")
for dir in "${backup_dirs[@]}"; do
local latest=$(find "$dir" -name "*.gz" -newer /tmp/.last_backup_check 2>/dev/null | wc -l)
if [ "$latest" -eq 0 ]; then
send_alert "KRITIK" "Son 1 saatte $dir dizininde yeni yedek oluşturulmadı!"
fi
done
touch /tmp/.last_backup_check
}
check_disk_space() {
# Yedek dizinlerinin disk kullanımını kontrol et
local backup_usage=$(df /backup | awk 'NR==2 {print $5}' | tr -d '%')
if [ "$backup_usage" -gt 90 ]; then
send_alert "KRITIK" "Yedek diski dolmak üzere: %${backup_usage} kullanımda!"
elif [ "$backup_usage" -gt 80 ]; then
send_alert "UYARI" "Yedek diski dolmaya başlıyor: %${backup_usage} kullanımda"
fi
}
# Tüm kontrolleri çalıştır
check_replication_lag
check_backup_freshness
check_disk_space
DRP Dokümantasyonu: Unutulan Kısım
Teknik hazırlık kadar önemli bir şey daha var: dokümantasyon. Gece yarısı sistemi kurtarmaya çalışan kişi siz olmayabilirsiniz. Tatilde olabilirsiniz, hasta olabilirsiniz ya da işi bırakan siz olabilirsiniz.
İyi bir DRP dokümanı şunları içermelidir:
- Sistem envanteri: Hangi sunucular var, nerede, ne işe yarıyor
- İletişim ağacı: Kim ne zaman aranacak, yöneticiler kimler
- Adım adım kurtarma prosedürleri: Her sistem için ayrı ayrı
- Erişim bilgileri: Şifreler, anahtarlar (güvenli bir vault’ta saklanmalı)
- Test geçmişi: Önceki testlerin sonuçları ve sorunlar
- Bağımlılık haritası: Hangi sistem hangisine bağımlı
Dokümanı sadece wiki’ye koymayın. Fiziksel bir kopyasının da olması faydalıdır. Sisteminiz çöktüğünde wiki’ye de erişemeyebilirsiniz.
Bulut Ortamında RTO ve RPO
Artık çoğu altyapı kısmen veya tamamen bulutta. AWS, Azure ya da GCP kullanıyorsanız, bu sağlayıcıların size sunduğu araçları RTO ve RPO hesaplamalarınıza dahil etmeniz gerekir.
AWS üzerinde basit bir yedekleme durumu kontrolü:
#!/bin/bash
# AWS RDS yedek durumu kontrol scripti
REGION="eu-west-1"
DB_INSTANCE="prod-postgres"
MAX_AGE_HOURS=2
# Son otomatik yedeği bul
LATEST_SNAPSHOT=$(aws rds describe-db-snapshots
--region "$REGION"
--db-instance-identifier "$DB_INSTANCE"
--snapshot-type automated
--query 'sort_by(DBSnapshots, &SnapshotCreateTime)[-1].SnapshotCreateTime'
--output text)
if [ -z "$LATEST_SNAPSHOT" ] || [ "$LATEST_SNAPSHOT" = "None" ]; then
echo "KRITIK: Hiç otomatik snapshot bulunamadı!"
exit 2
fi
# Snapshot zamanını epoch'a çevir
SNAPSHOT_EPOCH=$(date -d "$LATEST_SNAPSHOT" +%s 2>/dev/null ||
python3 -c "from datetime import datetime;
print(int(datetime.fromisoformat('${LATEST_SNAPSHOT%.*}').timestamp()))")
CURRENT_EPOCH=$(date +%s)
AGE_HOURS=$(( (CURRENT_EPOCH - SNAPSHOT_EPOCH) / 3600 ))
echo "Son RDS snapshot: $LATEST_SNAPSHOT"
echo "Snapshot yaşı: $AGE_HOURS saat"
if [ $AGE_HOURS -gt $MAX_AGE_HOURS ]; then
echo "UYARI: Snapshot RPO hedefini ($MAX_AGE_HOURS saat) aşmış!"
exit 1
else
echo "TAMAM: Snapshot güncel."
exit 0
fi
Sonuç
RTO ve RPO sadece teknik metrikler değil, iş kararlarıdır. Bir sysadmin olarak göreviniz bu metrikleri belirlemek değil, belirlenen metriklere uygun teknik çözümler üretmek ve bunların gerçekten çalıştığını kanıtlamaktır.
En büyük hata, bir felaket kurtarma planını yazıp çekmeceye kaldırmaktır. Planlar test edilmezse değer taşımaz. Yedekler doğrulanmadan alınırsa güvensizdir. RTO ve RPO hedefleri düzenli olarak gözden geçirilmezse iş gereksinimleriyle uyumsuz hale gelir.
Pratik önerilerle bitirelim:
- Her ay yedekten geri yükleme testi yapın
- Her çeyrekte tam felaket senaryosu tatbikatı düzenleyin
- Her yıl RTO ve RPO hedeflerini iş birimleriyle birlikte gözden geçirin
- Ekibinizdeki herkes kurtarma prosedürlerini bilsin, sadece kıdemli sysadmin değil
- Otomatik uyarıları gecenin köründe telefona yansıtın çünkü felaketler mesai saatlerini tanımaz
Bir felaket kurtarma planı aslında bir “ne zaman değil, nasıl” meselesidir. Sistem bir gün mutlaka çökecektir. Hazır mısınız?
