Yedekleme Scripti Yazma: tar Komutu ile Otomatik Yedekleme

Sistem yöneticiliğinde “yedek almak” deyince çoğu zaman aklımıza karmaşık yedekleme yazılımları, lisanslı araçlar ya da bulut çözümleri geliyor. Oysa Linux dünyasında onlarca yıldır hayatta olan tar komutu, doğru bir script altyapısıyla kurgulandığında kurumsal düzeyde son derece güvenilir bir yedekleme çözümüne dönüşebilir. Bu yazıda tar tabanlı yedekleme scriptleri yazacağız, gerçek dünya senaryolarını ele alacağız ve production ortamlarında kullanabileceğiniz, cron ile entegre edilmiş tam bir yedekleme altyapısı kuracağız.

tar Komutuna Hızlı Bakış

Önce temeli sağlam atalım. tar (Tape Archive) başlangıçta manyetik bantlara veri yazmak için tasarlanmıştı. Bugün ise dosya arşivlemenin ve yedeklemenin vazgeçilmez aracı olmaya devam ediyor.

En sık kullandığınız parametreler şunlar:

  • -c: Yeni arşiv oluştur (create)
  • -x: Arşivi aç (extract)
  • -t: Arşiv içeriğini listele (list)
  • -v: Ayrıntılı çıktı ver (verbose)
  • -f: Dosya adını belirt (file)
  • -z: gzip sıkıştırma kullan
  • -j: bzip2 sıkıştırma kullan
  • -J: xz sıkıştırma kullan
  • -p: Dosya izinlerini koru (preserve permissions)
  • –exclude: Belirtilen dizin veya dosyayı dışla
  • -C: Hedef dizini belirt (change directory)
  • –newer: Belirtilen tarihten sonra değişen dosyaları al

Basit bir yedekleme örneği:

tar -czf /backup/etc_yedek_$(date +%Y%m%d).tar.gz /etc/

Bu komut /etc dizinini alır, gzip ile sıkıştırır ve tarih damgalı bir isimle /backup altına yazar. Basit ama etkili. Şimdi bunu gerçek bir script altyapısına dönüştürelim.

Temel Yedekleme Scripti

İlk scriptimiz basit ama işlevsel olacak. Bir web sunucusunun konfigürasyon dosyalarını ve uygulama dizinini yedekleyen bir script yazıyoruz.

#!/bin/bash
# temel_yedek.sh - Basit yedekleme scripti

# Degiskenler
TARIH=$(date +%Y-%m-%d_%H-%M)
YEDEK_DIZIN="/backup/gunluk"
LOG_DOSYA="/var/log/yedekleme.log"
KAYNAK_DIZINLER=("/etc/nginx" "/var/www/html" "/etc/ssl")
YEDEK_SAKLAMA_GUN=7

# Renk kodlari
KIRMIZI='33[0;31m'
YESIL='33[0;32m'
SIFIRLA='33[0m'

# Log fonksiyonu
log_yaz() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_DOSYA"
}

# Yedek dizini yoksa olustur
mkdir -p "$YEDEK_DIZIN"

log_yaz "Yedekleme basliyor..."

# Her dizin icin yedek al
for DIZIN in "${KAYNAK_DIZINLER[@]}"; do
    if [ -d "$DIZIN" ]; then
        DIZIN_ADI=$(basename "$DIZIN")
        YEDEK_DOSYA="${YEDEK_DIZIN}/${DIZIN_ADI}_${TARIH}.tar.gz"
        
        tar -czpf "$YEDEK_DOSYA" "$DIZIN" 2>> "$LOG_DOSYA"
        
        if [ $? -eq 0 ]; then
            BOYUT=$(du -sh "$YEDEK_DOSYA" | cut -f1)
            log_yaz "BASARILI: $DIZIN -> $YEDEK_DOSYA ($BOYUT)"
        else
            log_yaz "HATA: $DIZIN yedeklenemedi!"
        fi
    else
        log_yaz "UYARI: $DIZIN dizini bulunamadi, atlaniyor."
    fi
done

# Eski yedekleri temizle
find "$YEDEK_DIZIN" -name "*.tar.gz" -mtime +$YEDEK_SAKLAMA_GUN -delete
log_yaz "7 gunden eski yedekler temizlendi."

log_yaz "Yedekleme tamamlandi."

Bu script birkaç önemli prensip uygular: değişkenleri üstte toplar, log yazar, hata kontrolü yapar ve eski yedekleri otomatik temizler.

