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-changed parametresi kullanmak ya da uygulamayı durdurarak veya snapshot alarak yedek almak.
  • Sembolik link döngüleri: tar sembolik link döngülerine girip sonsuza kadar çalışabilir. --no-recursion veya --exclude ile belirli dizinleri hariç tutun.
  • NFS/CIFS timeout hataları: Uzak dosya sistemi yedek sırasında kesilirse tar sessizce 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ı: tar yarı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.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir