Rsync ile Cron Otomasyonu: Zamanli Yedekleme Nasil Yapilir?

Yedekleme konusuna gelince, sistem yöneticilerinin büyük çoğunluğu “bir gün ayarlarım” diyerek erteleyip durur. Ta ki o korkunç an gelene kadar: disk çöktü, veri gitti, patron karşında. İşte tam bu yüzden otomatik yedekleme kurgusu kurmak, her sysadmin’in yapılacaklar listesinin en üstünde olmalı. Bu yazıda rsync ile cron’u birleştirerek gerçekten çalışan, güvenilir ve esnek bir yedekleme sistemi nasıl kurulur, adım adım ele alacağız.

Rsync Nedir ve Neden Bu Kadar Güçlü?

Rsync, “remote sync” kelimelerinden türetilmiş, dosya ve dizin senkronizasyonu için kullanılan bir araç. Basit görünse de altında ciddi bir algoritma yatıyor. Delta transfer algoritması sayesinde sadece değişen blokları kopyalıyor, bu da hem bant genişliği hem de zaman tasarrufu sağlıyor.

Birkaç temel özelliğinden bahsedelim:

  • Delta transfer: Sadece değişen kısımları kopyalar, tüm dosyayı baştan almaz
  • SSH desteği: Şifreli tünel üzerinden güvenli kopyalama yapabilir
  • Bant genişliği limiti: --bwlimit ile transfer hızını kısıtlayabilirsin
  • Checksum doğrulama: Tarih/boyut yerine gerçek içerik karşılaştırması
  • Partial transfer: Yarıda kesen transferleri devam ettirebilir
  • Hard link desteği: Incremental yedeklemelerde disk alanını verimli kullanır

Temel Rsync Kullanımı

Önce temel sözdizimini anlayalım:

rsync [seçenekler] kaynak hedef

En sık kullandığım temel parametreler:

  • -a: Archive modu, izinleri, zaman damgalarını, sembolik linkleri korur
  • -v: Verbose, ne yapıldığını ekrana basar
  • -z: Sıkıştırma uygular, uzak sunucular için faydalı
  • -h: Human-readable, dosya boyutlarını okunabilir formatta gösterir
  • -P: İlerleme çubuğu gösterir ve yarım kalan transferleri devam ettirir
  • –delete: Kaynakta olmayan dosyaları hedeften siler
  • –exclude: Belirtilen dosya/dizinleri yedek dışı bırakır
  • –link-dest: Incremental yedekleme için önceki yedeğe hard link kurar
  • –dry-run: Gerçekten çalıştırmadan ne yapacağını gösterir

Basit bir yerel yedekleme örneği:

rsync -avh /home/kullanici/belgeler/ /mnt/yedek/belgeler/

Uzak sunucuya yedekleme:

rsync -avzh /var/www/html/ [email protected]:/backup/web/

SSH Anahtarı ile Şifresiz Bağlantı

Cron ile otomasyona geçmeden önce şifresiz SSH bağlantısını kurmak şart. Cron işleri arka planda çalışır, şifre soramazlar.

# SSH anahtar çifti oluştur
ssh-keygen -t ed25519 -C "yedekleme-anahtari" -f ~/.ssh/yedekleme_key

# Public key'i hedef sunucuya kopyala
ssh-copy-id -i ~/.ssh/yedekleme_key.pub [email protected]

# Bağlantıyı test et
ssh -i ~/.ssh/yedekleme_key [email protected] "echo Baglanti basarili"

SSH config dosyasına da ekleyebilirsin, scriptleri daha temiz tutar:

# ~/.ssh/config dosyasına ekle
Host yedek-sunucu
    HostName 192.168.1.100
    User yedekleme-kullanici
    IdentityFile ~/.ssh/yedekleme_key
    StrictHostKeyChecking no

Artık [email protected] yerine sadece yedek-sunucu yazman yeterli.

Gerçek Dünya Senaryosu 1: Web Sunucusu Yedekleme Scripti