Artımlı Yedekleme (Incremental Backup)

Her gün tam yedek almak disk alanını hızla tüketir. tar ile artımlı yedekleme yaparak sadece değişen dosyaları yedekleyebiliriz. --newer parametresi ya da --listed-incremental seçeneği bu iş için biçilmiş kaftan.

#!/bin/bash
# artimli_yedek.sh - Artimli yedekleme scripti

YEDEK_DIZIN="/backup"
SNAPSHOT_DOSYA="/backup/.snapshot"
TARIH=$(date +%Y%m%d)
GUN=$(date +%u)  # 1=Pazartesi, 7=Pazar

# Pazar gunu tam yedek, diger gunler artimli
if [ "$GUN" -eq 7 ]; then
    YEDEK_TURU="tam"
    # Snapshot dosyasini sifirla
    rm -f "$SNAPSHOT_DOSYA"
    YEDEK_DOSYA="${YEDEK_DIZIN}/tam_yedek_${TARIH}.tar.gz"
    echo "TAM YEDEK aliniyor..."
else
    YEDEK_TURU="artimli"
    YEDEK_DOSYA="${YEDEK_DIZIN}/artimli_yedek_${TARIH}.tar.gz"
    echo "ARTIMLI YEDEK aliniyor..."
fi

tar --listed-incremental="$SNAPSHOT_DOSYA" 
    -czpf "$YEDEK_DOSYA" 
    /var/www 
    /etc 
    2>/dev/null

echo "Yedek tamamlandi: $YEDEK_DOSYA"
echo "Boyut: $(du -sh $YEDEK_DOSYA | cut -f1)"

Artımlı yedekleri geri yüklerken dikkat etmeniz gereken kritik nokta: önce son tam yedeği açmanız, sonra sırasıyla artımlı yedekleri uygulamanız gerekir. Sırayı karıştırırsanız tutarsız bir veri durumuna düşersiniz.

Veritabanı Yedeklemesini Scripte Entegre Etme

Sadece dosya sistemi yedeklemek çoğu zaman yetmez. Uygulama verisi veritabanında yaşıyorsa, bunu da yedek scriptine dahil etmeliyiz.

#!/bin/bash
# tam_sistem_yedek.sh - Dosya + Veritabani yedekleme

TARIH=$(date +%Y-%m-%d_%H-%M)
YEDEK_DIZIN="/backup/$(date +%Y/%m)"
LOG="/var/log/tam_yedekleme.log"
GECICI_DIZIN="/tmp/yedek_gecici_$$"

# MySQL baglanti bilgileri
DB_KULLANICI="yedek_kullanici"
DB_SIFRE="guvenli_sifre"
DB_LISTESI=("uygulama_db" "kullanici_db" "log_db")

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG"
}

# Dizinleri hazirla
mkdir -p "$YEDEK_DIZIN" "$GECICI_DIZIN"

log "=== Yedekleme Basliyor: $TARIH ==="

# Veritabanlarini yedekle
log "Veritabani yedekleme basliyor..."
for DB in "${DB_LISTESI[@]}"; do
    DB_DUMP="${GECICI_DIZIN}/${DB}.sql"
    mysqldump -u"$DB_KULLANICI" -p"$DB_SIFRE" 
              --single-transaction 
              --routines 
              --triggers 
              "$DB" > "$DB_DUMP" 2>> "$LOG"
    
    if [ $? -eq 0 ]; then
        log "OK: $DB dump alindi ($(du -sh $DB_DUMP | cut -f1))"
    else
        log "HATA: $DB dump alinamadi!"
    fi
done

# Dosya sistemi + veritabani dumplarini tek arsivde birlestir
FINAL_ARSIV="${YEDEK_DIZIN}/tam_yedek_${TARIH}.tar.gz"

tar -czpf "$FINAL_ARSIV" 
    --exclude="/var/www/html/cache" 
    --exclude="/var/www/html/tmp" 
    --exclude="*.log" 
    -C / var/www/html 
    -C / etc/nginx 
    -C / etc/php 
    -C "$GECICI_DIZIN" . 
    2>> "$LOG"

CIKIS_KODU=$?

# Gecici dosyalari temizle
rm -rf "$GECICI_DIZIN"

if [ $CIKIS_KODU -eq 0 ]; then
    BOYUT=$(du -sh "$FINAL_ARSIV" | cut -f1)
    log "BASARILI: $FINAL_ARSIV ($BOYUT)"
else
    log "KRITIK HATA: Arsiv olusturulamadi!"
    exit 1
