Warm Standby ile Felaket Kurtarma Mimarisi
Üretim ortamınız çöktüğünde saatler içinde değil, dakikalar içinde ayağa kalkabilmek istiyorsanız Warm Standby mimarisi tam da aradığınız şey. Ne Hot Standby kadar pahalı, ne de Cold Standby kadar riskli. Bu yazıda gerçek dünya senaryoları üzerinden Warm Standby’ı nasıl kuracağınızı, test edeceğinizi ve operasyonel hale getireceğinizi adım adım ele alacağız.
Warm Standby Nedir ve Neden Tercih Edilir
Felaket kurtarma stratejileri genellikle üç ana kategoriye ayrılır: Cold Standby, Warm Standby ve Hot Standby. Cold Standby’da sistemler kapalıdır, felaket anında kurulum ve yapılandırma gerekir, RTO (Recovery Time Objective) saatlerle ölçülür. Hot Standby’da ise her şey canlı çalışır, traffic anında devralınır ama maliyet iki katına çıkar.
Warm Standby tam ortada durur. Sistemler çalışır durumda ama production trafiği almaz. Veriler sürekli replike edilir. Felaket anında DNS veya load balancer seviyesinde bir yönlendirme değişikliğiyle dakikalar içinde devreye girebilirsiniz. Çoğu şirket için bu denge idealdir: RTO genellikle 15 dakika ile 1 saat arasında, RPO (Recovery Point Objective) ise dakikalar mertebesinde tutulabilir.
Gerçek dünyadan bir örnek verelim: E-ticaret platformu çalıştıran bir müşterim vardı. Black Friday’de primary datacenter’larında UPS arızası yaşandı. Hot Standby maliyetini karşılayamıyorlardı ama Warm Standby ile kurulu sistemleri 22 dakikada production trafiğini devraldı. O günkü ciro kaybı minimumda kaldı.
Mimari Bileşenler ve Tasarım Kararları
Warm Standby mimarisi birkaç temel bileşenden oluşur:
- Primary Site: Canlı trafiği karşılayan üretim ortamı
- Standby Site: Azaltılmış kapasitede çalışan, verinin replike edildiği yedek ortam
- Replikasyon Katmanı: Veritabanı, dosya sistemi ve uygulama state’inin senkronize tutulduğu mekanizma
- Monitoring ve Failover Orchestration: Sağlık kontrolü yapan ve failover’ı tetikleyen sistem
- DNS veya Load Balancer Katmanı: Traffic yönlendirmesini yöneten bileşen
Kapasite planlaması konusunda standby sitenizin %50 ile %70 kapasitede çalışması genellikle yeterlidir. Failover sonrası tam kapasite için auto-scaling devreye alınır.
PostgreSQL Streaming Replication ile Veritabanı Katmanı
Veritabanı replikasyonu Warm Standby’ın kalbidir. PostgreSQL’in built-in streaming replication özelliği bu iş için mükemmeldir.
Primary sunucuda PostgreSQL yapılandırması:
# /etc/postgresql/15/main/postgresql.conf
wal_level = replica
max_wal_senders = 5
wal_keep_size = 1GB
synchronous_commit = local
archive_mode = on
archive_command = 'rsync -az %p standby-server:/var/lib/postgresql/wal_archive/%f'
hot_standby = on
Replikasyon kullanıcısı oluşturma ve pg_hba.conf yapılandırması:
# Primary sunucuda
sudo -u postgres psql -c "CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'guclu_sifre_buraya';"
# /etc/postgresql/15/main/pg_hba.conf dosyasına ekle
echo "host replication replicator standby-ip/32 md5" >> /etc/postgresql/15/main/pg_hba.conf
# PostgreSQL'i yeniden yükle
sudo systemctl reload postgresql
Standby sunucuda base backup alıp replikasyonu başlatma:
# Standby sunucuda
sudo systemctl stop postgresql
sudo -u postgres rm -rf /var/lib/postgresql/15/main/*
# Primary'dan base backup al
sudo -u postgres pg_basebackup
-h primary-server-ip
-U replicator
-D /var/lib/postgresql/15/main
-P
-Xs
-R
# pg_basebackup -R flagı ile standby.signal ve recovery ayarları otomatik oluşturulur
# Oluşturulan postgresql.auto.conf'u doğrula
cat /var/lib/postgresql/15/main/postgresql.auto.conf
# Standby'ı başlat
sudo systemctl start postgresql
# Replikasyon durumunu kontrol et (primary'da)
sudo -u postgres psql -c "SELECT client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn FROM pg_stat_replication;"
Uygulama Katmanı Senkronizasyonu
Veritabanı hazır, şimdi uygulama sunucularındaki dosya ve konfigürasyonların standby’a taşınması gerekiyor. Bunun için rsync tabanlı sürekli senkronizasyon kullanacağız.
#!/bin/bash
# /usr/local/bin/app_sync.sh
# Uygulama dosyalarını standby'a senkronize eden script
STANDBY_HOST="standby-server.internal"
APP_DIR="/var/www/myapp"
CONFIG_DIR="/etc/myapp"
LOG_FILE="/var/log/app_sync.log"
RSYNC_OPTS="-avz --delete --exclude='*.tmp' --exclude='cache/' --exclude='sessions/'"
timestamp() {
date '+%Y-%m-%d %H:%M:%S'
}
log() {
echo "[$(timestamp)] $1" | tee -a "$LOG_FILE"
}
sync_directory() {
local src=$1
local dest=$2
rsync $RSYNC_OPTS "$src" "${STANDBY_HOST}:${dest}" 2>&1
if [ $? -eq 0 ]; then
log "SUCCESS: $src senkronize edildi"
else
log "ERROR: $src senkronizasyonu basarisiz"
# Alert gonder
echo "App sync failed for $src" | mail -s "SYNC ALERT" [email protected]
fi
}
# Ana senkronizasyon
sync_directory "$APP_DIR/" "$APP_DIR"
sync_directory "$CONFIG_DIR/" "$CONFIG_DIR"
# Sertifikaları da senkronize et
sync_directory "/etc/ssl/myapp/" "/etc/ssl/myapp"
log "Senkronizasyon tamamlandi"
Bu scripti cron ile 5 dakikada bir çalıştırın:
# crontab -e
*/5 * * * * /usr/local/bin/app_sync.sh >> /var/log/app_sync.log 2>&1
Sağlık Kontrolü ve Otomatik Failover
Warm Standby’ın en kritik parçası otomatik failover mekanizmasıdır. Bunu Bash ile basit ama güvenilir bir şekilde implemente edebilirsiniz. Daha karmaşık ortamlar için Patroni veya Pacemaker kullanabilirsiniz ama küçük ve orta ölçekli yapılar için aşağıdaki script gayet işe yarar.
#!/bin/bash
# /usr/local/bin/failover_monitor.sh
# Primary sağlık kontrolü ve otomatik failover
PRIMARY_IP="10.0.1.10"
STANDBY_IP="10.0.1.20"
DNS_ZONE="sirket.internal"
APP_FQDN="app.sirket.internal"
CHECK_INTERVAL=30
FAILURE_THRESHOLD=3
FAILURE_COUNT=0
STATE_FILE="/var/run/failover_state"
LOG_FILE="/var/log/failover_monitor.log"
# Nsupdate için TSIG key
TSIG_KEY="/etc/bind/failover.key"
timestamp() { date '+%Y-%m-%d %H:%M:%S'; }
log() { echo "[$(timestamp)] $1" >> "$LOG_FILE"; echo "[$(timestamp)] $1"; }
check_primary_health() {
# HTTP health check
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}"
--connect-timeout 5 --max-time 10
"http://${PRIMARY_IP}/health")
# PostgreSQL check
PG_STATUS=$(PGPASSWORD='monitor_pass' psql -h "$PRIMARY_IP"
-U monitor -d myapp -c "SELECT 1" -t -q 2>/dev/null | tr -d ' ')
if [ "$HTTP_STATUS" = "200" ] && [ "$PG_STATUS" = "1" ]; then
return 0
fi
return 1
}
perform_failover() {
log "FAILOVER BASLATIYOR: Primary ${PRIMARY_IP} erisilemez durumda"
# Slack/PagerDuty notification
curl -s -X POST -H 'Content-type: application/json'
--data "{"text":"KRITIK: Failover baslatildi! Standby ${STANDBY_IP} devreye aliyor"}"
"$SLACK_WEBHOOK_URL"
# PostgreSQL standby'ı promote et
ssh -i /root/.ssh/failover_key postgres@"$STANDBY_IP"
"pg_ctl promote -D /var/lib/postgresql/15/main"
sleep 5
# DNS kaydını güncelle (bind9 ile)
nsupdate -k "$TSIG_KEY" << EOF
server dns-server.internal
zone ${DNS_ZONE}
update delete ${APP_FQDN} A
update add ${APP_FQDN} 60 A ${STANDBY_IP}
send
EOF
# Standby'daki nginx'i aktive et
ssh -i /root/.ssh/failover_key root@"$STANDBY_IP"
"systemctl start nginx && systemctl start php-fpm"
echo "failed" > "$STATE_FILE"
log "FAILOVER TAMAMLANDI: Traffic ${STANDBY_IP} uzerine yonlendirildi"
# Ops ekibine detaylı mail
mail -s "FAILOVER TAMAMLANDI - Manuel Mudahale Gerekli" [email protected] << MAIL
Failover $(timestamp) zamaninda tamamlandi.
Primary: ${PRIMARY_IP} - ERISILEEMEZ
Standby: ${STANDBY_IP} - AKTIF
Yapilmasi gerekenler:
1. Primary sunucu durumunu kontrol et
2. Primary'i onardiktan sonra failback prosedurunu uygula
3. Replikasyonu yeniden kur
MAIL
}
# Ana dongu
while true; do
CURRENT_STATE=$(cat "$STATE_FILE" 2>/dev/null || echo "normal")
if [ "$CURRENT_STATE" = "normal" ]; then
if check_primary_health; then
FAILURE_COUNT=0
log "Primary saglikli - Kontrol: OK"
else
FAILURE_COUNT=$((FAILURE_COUNT + 1))
log "Primary saglik kontrolu basarisiz ($FAILURE_COUNT/$FAILURE_THRESHOLD)"
if [ "$FAILURE_COUNT" -ge "$FAILURE_THRESHOLD" ]; then
perform_failover
fi
fi
fi
sleep "$CHECK_INTERVAL"
done
Failback Prosedürü
Felaket kurtarma planının en çok ihmal edilen kısmı failback, yani standby’dan tekrar primary’a dönüş sürecidir. Bu prosedür dokümante edilmeli ve test edilmelidir.
#!/bin/bash
# /usr/local/bin/failback.sh
# Standby'dan Primary'a geri donuş prosedürü
PRIMARY_IP="10.0.1.10"
STANDBY_IP="10.0.1.20"
DNS_ZONE="sirket.internal"
APP_FQDN="app.sirket.internal"
echo "=== FAILBACK PROSEDURU BASLIYOR ==="
echo "Bu islem once primary'i standby olarak konfigüre edecek"
echo "Sonra rolleri degistirerek eski haline getirecek"
echo ""
read -p "Devam etmek istiyor musunuz? (evet/hayir): " CONFIRM
if [ "$CONFIRM" != "evet" ]; then
echo "Failback iptal edildi"
exit 1
fi
echo "[1/5] Primary sunucu saglik kontrolü..."
if ! ping -c 3 "$PRIMARY_IP" > /dev/null 2>&1; then
echo "ERROR: Primary sunucu hala erisilemez! Failback iptal edildi."
exit 1
fi
echo "[2/5] Primary'da PostgreSQL'i standby olarak yapilandirma..."
ssh root@"$PRIMARY_IP" << 'REMOTE'
# Mevcut veriyi temizle ve standby olarak yeniden baslat
systemctl stop postgresql
sudo -u postgres rm -rf /var/lib/postgresql/15/main/*
sudo -u postgres pg_basebackup
-h STANDBY_IP_BURAYA
-U replicator
-D /var/lib/postgresql/15/main
-P -Xs -R
systemctl start postgresql
REMOTE
echo "[3/5] Replikasyon kontrol ediliyor (30 saniye bekleniyor)..."
sleep 30
# Replikasyon lag kontrolü
LAG=$(PGPASSWORD='monitor_pass' psql -h "$STANDBY_IP" -U monitor -d myapp
-t -c "SELECT EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))::int;" 2>/dev/null | tr -d ' ')
echo "Replikasyon lag: ${LAG} saniye"
if [ "$LAG" -gt 60 ]; then
echo "UYARI: Replikasyon lag 60 saniyeden fazla. Devam etmek istiyor musunuz?"
read -p "(evet/hayir): " LAG_CONFIRM
if [ "$LAG_CONFIRM" != "evet" ]; then exit 1; fi
fi
echo "[4/5] Standby'ı durdurma ve Primary'i promote etme..."
# Standby'daki uygulama trafiğini durdur
ssh root@"$STANDBY_IP" "systemctl stop nginx"
# Primary'i promote et
ssh root@"$PRIMARY_IP" "sudo -u postgres pg_ctl promote -D /var/lib/postgresql/15/main"
sleep 5
echo "[5/5] DNS kaydini guncelleniyor..."
nsupdate -k /etc/bind/failover.key << EOF
server dns-server.internal
zone ${DNS_ZONE}
update delete ${APP_FQDN} A
update add ${APP_FQDN} 300 A ${PRIMARY_IP}
send
EOF
# Standby'ın eski rolüne döndürülmesi
ssh root@"$STANDBY_IP" "systemctl stop postgresql"
echo "=== FAILBACK TAMAMLANDI ==="
echo "Primary ${PRIMARY_IP} artik aktif"
echo "Standby replikasyonu yeniden kurmayi unutmayin!"
echo "State dosyasini temizleyin: rm /var/run/failover_state"
Felaket Kurtarma Testleri
Felaket kurtarma planı test edilmiyorsa yoktur. Warm Standby’ı üç farklı düzeyde test etmenizi öneririm.
Tier 1: Haftalık Otomatik Bileşen Testleri
Bu testler production’ı etkilemez, sadece bileşenlerin sağlıklı olduğunu doğrular:
#!/bin/bash
# /usr/local/bin/dr_health_check.sh
# Her Pazartesi 02:00'de cron ile çalışır
REPORT_FILE="/var/log/dr_weekly_report_$(date +%Y%m%d).txt"
ERRORS=0
check_replication_lag() {
LAG=$(sudo -u postgres psql -t -c
"SELECT EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))::int;"
2>/dev/null | tr -d ' ')
if [ -z "$LAG" ] || [ "$LAG" -gt 300 ]; then
echo "FAIL: Replikasyon lag $LAG saniye - kritik esigi asiyor" >> "$REPORT_FILE"
ERRORS=$((ERRORS + 1))
else
echo "OK: Replikasyon lag $LAG saniye" >> "$REPORT_FILE"
fi
}
check_standby_disk_space() {
DISK_USAGE=$(ssh root@standby-server "df /var/lib/postgresql | awk 'NR==2{print $5}' | tr -d '%'")
if [ "$DISK_USAGE" -gt 80 ]; then
echo "WARN: Standby disk kullanimi %$DISK_USAGE" >> "$REPORT_FILE"
ERRORS=$((ERRORS + 1))
else
echo "OK: Standby disk kullanimi %$DISK_USAGE" >> "$REPORT_FILE"
fi
}
check_rsync_freshness() {
# Son sync zamanını kontrol et
LAST_SYNC=$(ssh root@standby-server "stat -c %Y /var/www/myapp/version.txt 2>/dev/null")
NOW=$(date +%s)
DIFF=$((NOW - LAST_SYNC))
if [ "$DIFF" -gt 600 ]; then
echo "FAIL: Son dosya senkronizasyonu $DIFF saniye once" >> "$REPORT_FILE"
ERRORS=$((ERRORS + 1))
else
echo "OK: Son dosya senkronizasyonu $DIFF saniye once" >> "$REPORT_FILE"
fi
}
echo "DR Haftalik Saglik Raporu - $(date)" > "$REPORT_FILE"
echo "==========================================" >> "$REPORT_FILE"
check_replication_lag
check_standby_disk_space
check_rsync_freshness
echo "" >> "$REPORT_FILE"
echo "Toplam hata: $ERRORS" >> "$REPORT_FILE"
# Raporu mail ile gonder
mail -s "DR Haftalik Rapor - Hata: $ERRORS" [email protected] < "$REPORT_FILE"
exit $ERRORS
Tier 2: Aylık Tam Failover Testi
Aylık olarak maintenance window açıp gerçek failover testi yapın. Bu test production ortamında değil, traffic’in az olduğu gece saatlerinde yapılabilir veya staging ortamında simüle edilebilir.
Test adımları şunlardır:
- Primary sunucuyu bilinçli olarak durdur (ağ bağlantısını kes veya servisi durdur)
- Failover monitor’ün tetiklendiğini izle ve süreyi kaydet
- Standby’ın trafiği devraldığını fonksiyonel testlerle doğrula
- RTO ve RPO hedeflerine ulaşılıp ulaşılmadığını kayıt altına al
- Failback prosedürünü uygula ve sonuçları dokümante et
Her testin ardından şu bilgileri kaydedin:
- Failover için geçen süre (saniye cinsinden)
- Veri kaybı miktarı (son commit ile failover anı arasındaki fark)
- Hangi alarm mekanizmaları tetiklendi
- Manuel müdahale gerektiren adımlar
- Sonraki testte iyileştirilecek noktalar
Monitoring ve Alerting Entegrasyonu
Warm Standby’ın işlevsel olması için sürekli gözlemlenmesi gerekir. Prometheus ve Alertmanager ile entegrasyon için temel metrikler şunlardır:
- pg_stat_replication: Replikasyon lag ve state
- node_disk_io_time_seconds_total: I/O baskısı
- node_filesystem_avail_bytes: Disk kapasitesi
- custom metric: Rsync başarı/başarısızlık oranı
Grafana dashboard’unuzda mutlaka şu paneller olsun:
- Replikasyon lag (zaman serisi grafik, 5 dakikalık pencere)
- Son başarılı rsync zamanı
- Standby servis durumları (PostgreSQL, Nginx, uygulama)
- Failover geçmişi ve süresi
Gerçek Dünya: Kaçınılması Gereken Hatalar
Yıllar içinde gördüğüm en yaygın Warm Standby hatalarını paylaşayım:
Split-brain durumu: Her iki node da kendini primary sanabilir. Bunu önlemek için STONITH (Shoot The Other Node In The Head) mekanizması veya basitçe DNS-only failover kullanın. Primary’i durdurduğunuzdan emin olmadan standby’ı promote etmeyin.
Test edilmemiş failover: Bir müşterimde gerçek felaket anında failover scripti çalıştı ama PostgreSQL promote komutu beklenmedik bir hata verdi. Nedeni: pg_ctl binary’nin path’i farklıydı ve hiç test edilmemişti. Test etmeden güvenme.
DNS TTL gözardı etme: Failover için DNS güncellerken eski TTL değerleri cachede kalabilir. Kritik kayıtlar için TTL’i normalden 300 saniyede tutun. Felaket öncesinde bunu 60’a düşürmek iyi bir pratiktir.
Replikasyon lag alarmını görmezden gelme: “Biraz lag var, düzelir” demek felaket habercisidir. Replikasyon lag 5 dakikayı geçtiğinde mutlaka araştırın.
Sadece veritabanını düşünmek: Uygulama session’ları, Redis cache, job queue’lar, cronjob state’leri de replikasyona dahil edilmelidir. Sadece PostgreSQL’i kopyalamak yeterli değildir.
Sonuç
Warm Standby mimarisi, gerçek dünyada dengeyi doğru kuran bir felaket kurtarma yaklaşımıdır. Hot Standby kadar pahalı değil ama Cold Standby kadar riskli de değil. Kritik nokta şu: mimariyi kurmak başlangıçtır, düzenli test etmek onu hayata geçirir.
Bu yazıda anlattığım yapıyı kendi ortamınıza adapte ederken şu öncelikleri gözetin: Önce veritabanı replikasyonunu sağlam kurun ve lag’ı sürekli izleyin. Sonra dosya senkronizasyonunu otomatize edin. Ardından failover/failback scriptlerini yazın ve her ikisini de test edin. Son olarak tüm bu süreçleri monitoring’e bağlayın ve aylık testleri takvime koyun.
Ekibinizde herkes DR prosedürünü bilmeli. Felaket gece 02:00’de gerçekleşir ve o saatte en deneyimli ekip üyeniz ulaşılamaz olabilir. Runbook’larınızı güncel tutun, onboarding sürecinize DR testini dahil edin ve her başarılı failover testini bir kutlama olarak değil, sistemin çalıştığının kanıtı olarak kaydedin.