Diyelim ki bir e-ticaret sitesi işletiyorsun. /var/www/html altında web dosyaları var, MySQL veritabanı var, log dosyaları var. Bunları her gece otomatik olarak uzak bir sunucuya yedeklemek istiyorsun.

#!/bin/bash
# /usr/local/bin/web_yedekle.sh

# Değişkenler
KAYNAK="/var/www/html/"
HEDEF="yedek-sunucu:/backup/web/"
LOG_DOSYA="/var/log/rsync_yedek.log"
TARIH=$(date '+%Y-%m-%d %H:%M:%S')
HATA_MAILI="[email protected]"

# Log fonksiyonu
log() {
    echo "[$TARIH] $1" >> "$LOG_DOSYA"
}

log "Yedekleme basliyor..."

# Rsync komutu
rsync -avzh 
    --delete 
    --exclude="*.log" 
    --exclude="*.tmp" 
    --exclude=".git/" 
    --exclude="node_modules/" 
    --exclude="cache/" 
    --stats 
    "$KAYNAK" "$HEDEF" >> "$LOG_DOSYA" 2>&1

# Çıkış kodunu kontrol et
if [ $? -eq 0 ]; then
    log "Yedekleme basariyla tamamlandi."
else
    log "HATA: Yedekleme basarisiz oldu!"
    echo "Web sunucusu yedeklemesi basarisiz! Log: $LOG_DOSYA" | 
        mail -s "KRITIK: Yedekleme Hatasi" "$HATA_MAILI"
fi

Script’e çalışma izni ver:

chmod +x /usr/local/bin/web_yedekle.sh

# Önce dry-run ile test et
rsync -avzh --dry-run --exclude="*.log" /var/www/html/ yedek-sunucu:/backup/web/

Incremental Yedekleme: Hard Link Yöntemi

Günlük tam yedekleme disk alanını hızla tüketir. Incremental yedekleme ile her gün sadece değişen dosyalar kopyalanır, ama her yedek sanki tam yedekmişçesine görünür. Bunu rsync’in --link-dest parametresi ile yapıyoruz.

Mantığı şu: Yeni yedek klasörü oluştur, değişmeyen dosyalar için önceki yedeğe hard link kur. Değişen dosyaları gerçekten kopyala. Bu sayede her gün tarihli bir klasörün var, ama disk alanı sadece değişen dosyalar kadar artıyor.

#!/bin/bash
# /usr/local/bin/incremental_yedekle.sh

KAYNAK="/home/"
YEDEK_DIZIN="/mnt/nas/yedekler"
TARIH=$(date '+%Y-%m-%d')
ONCEKI_YEDEK=$(ls -1d "$YEDEK_DIZIN"/????-??-?? 2>/dev/null | tail -n 1)

# Bugünün yedek klasörünü oluştur
BUGUN_YEDEK="$YEDEK_DIZIN/$TARIH"
mkdir -p "$BUGUN_YEDEK"

if [ -n "$ONCEKI_YEDEK" ]; then
    echo "Incremental yedekleme: $ONCEKI_YEDEK referans aliniyor"
    rsync -avh 
        --delete 
        --link-dest="$ONCEKI_YEDEK" 
        "$KAYNAK" "$BUGUN_YEDEK/" 
        >> "/var/log/incremental_yedek.log" 2>&1
else
    echo "Ilk tam yedekleme yapiliyor..."
    rsync -avh 
        "$KAYNAK" "$BUGUN_YEDEK/" 
        >> "/var/log/incremental_yedek.log" 2>&1
fi

echo "Yedekleme tamamlandi: $BUGUN_YEDEK"

# 30 günden eski yedekleri temizle
find "$YEDEK_DIZIN" -maxdepth 1 -type d -name "????-??-??" 
    -mtime +30 -exec rm -rf {} ;
echo "30 gun eski yedekler temizlendi."

Bu yöntemin güzelliği şu: 2 aylık yedek tutsan bile disk kullanımı çok makul kalır çünkü hard link’ler aynı disk bloklarını paylaşır.