fi

log "=== Yedekleme Tamamlandi ==="

Yedek Doğrulama ve Bütünlük Kontrolü

Yedek aldınız, harika. Peki o yedeği geri yükleyebilecek misiniz? Yedeklerin düzenli olarak doğrulanması, felaket anında sürprizle karşılaşmamanızı sağlar.

#!/bin/bash
# yedek_dogrula.sh - Yedek butunlugunu test et

YEDEK_DIZIN="/backup"
LOG="/var/log/yedek_dogrulama.log"
HATA_SAYISI=0

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG"
}

log "Yedek dogrulama basliyor..."

# Son 24 saatte olusturulan yedekleri bul ve dogrula
find "$YEDEK_DIZIN" -name "*.tar.gz" -newer "$YEDEK_DIZIN/.son_dogrulama" 2>/dev/null | while read -r ARSIV; do
    log "Test ediliyor: $ARSIV"
    
    # Arsivi test et (acmadan icerik kontrolu)
    tar -tzf "$ARSIV" > /dev/null 2>&1
    
    if [ $? -eq 0 ]; then
        DOSYA_SAYISI=$(tar -tzf "$ARSIV" | wc -l)
        log "OK: $ARSIV - $DOSYA_SAYISI dosya iceriyor"
    else
        log "BOZUK ARSIV: $ARSIV dogrulanamadi!"
        HATA_SAYISI=$((HATA_SAYISI + 1))
        
        # Uyari maili gonder
        echo "UYARI: $ARSIV yedek dosyasi bozuk veya okunamaz!" | 
            mail -s "[KRITIK] Bozuk Yedek Tespit Edildi" [email protected]
    fi
done

# Son dogrulama zamanini guncelle
touch "$YEDEK_DIZIN/.son_dogrulama"

if [ $HATA_SAYISI -gt 0 ]; then
    log "DIKKAT: $HATA_SAYISI adet bozuk arsiv bulundu!"
    exit 1
else
    log "Tum yedekler basariyla dogrulandi."
fi

Uzak Sunucuya Güvenli Yedekleme

Yedeklerinizi sadece yerel diskte tutmak yetmez. 3-2-1 kuralı gereği en az bir kopyanın farklı bir konumda olması gerekir. rsync veya scp ile yedekleri uzak sunucuya aktarabiliriz.

#!/bin/bash
# uzak_yedek_aktar.sh - Yedekleri uzak sunucuya aktar

YEREL_YEDEK_DIZIN="/backup/gunluk"
UZAK_SUNUCU="backup-server.sirket.local"
UZAK_KULLANICI="yedek"
UZAK_DIZIN="/mnt/yedek_depo/$(hostname)"
SSH_ANAHTAR="/root/.ssh/yedek_rsa"
LOG="/var/log/uzak_yedek.log"
TARIH=$(date +%Y-%m-%d)

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG"
}

log "Uzak sunucuya aktarim basliyor: $UZAK_SUNUCU"

# Uzak dizini olustur (yoksa)
ssh -i "$SSH_ANAHTAR" -o StrictHostKeyChecking=no 
    "${UZAK_KULLANICI}@${UZAK_SUNUCU}" 
    "mkdir -p ${UZAK_DIZIN}/${TARIH}"

# rsync ile aktar (sadece degisenleri)
rsync -avz 
      --progress 
      -e "ssh -i $SSH_ANAHTAR -o StrictHostKeyChecking=no" 
      --delete-after 
      "${YEREL_YEDEK_DIZIN}/" 
      "${UZAK_KULLANICI}@${UZAK_SUNUCU}:${UZAK_DIZIN}/${TARIH}/"

RSYNC_SONUC=$?

if [ $RSYNC_SONUC -eq 0 ]; then
    log "Aktarim basarili: $UZAK_SUNUCU:$UZAK_DIZIN/$TARIH"
    
    # Uzaktaki eski yedekleri temizle (30 gunden eski)
    ssh -i "$SSH_ANAHTAR" "${UZAK_KULLANICI}@${UZAK_SUNUCU}" 
        "find ${UZAK_DIZIN} -maxdepth 1 -type d -mtime +30 -exec rm -rf {} ; 2>/dev/null"
    log "Uzaktaki 30 gunden eski yedekler temizlendi."
else
    log "HATA: Uzak aktarim basarisiz! rsync cikis kodu: $RSYNC_SONUC"
    exit 1
fi

