Sunucu Çökmesi Senaryosu: Adım Adım Kurtarma Rehberi
Saat sabahın 3’ü. Telefonunuz çalıyor. Nöbetçi mühendis panikle sizi arıyor: “Production sunucu cevap vermiyor, kullanıcılar sisteme giremiyor.” İşte bu an, aylar öncesinden hazırladığınız felaket kurtarma planının değerini kanıtlayacak andır. Eğer plan yoksa, o gece çok uzun geçecek.
Bu yazıda gerçek bir sunucu çökmesi senaryosunu ele alacağız. Teorik değil, ellerin klavyede olduğu, terminallerin ekrana döküldüğü, kararların hızla alındığı pratik bir kılavuz. Linux tabanlı bir web sunucusunun tamamen çöktüğünü ve sistemi ayağa kaldırmamız gerektiğini varsayalım.
Senaryo: Ne Oldu?
Bir e-ticaret şirketinin production ortamında, Ubuntu 22.04 üzerinde çalışan bir web sunucusu var. Disk I/O hataları başlamış, ardından filesystem bozulmuş ve sunucu boot edemez hale gelmiş. Üstüne bir de veritabanı servisinin son yedeği 6 saat önce alınmış.
Elimizdekiler:
- Fiziksel sunucuya konsol erişimi
- Son 6 saatin dışında kalan bir tam sistem yedeği (rsync tabanlı)
- MySQL veritabanının 6 saatlik binary log’ları
- Ayrı bir sunucuda hazırda bekleyen standby makine
Hedefimiz sistemi mümkün olan en az veri kaybıyla, mümkün olan en kısa sürede ayağa kaldırmak.
İlk 15 Dakika: Panik Değil, Değerlendirme
Sunucu çöktüğünde yapılacak ilk şey durumu net olarak anlamaktır. Körü körüne komut çalıştırmak durumu daha da kötüleştirebilir. Önce bir nefes alın, sonra sistematik ilerleyin.
Hasarı Tespit Etme
Sunucuya konsol üzerinden bağlanın ve boot mesajlarına bakın. Çoğu zaman sorunun kaynağı burada açıkça yazıyor olur.
# Sistemin son kernel mesajlarını görüntüle
dmesg | tail -50
# Filesystem hatalarını kontrol et
dmesg | grep -i "error|fail|corrupt|i/o"
# Disk durumunu kontrol et
smartctl -a /dev/sda
smartctl -H /dev/sda
Bizim senaryomuzda dmesg çıktısı şunu gösteriyor:
[123456.789] EXT4-fs error (device sda1): ext4_find_entry:1455: inode #2: comm systemd: reading directory lblock 0
[123456.790] Buffer I/O error on dev sda1, logical block 0, async page read
[123456.791] EXT4-fs (sda1): delayed block allocation failed for inode 1234 at logical offset 0 with max blocks 8 with error -5
Bu mesajlar açık: filesystem bozulmuş, disk’te bad sector’lar var. Sistemi bu haliyle ayağa kaldırmaya çalışmak daha fazla veri kaybına yol açar.
Önce Yedeği Doğrulama
Kurtarma işlemine başlamadan önce yedeğinizin gerçekten sağlıklı olduğunu doğrulayın. Bu adımı atlayan sysadminler, saatler sonra yedeğin bozuk olduğunu keşfederek ikinci bir şokla karşılaşır.
# Yedek sunucusuna bağlanın ve integrity kontrolü yapın
ssh backup-server
# Rsync tabanlı yedeğin son durumunu kontrol et
ls -lah /backups/production/latest/
cat /backups/production/latest/.backup_manifest
# Kritik dosyaların var olup olmadığını kontrol et
ls -lah /backups/production/latest/var/lib/mysql/
ls -lah /backups/production/latest/var/www/html/
ls -lah /backups/production/latest/etc/nginx/
Yedek sağlıklı görünüyor. Artık kurtarma planını devreye alabilirsiniz.
Standby Sunucuyu Devreye Alma
Bizim senaryomuzda hazırda bekleyen bir standby makine var. Bu ideal durum. Gerçek dünyada standby sunucu olmayabilir, ama olması için şimdi not düşüyoruz.
DNS ve Load Balancer Geçişi
İlk yapılacak şey trafiği sağlıklı bir makineye yönlendirmektir. Kullanıcılar beklemeye devam etmesin.
# DNS TTL değerini düşürün (öncesinde yapılmış olmalıydı ama olmadıysa şimdi yapın)
# AWS Route53 örneği - AWS CLI ile
aws route53 change-resource-record-sets
--hosted-zone-id ZXXXXXXXXXXXXX
--change-batch '{
"Changes": [{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "api.sirketiniz.com",
"Type": "A",
"TTL": 60,
"ResourceRecords": [{"Value": "10.0.1.50"}]
}
}]
}'
# HAProxy load balancer varsa backend'i devre dışı bırak
echo "disable server web_backend/prod-web-01" | sudo socat stdio /var/run/haproxy/admin.sock
echo "enable server web_backend/standby-web-01" | sudo socat stdio /var/run/haproxy/admin.sock
Bu komutları çalıştırdıktan sonra trafiğin standby sunucuya aktığını doğrulayın. Kullanıcılar artık sisteme girebiliyor olmalı, ancak son 6 saatlik veriler henüz orada değil.
Bozuk Diskten Veri Kurtarma Girişimi
Sistem çalışırken, bozuk diskten ne kadar veri kurtarabileceğimizi araştırmaya başlayabiliriz. Bu işlemi paralel yürütmek zaman kazandırır.
Rescue Mode ile Boot
Sunucuyu rescue mode’a alın ve filesystem’i okumaya çalışın.
# Rescue modda, önce filesystem'i sadece okuma modunda mount etmeye çalışın
mount -o ro,noload /dev/sda1 /mnt/recovery
# Eğer mount başarısızsa fsck deneyin (ama önce yedeği alın!)
# Disk image'ini başka bir diske kopyalayın
ddrescue -d -r3 /dev/sda /dev/sdb /tmp/recovery.log
# ddrescue durumu
cat /tmp/recovery.log
ddrescue aracı, normal dd’den çok daha akıllıdır. Bad sector’ları atlayarak okuyabileceği tüm veriyi kurtarmaya çalışır. Bu işlem saatler sürebilir, sabırlı olun.
# ddrescue bittikten sonra kopyayı mount etmeyi deneyin
mount -o ro /dev/sdb1 /mnt/recovery
# MySQL data dizinini kopyala
cp -a /mnt/recovery/var/lib/mysql/ /tmp/mysql_recovery/
# Binary log'ları bul
ls -lah /mnt/recovery/var/lib/mysql/mysql-bin.*
MySQL Veri Kurtarma ve Point-in-Time Recovery
Bu noktada elimizde şunlar var:
- 6 saatlik bir tam yedek
- Kurtarılan binary log’lar
Binary log’lar ile point-in-time recovery yaparak veri kaybını minimize edebiliriz.
Yedeği Standby Sunucuya Restore Etme
# Standby sunucuda MySQL servisini durdur
sudo systemctl stop mysql
# Mevcut data dizinini temizle (dikkatli olun!)
sudo rm -rf /var/lib/mysql/*
# Yedeği geri yükle
sudo rsync -avz --progress
backup-server:/backups/production/latest/var/lib/mysql/
/var/lib/mysql/
# Ownership'i düzelt
sudo chown -R mysql:mysql /var/lib/mysql/
# MySQL'i başlat
sudo systemctl start mysql
# Restore'u doğrula
mysql -u root -p -e "SHOW DATABASES; SELECT COUNT(*) FROM siparisler.orders;"
Binary Log ile Veri Geri Kazanma
Yedeğin alındığı zamandan crash anına kadar olan işlemleri binary log’lardan replay edeceğiz.
# Binary log'ları standby sunucuya kopyala
scp -r root@backup-server:/tmp/mysql_recovery/mysql-bin.* /tmp/binlogs/
# Hangi log'ların hangi zaman aralığını kapsadığını göster
mysqlbinlog --no-defaults /tmp/binlogs/mysql-bin.000123 | head -20
# Yedeğin alındığı andan crash anına kadar olan işlemleri uygula
mysqlbinlog --no-defaults
--start-datetime="2024-01-15 09:00:00"
--stop-datetime="2024-01-15 15:00:00"
/tmp/binlogs/mysql-bin.000123
/tmp/binlogs/mysql-bin.000124
| mysql -u root -p
# Veri tutarlılığını doğrula
mysql -u root -p -e "
SELECT COUNT(*) as toplam_siparis FROM siparisler.orders;
SELECT MAX(created_at) as son_siparis FROM siparisler.orders;
"
Eğer bu işlem başarılıyla tamamlanırsa, veri kaybınız neredeyse sıfıra inmiş demektir.
Uygulama Katmanını Restore Etme
Veritabanı ayakta, şimdi sıra web uygulamasında.
# Nginx konfigürasyonunu restore et
sudo rsync -avz
backup-server:/backups/production/latest/etc/nginx/
/etc/nginx/
# Uygulama dosyalarını restore et
sudo rsync -avz
backup-server:/backups/production/latest/var/www/html/
/var/www/html/
# Environment dosyasını kontrol et (veritabanı connection string'i güncellenmeli mi?)
cat /var/www/html/.env | grep DB_HOST
# Eğer eski sunucunun IP'sini gösteriyorsa güncelle
sed -i 's/DB_HOST=10.0.1.10/DB_HOST=localhost/g' /var/www/html/.env
# PHP-FPM ve Nginx'i başlat
sudo systemctl start php8.1-fpm
sudo systemctl start nginx
# Servislerin durumunu kontrol et
sudo systemctl status nginx php8.1-fpm mysql
Uygulama Sağlık Kontrolü
# HTTP response kontrolü
curl -I https://sirketiniz.com/health-check
# Beklenen: HTTP/2 200
# Veritabanı bağlantısını uygulamadan test et
curl -s https://sirketiniz.com/api/db-status | jq .
# Log dosyalarını izle
tail -f /var/log/nginx/error.log
tail -f /var/log/php8.1-fpm.log
journalctl -u mysql -f
Sistemin Tam Olarak Kurtarıldığını Doğrulama
Sistem ayaktayken doğrulama yapmadan “tamam” demek erken sevinç olur. Bir kontrol listesi üzerinden geçin.
#!/bin/bash
# recovery_check.sh - Kurtarma sonrası sağlık kontrolü
echo "=== Servis Durumları ==="
for service in nginx mysql php8.1-fpm redis-server; do
status=$(systemctl is-active $service)
echo "$service: $status"
done
echo ""
echo "=== Disk Kullanımı ==="
df -h | grep -v tmpfs
echo ""
echo "=== Bellek Durumu ==="
free -h
echo ""
echo "=== Son 100 Nginx Hatası ==="
tail -100 /var/log/nginx/error.log | grep -c "error"
echo ""
echo "=== MySQL Bağlantı Havuzu ==="
mysql -u root -p -e "SHOW STATUS LIKE 'Threads_connected';" 2>/dev/null
echo ""
echo "=== Uygulama Response Time ==="
for endpoint in "/" "/api/products" "/api/orders"; do
time=$(curl -o /dev/null -s -w "%{time_total}" https://sirketiniz.com$endpoint)
echo "$endpoint: ${time}s"
done
Bu script’i çalıştırın ve her şeyin yeşil olduğunu görene kadar sistemi tam olarak kurtarılmış saymayın.
Incident Sonrası Yapılacaklar
Sistem ayakta, kriz atlatıldı. Ama iş bitmedi. Asıl önemli kısım şimdi başlıyor.
Post-Mortem Hazırlama
Olay bittikten sonraki 24-48 saat içinde bir post-mortem raporu yazılmalı. Bu rapor suçlamak için değil, öğrenmek ve tekrar yaşanmaması için hazırlanır.
Post-mortem’de şunlar olmalı:
- Olay özeti: Ne oldu, ne zaman oldu, ne zaman fark edildi
- Etki analizi: Kaç kullanıcı etkilendi, ne kadar süre sistem çevrimdışıydı
- Zaman çizelgesi: Dakika dakika ne yapıldı
- Kök neden analizi: Neden oldu? Sadece “disk bozuldu” değil, neden bozuldu, neden monitoring uyarı vermedi, neden yedek 6 saatlikti
- Aksiyon maddeleri: Tekrar yaşanmaması için ne yapılacak, kim yapacak, ne zamana kadar
Altyapı İyileştirmeleri
Bu olaydan çıkarılan dersler doğrultusunda yapılması gerekenler:
# SMART monitoring kurulumu - disk bozulmadan önce uyarı vermeli
sudo apt install smartmontools
# /etc/smartd.conf düzenle
echo "DEVICESCAN -a -o on -S on -n standby,q -s (S/../.././02|L/../../6/03)
-W 4,45,55 -m [email protected]" | sudo tee /etc/smartd.conf
sudo systemctl enable smartd
sudo systemctl start smartd
# Disk I/O monitoring için iostat
sudo apt install sysstat
# /etc/default/sysstat'ta ENABLED="true" yap
sudo systemctl enable sysstat
sudo systemctl start sysstat
# Geçmiş I/O verilerini izle
sar -d 1 10
Yedekleme Stratejisini Güçlendirme
6 saatlik yedek aralığı kabul edilemez. Hemen güncelleyin.
#!/bin/bash
# mysql_incremental_backup.sh
# Her saat çalışacak incremental yedekleme scripti
BACKUP_DIR="/backups/mysql/incremental"
MYSQL_USER="backup_user"
MYSQL_PASS="guclu_sifre"
DATE=$(date +%Y%m%d_%H%M%S)
# Binary log pozisyonunu kaydet
mysql -u$MYSQL_USER -p$MYSQL_PASS -e
"SHOW MASTER STATUSG" > $BACKUP_DIR/binlog_position_$DATE.txt
# Binary log dosyalarını kopyala
LATEST_BINLOG=$(mysql -u$MYSQL_USER -p$MYSQL_PASS -Nse
"SHOW MASTER STATUS" | awk '{print $1}')
rsync -avz /var/lib/mysql/mysql-bin.*
backup-server:/backups/mysql/binlogs/
# Backup log
echo "$(date): Incremental backup tamamlandi - $LATEST_BINLOG"
>> /var/log/mysql_backup.log
# Crontab'a ekle - her saat başı çalışsın
(crontab -l 2>/dev/null; echo "0 * * * * /usr/local/bin/mysql_incremental_backup.sh") | crontab -
Monitoring ve Erken Uyarı Sistemi
Bu felaket bir kısmıyla önlenebilirdi. Doğru monitoring olsaydı disk sorunu daha erken fark edilebilirdi.
# Prometheus node exporter kurulumu
wget https://github.com/prometheus/node_exporter/releases/download/v1.7.0/node_exporter-1.7.0.linux-amd64.tar.gz
tar xvf node_exporter-1.7.0.linux-amd64.tar.gz
sudo mv node_exporter-1.7.0.linux-amd64/node_exporter /usr/local/bin/
# Servis olarak başlat
sudo tee /etc/systemd/system/node_exporter.service << EOF
[Unit]
Description=Node Exporter
After=network.target
[Service]
User=node_exporter
ExecStart=/usr/local/bin/node_exporter
--collector.diskstats
--collector.filesystem
--collector.meminfo
--collector.loadavg
Restart=always
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable node_exporter
sudo systemctl start node_exporter
Prometheus alert kuralları da şöyle tanımlanmalı:
# /etc/prometheus/alerts/disk_alerts.yml
cat << 'EOF' | sudo tee /etc/prometheus/alerts/disk_alerts.yml
groups:
- name: disk_health
rules:
- alert: DiskSpaceKritik
expr: (node_filesystem_avail_bytes / node_filesystem_size_bytes) * 100 < 10
for: 5m
labels:
severity: critical
annotations:
summary: "{{ $labels.instance }} disk dolmak uzere"
description: "Disk dolulugu %90 ustunde"
- alert: DiskIOYuksek
expr: rate(node_disk_io_time_seconds_total[5m]) > 0.9
for: 10m
labels:
severity: warning
annotations:
summary: "Yuksek disk I/O tespit edildi"
EOF
Felaket Kurtarma Planını Test Etme
Bu deneyimi yaşadıktan sonra sizi en çok kızdıracak şey şu olacak: “Keşke önceden test etseydik.” Test edilmemiş bir DR planı, planın olmadığı kadar tehlikelidir.
Her çeyrek dönemde en az bir kez tam felaket senaryosu tatbikatı yapın. Bu tatbikatta:
- Yedekten tam restore süresini ölçün
- RTO (Recovery Time Objective) ve RPO (Recovery Point Objective) değerlerinizi doğrulayın
- Ekibin hangi adımları bilmediğini veya nerede tıkandığını tespit edin
- Kurtarma playbook’unuzu tatbikattan çıkan derslerle güncelleyin
Tatbikat sırasında tutulacak notlar post-mortem kadar değerlidir.
Sonuç
Sabahın 3’ünde başlayan o panik dolu telefon konuşmasından saatlerce sonra sistem yeniden ayaktadır. Veri kaybı minimize edilmiştir. Kullanıcılar sisteme girebilmektedir.
Ama asıl soru şu: Bu felaketi neden yaşadınız?
Disk hatası bir gün gelecekti. SMART uyarıları bunu haftalar öncesinden söyleyebilirdi. Yedekleme sıklığı yetersizdi. Standby sunucu otomatik devreye girmiyor, elle müdahale gerekiyordu. Monitoring eksikti.
Felaket kurtarma, kriz anında değil, kriz öncesinde kazanılır. Yedeklerinizi her gün test edin, SMART monitoring’i aktif edin, binary log’ları sürekli replication ile başka bir sunucuya taşıyın, playbook’unuzu güncel tutun ve tatbikatları es geçmeyin.
O gece sabahın 3’ünde telefonu açan kişi, eğer hazırlıklıysa “tamam, planı devreye alıyoruz” diyebilir. Değilse, o gece sabahı görmek zorunda kalır.
Hazırlık pahalıdır, ama veri kaybı ve kesinti daha pahalıdır. Seçim sizin.