Cron ile Zamanlama

Artık script’lerimiz hazır. Cron ile bunları zamanlayalım. Crontab sözdizimini bir hatırlayalım:

* * * * * komut
| | | | |
| | | | +-- Haftanın günü (0-7, 0 ve 7 Pazar)
| | | +---- Ay (1-12)
| | +------ Ayın günü (1-31)
| +-------- Saat (0-23)
+---------- Dakika (0-59)

Crontab dosyasını düzenlemek için:

# Root olarak crontab düzenle
crontab -e

# Belirli kullanıcı için
crontab -u www-data -e

# Mevcut crontab'ı görüntüle
crontab -l

Gerçek dünyadan cron zamanlaması örnekleri:

# Her gece 02:30'da web yedeklemesi
30 2 * * * /usr/local/bin/web_yedekle.sh

# Her saat başı database dump + rsync
0 * * * * /usr/local/bin/db_yedekle.sh

# Hafta içi her gün 03:00'da incremental yedek
0 3 * * 1-5 /usr/local/bin/incremental_yedekle.sh

# Pazar 04:00'da tam yedekleme
0 4 * * 0 /usr/local/bin/tam_yedekleme.sh

# Her 6 saatte bir kritik config dosyaları yedeği
0 */6 * * * rsync -avz /etc/ yedek-sunucu:/backup/etc/

Gerçek Dünya Senaryosu 2: Veritabanı Dump + Rsync

Veritabanını doğrudan rsync ile kopyalamak mantıklı değil, çalışan bir DB’nin dosyalarını rsync’lemek tutarsız veriye yol açar. Önce dump al, sonra rsync ile taşı:

#!/bin/bash
# /usr/local/bin/db_yedekle.sh

DB_KULLANICI="root"
DB_SIFRE="gizli_sifre_buraya"
DUMP_DIZIN="/tmp/db_yedek"
HEDEF="yedek-sunucu:/backup/database/"
TARIH=$(date '+%Y%m%d_%H%M%S')
LOG="/var/log/db_yedek.log"

mkdir -p "$DUMP_DIZIN"

echo "[$(date)] Database dump basliyor..." >> "$LOG"

# Tüm veritabanlarını dump et
mysqldump 
    -u "$DB_KULLANICI" 
    -p"$DB_SIFRE" 
    --all-databases 
    --single-transaction 
    --routines 
    --triggers 
    | gzip > "$DUMP_DIZIN/all_databases_$TARIH.sql.gz"

if [ $? -ne 0 ]; then
    echo "[$(date)] HATA: mysqldump basarisiz!" >> "$LOG"
    exit 1
fi

echo "[$(date)] Dump tamamlandi, rsync basliyor..." >> "$LOG"

# Rsync ile uzak sunucuya gönder
rsync -avzh 
    --remove-source-files 
    "$DUMP_DIZIN/" "$HEDEF" >> "$LOG" 2>&1

# Uzak sunucuda 7 günden eski dump'ları temizle
ssh yedek-sunucu "find /backup/database/ -name '*.sql.gz' -mtime +7 -delete"

echo "[$(date)] DB yedeklemesi tamamlandi." >> "$LOG"

--remove-source-files parametresine dikkat: Rsync başarıyla aktardıktan sonra yerel dosyaları siliyor. /tmp alanını doldurmuyoruz bu sayede.

Log Yönetimi ve Bildirimleri

İyi bir yedekleme sistemi sessizce çalışır, ama bir şeyler ters gittiğinde seni haberdar eder. Logrotate ile log dosyalarını yönetelim:

# /etc/logrotate.d/rsync-yedek dosyasını oluştur
cat > /etc/logrotate.d/rsync-yedek << 'EOF'
/var/log/rsync_yedek.log
/var/log/db_yedek.log
/var/log/incremental_yedek.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    dateext
}
EOF

Yedekleme durumunu merkezi olarak takip etmek için basit bir kontrol scripti:

#!/bin/bash
# /usr/local/bin/yedek_kontrol.sh
# Her sabah 08:00'da çalışır, geceki yedekleri raporlar