Uzak sunucu aktarımlarında StrictHostKeyChecking=no kullanımı güvenlik riski taşır. Production ortamında bunu ayarlamak yerine ilk bağlantıda known_hosts dosyasına manuel olarak eklemenizi öneririm.

Cron ile Otomatik Zamanlama

Scriptlerimizi yazdık, şimdi bunları cron ile zamanlamak gerekiyor.

#!/bin/bash
# cron_kur.sh - Yedekleme cron gorevlerini ekle

# Root crontab'a ekle
crontab -l 2>/dev/null > /tmp/mevcut_cron

cat >> /tmp/mevcut_cron << 'CRON'
# Yedekleme Gorevleri
# Her gece 02:00'de tam yedek
0 2 * * * /opt/scripts/tam_sistem_yedek.sh >> /var/log/cron_yedek.log 2>&1

# Her gece 03:00'de artimli yedek (Pazar haric)
0 3 * * 1-6 /opt/scripts/artimli_yedek.sh >> /var/log/cron_yedek.log 2>&1

# Her sabah 06:00'da uzak aktarim
0 6 * * * /opt/scripts/uzak_yedek_aktar.sh >> /var/log/cron_uzak.log 2>&1

# Her sabah 07:00'da dogrulama
0 7 * * * /opt/scripts/yedek_dogrula.sh >> /var/log/cron_dogrulama.log 2>&1

# Her Pazar 08:00'de haftalik rapor
0 8 * * 7 /opt/scripts/yedek_rapor.sh | mail -s "Haftalik Yedek Raporu" [email protected]
CRON

crontab /tmp/mevcut_cron
rm /tmp/mevcut_cron
echo "Cron gorevleri eklendi."
crontab -l

Haftalık Raporlama Scripti

Yöneticiniz her Pazartesi sabahı “yedekler tamam mı?” diye soruyor olabilir. Bu rapor scripti o soruyu sorudan önce cevaplamanızı sağlar.

#!/bin/bash
# yedek_rapor.sh - Haftalik yedek durum raporu

YEDEK_DIZIN="/backup"
RAPOR_TARIH=$(date +"%d.%m.%Y")
SUNUCU=$(hostname -f)

echo "========================================"
echo "  HAFTALIK YEDEK RAPORU"
echo "  Sunucu: $SUNUCU"
echo "  Tarih: $RAPOR_TARIH"
echo "========================================"
echo ""

echo "## Son 7 Gunun Yedekleri:"
echo ""

find "$YEDEK_DIZIN" -name "*.tar.gz" -mtime -7 | sort | while read -r DOSYA; do
    BOYUT=$(du -sh "$DOSYA" | cut -f1)
    TARIH=$(stat -c %y "$DOSYA" | cut -d. -f1)
    
    # Arsiv saglikli mi kontrol et
    tar -tzf "$DOSYA" > /dev/null 2>&1
    if [ $? -eq 0 ]; then
        DURUM="[SAGLIKLI]"
    else
        DURUM="[BOZUK - DIKKAT!]"
    fi
    
    echo "  $DURUM $TARIH | Boyut: $BOYUT | $(basename $DOSYA)"
done

echo ""
echo "## Disk Kullanim Durumu:"
df -h "$YEDEK_DIZIN" | tail -1 | awk '{print "  Toplam: "$2" | Kullanilan: "$3" | Bos: "$4" | Doluluk: "$5}'

echo ""
echo "## Toplam Yedek Boyutu (7 gun):"
find "$YEDEK_DIZIN" -name "*.tar.gz" -mtime -7 -exec du -sh {} ; | 
    awk '{sum += $1} END {print "  " sum " MB"}'

echo ""
echo "========================================"
echo "Rapor otomatik olarak uretilmistir."

Yedekten Geri Yükleme

Yedek almak kadar, geri yükleyebilmek de önemli. İşte felaket anında kullanabileceğiniz geri yükleme scripti:

#!/bin/bash
# geri_yukle.sh - Yedekten geri yukleme

ARSIV=$1
HEDEF_DIZIN=${2:-"/"}

if [ -z "$ARSIV" ]; then
    echo "Kullanim: $0 <arsiv_dosyasi> [hedef_dizin]"
    echo "Ornek: $0 /backup/tam_yedek_2024-01-15.tar.gz /mnt/recover"
    exit 1
fi

if [ ! -f "$ARSIV" ]; then
    echo "HATA: $ARSIV dosyasi bulunamadi!"
    exit 1
fi

