Arşivleme İşlemlerinde Hata Ayıklama ve Loglama: Başarısız Yedekleri Tespit Etme ve Raporlama
Yedekleme sistemleri, tam olarak ihtiyacınız olduğu an sizi yarı yolda bırakır. Bu bir kural değil, Murphy’nin sysadmin versiyonu. Sabah 3’te bir production sunucusunda disaster recovery yapmaya çalışırken yedeklerin bozuk olduğunu keşfetmek, bu mesleğin en kötü anlarından biridir. Bu yazıda, arşivleme işlemlerinde hata ayıklamanın ve düzgün loglama kurmanın nasıl yapıldığını, gerçek senaryolar üzerinden ele alacağız.
Sorunun Kaynağını Anlamak: Sessiz Hatalar
Arşivleme işlemlerinde en tehlikeli hata türü, sessiz hatalardır. tar veya gzip çalışır, çıkış kodu 0 döner, ama oluşan arşiv ya eksik ya bozuk ya da hiç işe yaramaz durumdadır. Çoğu sysadmin, cron job’a bir satır ekleyip işin bittiğini sanır. Oysa bu tam tersine, problemi görünmez hale getirir.
Örneğin klasik bir yedekleme scripti şöyle görünür:
tar -czf /backup/uygulama_$(date +%Y%m%d).tar.gz /var/www/uygulama/
Bu komut hata verse bile, cron’dan gelen mail genellikle kimsenin okumadığı bir inbox’a düşer. Sonuç: 3 aydır bozuk yedek alıyorsunuzdur, hiç haberiniz yoktur.
Temel Hata Tespiti: Exit Code Kontrolü
Her şey exit code kontrolüyle başlar. tar başarısızlıkta sıfırdan farklı bir kod döner, bunu yakalamazsanız problemi göremezsiniz.
#!/bin/bash
KAYNAK="/var/www/uygulama"
HEDEF="/backup/uygulama_$(date +%Y%m%d_%H%M%S).tar.gz"
LOG="/var/log/yedekleme/yedek.log"
tar -czf "$HEDEF" "$KAYNAK"
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] HATA: tar islemi basarisiz oldu. Exit code: $EXIT_CODE" >> "$LOG"
exit 1
else
echo "[$(date '+%Y-%m-%d %H:%M:%S')] BASARILI: $HEDEF olusturuldu." >> "$LOG"
fi
Burada $? değişkeni kritik. Ama dikkat edin: tar bazı durumlarda uyarı vererek çalışmaya devam eder ve 1 döner. Bu, “bazı dosyalar değişti” anlamına gelebilir. 2 dönerse bu gerçek bir hata anlamındadır. Bu ayrımı yapmak önemlidir.
- Exit code 0: Her şey yolunda
- Exit code 1: Uyarılar var, bazı dosyalar değişmiş olabilir (genellikle kabul edilebilir)
- Exit code 2: Kritik hata, arşiv büyük ihtimalle kullanılamaz
Verbose Loglama ile Detaylı İzleme
Üretim ortamında neyin yedeklendiğini, neyin atlandığını bilmek zorundasınız. tar‘ın -v parametresi bunu sağlar ama log dosyanızı hızla şişirir. Daha akıllıca bir yaklaşım, sadece hata satırlarını yakalamaktır.
#!/bin/bash
KAYNAK="/var/www/uygulama"
HEDEF="/backup/uygulama_$(date +%Y%m%d_%H%M%S).tar.gz"
LOG_DIR="/var/log/yedekleme"
LOG_DOSYA="$LOG_DIR/yedek_$(date +%Y%m%d).log"
HATA_LOG="$LOG_DIR/hata_$(date +%Y%m%d).log"
mkdir -p "$LOG_DIR"
# Hem stdout hem stderr'i ayir, hatalari ayri dosyaya yaz
tar -czf "$HEDEF" "$KAYNAK"
2> >(while IFS= read -r satir; do
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [HATA] $satir" | tee -a "$HATA_LOG" >> "$LOG_DOSYA"
done)
EXIT_CODE=$?
BOYUT=$(du -sh "$HEDEF" 2>/dev/null | cut -f1)
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [BILGI] Tamamlandi. Boyut: ${BOYUT:-BELIRSIZ}, Exit: $EXIT_CODE" >> "$LOG_DOSYA"
# Hata logu doluysa alert gonder
if [ -s "$HATA_LOG" ]; then
echo "Yedekleme sureci hatalarla tamamlandi. Log: $HATA_LOG" |
mail -s "[UYARI] Yedekleme Hatasi - $(hostname)" [email protected]
fi
Buradaki process substitution >(...) kullanımı, stderr’i gerçek zamanlı timestamp’le loglamanızı sağlar. Standart 2>>log yönlendirmesinden çok daha kullanışlı çünkü her satıra zaman damgası ekleyebilirsiniz.
Arşiv Bütünlük Kontrolü: En Kritik Adım
Arşiv oluştu diye sevinmeyin. Test etmeden yedek sayılmaz. tar‘ın -t parametresi arşivi açmadan içindeki dosya listesini doğrular; gzip -t ise sıkıştırma bütünlüğünü kontrol eder.
#!/bin/bash
arsiv_dogrula() {
local ARSIV=$1
local LOG=$2
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Butunluk kontrolu basliyor: $ARSIV" >> "$LOG"
# Once gzip kontrolu
if ! gzip -t "$ARSIV" 2>> "$LOG"; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [KRITIK] gzip butunluk hatasi: $ARSIV" >> "$LOG"
return 1
fi
# Sonra tar kontrolu
DOSYA_SAYISI=$(tar -tzf "$ARSIV" 2>> "$LOG" | wc -l)
if [ "$DOSYA_SAYISI" -eq 0 ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [KRITIK] Arsiv bos! $ARSIV" >> "$LOG"
return 1
fi
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [OK] Arsiv gecerli. Dosya sayisi: $DOSYA_SAYISI" >> "$LOG"
return 0
}
HEDEF="/backup/uygulama_$(date +%Y%m%d_%H%M%S).tar.gz"
LOG="/var/log/yedekleme/yedek.log"
tar -czf "$HEDEF" /var/www/uygulama/ 2>> "$LOG"
if [ $? -eq 0 ]; then
arsiv_dogrula "$HEDEF" "$LOG"
DOGRULAMA=$?
if [ $DOGRULAMA -ne 0 ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [KRITIK] Dogrulama basarisiz, arsiv siliniyor" >> "$LOG"
rm -f "$HEDEF"
exit 2
fi
fi
Bu yaklaşım, disk alanı dolduğunda, NFS bağlantısı kesildiğinde veya dosya sistemi hataları olduğunda sizi korur. Pratikte, kontrol etmeden güvendiğim için pişman olduğum sayısız an yaşadım.
Checksum ile Kriptografik Doğrulama
Bütünlük kontrolünü bir adım öteye taşımak istiyorsanız, checksum kullanın. Özellikle arşivleri uzak bir konuma transfer ediyorsanız bu zorunlu hale gelir.
#!/bin/bash
ARSIV="/backup/uygulama_$(date +%Y%m%d).tar.gz"
CHECKSUM_DOSYA="${ARSIV}.sha256"
LOG="/var/log/yedekleme/yedek.log"
# Arsivi olustur
tar -czf "$ARSIV" /var/www/uygulama/ 2>> "$LOG"
TAR_EXIT=$?
if [ $TAR_EXIT -le 1 ]; then
# SHA256 hesapla ve kaydet
sha256sum "$ARSIV" > "$CHECKSUM_DOSYA"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Checksum kaydedildi: $(cat $CHECKSUM_DOSYA)" >> "$LOG"
fi
# Dogrulama asamasi (ornegin transfer sonrasi)
dogrula_checksum() {
local ARSIV=$1
local CHECKSUM=$2
if sha256sum -c "$CHECKSUM" &>> "$LOG"; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [OK] Checksum eslesiyor: $ARSIV" >> "$LOG"
return 0
else
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [KRITIK] Checksum UYUSMUYOR: $ARSIV" >> "$LOG"
return 1
fi
}
dogrula_checksum "$ARSIV" "$CHECKSUM_DOSYA"
SHA256 kullanımı önemli çünkü MD5 collision zayıflıkları nedeniyle güvenlik açısından artık önerilmiyor. Yedek bütünlüğü için bile olsa doğru araçları kullanmak alışkanlık haline gelmeli.
Gerçek Dünya Senaryosu: Kapsamlı Yedekleme ve Raporlama Scripti
Şimdiye kadar anlattıklarımı bir araya getirip, gerçek ortamlarda kullanabileceğiniz, modüler bir script yazalım. Bu script mail bildirimi, log rotasyonu ve durum raporu özelliklerine sahiptir.
#!/bin/bash
# yedekleme_kontrol.sh - Kapsamli yedekleme ve raporlama sistemi
# Kullanim: ./yedekleme_kontrol.sh [kaynak_dizin] [hedef_dizin]
set -euo pipefail
# Yapilandirma
KAYNAK="${1:-/var/www}"
HEDEF_DIZIN="${2:-/backup}"
TARIH=$(date +%Y%m%d_%H%M%S)
SUNUCU=$(hostname -s)
ARSIV_ADI="${SUNUCU}_yedek_${TARIH}.tar.gz"
ARSIV_YOLU="${HEDEF_DIZIN}/${ARSIV_ADI}"
LOG_DIZIN="/var/log/yedekleme"
LOG="${LOG_DIZIN}/yedek_${TARIH}.log"
DURUM_DOSYA="${LOG_DIZIN}/son_yedek_durumu.txt"
BILDIRIM_MAIL="[email protected]"
MAX_LOG_YASINDA_GUN=30
BASARILI=0
HATALAR=()
# Log fonksiyonu
log() {
local SEVIYE=$1
shift
local MESAJ="$*"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$SEVIYE] $MESAJ" | tee -a "$LOG"
}
# Hazirlik
mkdir -p "$LOG_DIZIN" "$HEDEF_DIZIN"
log "BILGI" "Yedekleme basliyor: $KAYNAK -> $ARSIV_YOLU"
# Disk alan kontrolu
BOSLUK=$(df -BG "$HEDEF_DIZIN" | awk 'NR==2 {gsub("G",""); print $4}')
KAYNAK_BOYUT=$(du -sG "$KAYNAK" | cut -f1)
if [ "$BOSLUK" -lt "$((KAYNAK_BOYUT * 2))" ]; then
log "UYARI" "Disk alani yetersiz olabilir. Bos: ${BOSLUK}G, Kaynak: ${KAYNAK_BOYUT}G"
HATALAR+=("Disk alani uyarisi")
fi
# Arşivleme
tar -czf "$ARSIV_YOLU"
--warning=no-file-changed
--exclude="*.tmp"
--exclude="*.log"
--exclude=".git"
"$KAYNAK" 2>> "$LOG"
TAR_EXIT=$?
if [ $TAR_EXIT -eq 0 ] || [ $TAR_EXIT -eq 1 ]; then
log "BILGI" "tar tamamlandi (exit: $TAR_EXIT)"
# Boyut kontrolu
ARSIV_BOYUT=$(stat -c%s "$ARSIV_YOLU" 2>/dev/null || echo 0)
if [ "$ARSIV_BOYUT" -lt 1024 ]; then
log "KRITIK" "Arsiv boyutu suphesel: $ARSIV_BOYUT byte"
HATALAR+=("Arsiv boyutu anormal kucuk")
fi
# Butunluk dogrulama
if gzip -t "$ARSIV_YOLU" 2>> "$LOG" &&
tar -tzf "$ARSIV_YOLU" &>> "$LOG"; then
DOSYA_SAYISI=$(tar -tzf "$ARSIV_YOLU" | wc -l)
sha256sum "$ARSIV_YOLU" > "${ARSIV_YOLU}.sha256"
log "BASARILI" "Dogrulama tamam. Dosya: $DOSYA_SAYISI, Boyut: $(du -sh $ARSIV_YOLU | cut -f1)"
BASARILI=1
else
log "KRITIK" "Arsiv dogrulama BASARISIZ"
HATALAR+=("Arsiv butunluk kontrolu basarisiz")
rm -f "$ARSIV_YOLU"
fi
else
log "KRITIK" "tar kritik hatayla cikti: exit $TAR_EXIT"
HATALAR+=("tar kritik hata: exit $TAR_EXIT")
fi
# Eski loglari temizle
find "$LOG_DIZIN" -name "*.log" -mtime +$MAX_LOG_YASINDA_GUN -delete 2>/dev/null
find "$HEDEF_DIZIN" -name "*.tar.gz" -mtime +$MAX_LOG_YASINDA_GUN -delete 2>/dev/null
# Durum dosyasini guncelle
{
echo "Son Yedek: $TARIH"
echo "Sunucu: $SUNUCU"
echo "Durum: $([ $BASARILI -eq 1 ] && echo 'BASARILI' || echo 'BASARISIZ')"
echo "Hata Sayisi: ${#HATALAR[@]}"
for hata in "${HATALAR[@]}"; do
echo " - $hata"
done
} > "$DURUM_DOSYA"
# Bildirim
if [ ${#HATALAR[@]} -gt 0 ]; then
HATA_LISTESI=$(printf '%sn' "${HATALAR[@]}")
mail -s "[KRITIK] $SUNUCU Yedekleme Basarisiz - $TARIH" "$BILDIRIM_MAIL" <<EOF
Sunucu: $SUNUCU
Tarih: $TARIH
Durum: BASARISIZ
Hatalar:
$HATA_LISTESI
Detay icin: $LOG
EOF
fi
exit $([ $BASARILI -eq 1 ] && echo 0 || echo 2)
Bu scriptte set -euo pipefail kullanımına dikkat edin. set -e herhangi bir komut başarısız olduğunda scripti durdurur, -u tanımsız değişken kullanımını engeller, -o pipefail ise pipe zincirinde herhangi bir komutun başarısız olmasını yakalar.
Log Analizi: Geçmiş Hataları Bulmak
Loglar birikince içlerinden anlamlı bilgi çıkarmak için birkaç pratik komut:
# Son 7 gundeki kritik hatalari bul
grep -h "[KRITIK]" /var/log/yedekleme/yedek_*.log |
awk '{print $1, $2, $4, $5}' |
sort | uniq -c | sort -rn | head -20
# Basarisiz yedekleri ozet olarak goster
grep -l "BASARISIZ|KRITIK" /var/log/yedekleme/yedek_*.log |
while read dosya; do
echo "=== $dosya ==="
grep -E "KRITIK|BASARISIZ|Exit code" "$dosya"
done
# Arsiv boyutlarinin zaman icindeki degisimini izle
find /backup -name "*.tar.gz" -printf "%T@ %s %fn" |
sort -n |
awk '{printf "%s - Boyut: %.2f MBn", strftime("%Y-%m-%d %H:%M",$1), $2/1048576}'
Bu komutlar, özellikle boyut anomalilerini tespit etmek için değerlidir. Bir arşiv aniden yüzde elliyse, büyük ihtimalle bir dizin silinmiş ya da bir bağlama noktası kaybolmuştur.
Cron Job Entegrasyonu ve Monitoring
Tüm bu yapıyı cron’a bağlamak ve monitoring sistemine entegre etmek için örnek bir yapı:
# /etc/cron.d/yedekleme
# Her gece saat 02:30'da calistir, cron mailini suppress et
30 2 * * * root /usr/local/bin/yedekleme_kontrol.sh /var/www /backup >> /var/log/yedekleme/cron.log 2>&1
# Sabah 07:00'de son durumu kontrol et ve rapor gonder
0 7 * * * root /usr/local/bin/yedek_rapor.sh
Raporlama scripti:
#!/bin/bash
# yedek_rapor.sh - Gunluk durum raporu
DURUM="/var/log/yedekleme/son_yedek_durumu.txt"
MAIL="[email protected]"
SUNUCU=$(hostname -s)
if [ ! -f "$DURUM" ]; then
echo "UYARI: Durum dosyasi bulunamadi, yedekleme hic calismamiş olabilir" |
mail -s "[UYARI] $SUNUCU - Yedek Durumu Belirsiz" "$MAIL"
exit 1
fi
# Son yedek ne zaman alindi?
SON_TARIH=$(grep "Son Yedek:" "$DURUM" | cut -d: -f2 | tr -d ' ')
SIMDI=$(date +%Y%m%d_%H%M%S)
# 25 saatten eski ise uyar (bir gun = 24 saat + 1 saat tolerans)
if [[ "$SON_TARIH" < "$(date -d '25 hours ago' +%Y%m%d_%H%M%S)" ]]; then
echo "Son yedek 25 saatten eski: $SON_TARIH" |
mail -s "[KRITIK] $SUNUCU - Yedek Cok Eski!" "$MAIL"
fi
# Gunluk ozet raporu gonder
mail -s "[BILGI] $SUNUCU - Gunluk Yedek Raporu" "$MAIL" < "$DURUM"
Sık Karşılaşılan Hatalar ve Çözümleri
Yıllardır bu işi yapanların mutlaka karşılaştığı durumlar şunlardır:
- “file changed as we read it” hatası: Aktif veritabanı dosyaları veya log dosyaları yedek alınırken değişir. Çözüm:
--warning=no-file-changedparametresi kullanmak ya da uygulamayı durdurarak veya snapshot alarak yedek almak.
- Sembolik link döngüleri:
tarsembolik link döngülerine girip sonsuza kadar çalışabilir.--no-recursionveya--excludeile belirli dizinleri hariç tutun.
- NFS/CIFS timeout hataları: Uzak dosya sistemi yedek sırasında kesilirse
tarsessizce yanlış bir arşiv oluşturabilir. Checksum kontrolü bu yüzden kritik.
- Sıfır boyutlu arşiv: Genellikle kaynak dizin boşken ya da izin hataları olduğunda oluşur. Boyut kontrolü her zaman yapın.
- Disk dolu hatası:
taryarım arşiv bırakır, exit code 2 döner ama bozuk dosya diskte kalır. Script sonunda başarısız arşivleri temizleyin.
Sonuç
Yedekleme, tamamlandı bildirimi alan bir cron job değildir. Doğrulanmış, loglanmış, raporlanmış ve düzenli olarak test edilmiş bir süreçtir. Burada anlattığım yaklaşımların özü şudur: her adımın çıktısını kaydet, her arşivi doğrula, anormalliği anında haber ver.
Özellikle checksum doğrulaması ve boyut anomali tespiti, pratikte en çok iş yapan kontroller. Gzip bütünlük testi ise hiç atlamayın; bozuk bir arşivi restore etmeye çalışmak yerine bunu erkenden tespit etmek, kriz anında saatlerce zaman kazandırır.
Sistemi kurduktan sonra yapmanız gereken son şey: Ayda en az bir kez gerçek bir restore testi yapmak. Çünkü alınan yedekler değil, başarıyla geri yüklenenler önemlidir.