RAPOR=""
HATA_VAR=0

kontrol_et() {
    local dosya="$1"
    local tanim="$2"
    local max_saat="$3"

    if [ ! -f "$dosya" ]; then
        RAPOR+="HATA: $tanim log dosyasi bulunamadi!n"
        HATA_VAR=1
        return
    fi

    # Son değişiklik zamanını kontrol et (saat cinsinden)
    GECEN_SAAT=$(( ($(date +%s) - $(stat -c %Y "$dosya")) / 3600 ))

    if [ "$GECEN_SAAT" -gt "$max_saat" ]; then
        RAPOR+="UYARI: $tanim son $GECEN_SAAT saat once calismiş!n"
        HATA_VAR=1
    else
        RAPOR+="OK: $tanim ($GECEN_SAAT saat once)n"
    fi
}

kontrol_et "/var/log/rsync_yedek.log" "Web Yedekleme" 25
kontrol_et "/var/log/db_yedek.log" "DB Yedekleme" 13
kontrol_et "/var/log/incremental_yedek.log" "Incremental Yedek" 25

echo -e "$RAPOR"

if [ "$HATA_VAR" -eq 1 ]; then
    echo -e "YEDEKLEME SORUNLARI TESPIT EDILDI:nn$RAPOR" | 
        mail -s "[UYARI] Yedekleme Durum Raporu" [email protected]
fi

Bant Genişliği Kontrolü

Yedekleme trafiği üretim trafiğini boğmamalı. Özellikle gündüz saatlerinde çalışan script’ler için bant genişliği limiti şart:

# Maksimum 10 MB/s ile yedekle
rsync -avzh --bwlimit=10240 /var/www/ yedek-sunucu:/backup/web/

# Daha dinamik bir yaklaşım: saate göre limit belirle
SAAT=$(date +%H)

if [ "$SAAT" -ge 8 ] && [ "$SAAT" -lt 18 ]; then
    # Mesai saatlerinde 2 MB/s
    LIMIT="--bwlimit=2048"
else
    # Gece sınırsız
    LIMIT=""
fi

rsync -avzh $LIMIT /veri/ yedek-sunucu:/backup/veri/

Exclude Dosyası ile Temiz Yönetim

Script içine çok fazla --exclude parametresi doldurmak yerine, ayrı bir dosya tutmak çok daha temiz:

# /etc/rsync/web_exclude.txt
*.log
*.tmp
*.swp
*.cache
.git/
.svn/
node_modules/
vendor/
__pycache__/
*.pyc
.DS_Store
Thumbs.db
/var/www/html/temp/
/var/www/html/uploads/tmp/

Sonra script’te bu dosyayı referans alırsın:

rsync -avzh 
    --exclude-from="/etc/rsync/web_exclude.txt" 
    --delete 
    /var/www/html/ yedek-sunucu:/backup/web/

Yedek Doğrulama: En Unutulan Adım

Yedek almak yeterli değil, o yedeğin çalışıp çalışmadığını test etmek gerekiyor. Çoğu sysadmin bu adımı atlar, ta ki felaket gününe kadar.

#!/bin/bash
# /usr/local/bin/yedek_dogrula.sh
# Haftalık çalışır, rastgele dosyaları karşılaştırır

KAYNAK="/var/www/html"
YEDEK="yedek-sunucu:/backup/web"
GECICI="/tmp/yedek_dogrulama"
HATA_SAYISI=0

mkdir -p "$GECICI"

echo "Yedek dogrulama basliyor: $(date)"

# Kaynaktan 10 rastgele dosya seç
DOSYALAR=$(find "$KAYNAK" -type f -name "*.php" | shuf -n 10)