echo "Arsiv dogrulanıyor: $ARSIV"
tar -tzf "$ARSIV" > /dev/null 2>&1

if [ $? -ne 0 ]; then
    echo "HATA: Arsiv bozuk veya okunamaz!"
    exit 1
fi

echo ""
echo "UYARI: Asagidaki arsiv $HEDEF_DIZIN altina acilacak:"
echo "  Arsiv: $ARSIV"
echo "  Hedef: $HEDEF_DIZIN"
echo "  Icerik: $(tar -tzf $ARSIV | wc -l) dosya"
echo ""
read -p "Devam etmek istiyor musunuz? (evet/hayir): " ONAY

if [ "$ONAY" != "evet" ]; then
    echo "Islem iptal edildi."
    exit 0
fi

mkdir -p "$HEDEF_DIZIN"

echo "Geri yukleme basliyor..."
tar -xzpf "$ARSIV" -C "$HEDEF_DIZIN" --same-owner

if [ $? -eq 0 ]; then
    echo "Geri yukleme BASARILI: $HEDEF_DIZIN"
else
    echo "HATA: Geri yukleme sirasinda sorun olustu!"
    exit 1
fi

Gerçek Dünya Notları ve İnce Noktalar

Yıllarca production ortamlarında çalışırken öğrendiğim bazı kritik noktalar var:

Yedek alırken dosya değişirse ne olur? Özellikle veritabanı dosyaları veya aktif olarak yazılan log dosyaları için tar bazen “file changed as we read it” uyarısı verir. Bu durumda --warning=no-file-changed parametresi uyarıyı bastırır ama asıl çözüm veritabanlarını dump alarak yedeklemektir.

Sembolik bağlantıları korumak için -p parametresiyle birlikte --dereference veya varsayılan semlink davranışını kullanın. Sunucuyu başka bir makineye restore ettiğinizde semlink hedefleri farklı olabilir.

Büyük arşivleri bölerek yedeklemek için split komutuyla birlikte kullanabilirsiniz:

# 4GB parcalara bolerek yedekle
tar -czp /var/www | split -b 4G - "/backup/buyuk_yedek_$(date +%Y%m%d).tar.gz.part"

# Geri birlestirmek icin
cat /backup/buyuk_yedek_20240115.tar.gz.part* | tar -xz -C /hedef

Yedek script’lerini her zaman set -e ile başlatmanızı önermiyorum çünkü bir dizin bulunamazsa script tamamen durur. Bunun yerine $? ile her komutun dönüş kodunu kontrol edin ve hata yönetimini kendiniz yapın.

İzin sorunları en sık karşılaşılan problemlerin başında gelir. Script’lerinizi mutlaka root ile ya da yedeklenecek dizinlerde okuma iznine sahip bir kullanıcıyla çalıştırın. Cron’daki bir yedekleme görevi izin hatasıyla sessizce başarısız olabilir ve log’lara bakmadan fark edemezsiniz.

Ayrıca tar arşivlerini şifrelemek için OpenSSL ile birleştirebilirsiniz:

# Sifreli yedek olustur
tar -czp /etc | openssl enc -aes-256-cbc -salt -k "guclu_sifre" > /backup/sifreli_yedek.tar.gz.enc

# Sifreli yedegi ac
openssl enc -d -aes-256-cbc -k "guclu_sifre" -in /backup/sifreli_yedek.tar.gz.enc | tar -xz -C /hedef

Sonuç

tar ile yedekleme yazmak ilk bakışta basit görünse de, güvenilir bir yedekleme altyapısı kurmak dikkat ve deneyim ister. Bu yazıda ele aldığımız scriptler production’da kullanıma hazır temel bileşenler sunuyor: tam ve artımlı yedekleme, veritabanı entegrasyonu, bütünlük doğrulama, uzak sunucuya aktarım ve otomatik raporlama.

En önemli nokta şu: Aldığınız yedeği mutlaka test edin. Geri yükleme yapmadığınız bir yedek, yedek değildir. En az ayda bir kez test ortamında geri yükleme deneyin. Cron görevlerinin çalışıp çalışmadığını log’lardan takip edin. Disk doluluk oranlarını izleyin. Yedek almak işin yüzde kırkı, geri kalanı izleme ve doğrulama.

Bu scriptleri kendi ortamınıza göre uyarlamaktan çekinmeyin. Değişken isimlerini, dizin yollarını ve zamanlamaları ihtiyacınıza göre düzenleyin. Soru veya öneri varsa yorumlarda buluşalım.

Yorum yapın