for DOSYA in $DOSYALAR; do
    GORELI_YOL="${DOSYA#$KAYNAK/}"

    # Yedeği geçici konuma çek
    rsync -ah "yedek-sunucu:/backup/web/$GORELI_YOL" "$GECICI/test_dosya" 2>/dev/null

    if [ $? -ne 0 ]; then
        echo "HATA: $GORELI_YOL yedekte bulunamadi!"
        HATA_SAYISI=$((HATA_SAYISI + 1))
        continue
    fi

    # MD5 karşılaştır
    KAYNAK_MD5=$(md5sum "$DOSYA" | awk '{print $1}')
    YEDEK_MD5=$(md5sum "$GECICI/test_dosya" | awk '{print $1}')

    if [ "$KAYNAK_MD5" != "$YEDEK_MD5" ]; then
        echo "UYARI: $GORELI_YOL - MD5 uyumsuzlugu!"
        HATA_SAYISI=$((HATA_SAYISI + 1))
    else
        echo "OK: $GORELI_YOL"
    fi

    rm -f "$GECICI/test_dosya"
done

rm -rf "$GECICI"

echo "Dogrulama tamamlandi. Hata: $HATA_SAYISI"

if [ "$HATA_SAYISI" -gt 0 ]; then
    echo "$HATA_SAYISI dosyada sorun tespit edildi!" | 
        mail -s "Yedek Dogrulama Hatasi" [email protected]
fi

Bu script’i cron’a ekleyebilirsin, haftalık çalışır:

# Her Pazar 09:00'da yedek doğrulama
0 9 * * 0 /usr/local/bin/yedek_dogrula.sh >> /var/log/yedek_dogrulama.log 2>&1

Sık Karşılaşılan Sorunlar ve Çözümleri

Cron’da script çalışıyor ama rsync bağlanamıyor:

Cron ortamı shell ortamından farklıdır. PATH, SSH_AUTH_SOCK gibi değişkenler tanımlı olmayabilir. Script başına şunu ekle:

#!/bin/bash
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
export HOME="/root"

“Permission denied” hatası:

Rsync’i root olarak mı çalıştırıyorsun? Root’un SSH key’i /root/.ssh/ altında olmalı. Yedekleme için ayrı bir düşük yetkili kullanıcı oluşturmak daha güvenli bir yaklaşım.

Disk alanı doldu:

--delete parametresini kullanmıyorsanız, kaynaktan silinen dosyalar hedefte birikir. Zaman zaman elle kontrol edin ya da script’e disk alanı kontrolü ekleyin:

BOSTA=$(df /mnt/yedek | awk 'NR==2 {print $5}' | tr -d '%')
if [ "$BOSTA" -gt 85 ]; then
    echo "UYARI: Yedek diski %$BOSTA dolu!" | mail -s "Disk Uyarisi" [email protected]
fi

Güvenlik Notları

  • Yedekleme kullanıcısını minimum yetkiyle oluştur, root SSH erişimi verme
  • SSH anahtarlarını authorized_keys dosyasında command= kısıtlamasıyla sınırlandır
  • Yedeklerin kendisini de yedekle, 3-2-1 kuralını uygula: 3 kopya, 2 farklı medya, 1 uzak lokasyon
  • Rsync üzerinden gelen şifreli verileri sakla, açık metin DB şifrelerini script içine gömmekten kaçın, environment variable veya şifreli vault kullan
  • Yedek sunucusuna sadece push değil, pull yöntemi de düşün; kaynak sunucu ele geçirilse bile yedekleri silemez

Sonuç

Rsync ve cron kombinasyonu, kurumsal lisanslı yedekleme yazılımlarına para harcamadan ciddiye alınabilecek bir yedekleme altyapısı kurmana olanak veriyor. Burada anlattığım yaklaşımları kendi ortamına göre adapte etmen gerekecek; disk boyutları, ağ kapasitesi, veri hassasiyeti ve recovery time objective (RTO) hedeflerin farklılık gösterecektir.

Ama temel prensip hep aynı: Test etmediğin yedek, yedek değildir. Script’leri kur, logları izle, arada bir restore testi yap. O “bir gün çökürsek ne yaparız” sorusunun cevabı hazır olsun. Sistemlerin sağlam, yedekleriniz güncel olsun.

Bir yanıt yazın

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