Cron ile Dosya Senkronizasyonu: rsync Entegrasyonu

Sistem yöneticiliğinde en çok vakit kaybettiren şeylerden biri dosya senkronizasyonunu manuel yapmak zorunda kalmaktır. “Bugün yedek aldım mı?”, “Sunucular arasındaki farkı son ne zaman senkronladım?” gibi sorular aklınızın bir köşesini sürekli meşgul eder. İşte tam bu noktada cron ve rsync ikilisi devreye giriyor. Bu iki araç bir araya geldiğinde, neredeyse hiç bakım gerektirmeyen, sağlam ve güvenilir bir senkronizasyon altyapısı kurabilirsiniz. Bu yazıda sıfırdan başlayıp production ortamında kullanabileceğiniz bir yapıya kadar her şeyi ele alacağız.

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

rsync (remote sync), dosyaları hem yerel hem de uzak sistemler arasında senkronize etmek için kullanılan bir araçtır. Ama onu gerçekten güçlü yapan şey “delta transfer” algoritmasıdır. Yani her seferinde tüm dosyayı kopyalamak yerine, sadece değişen kısımları transfer eder. 10 GB’lık bir dosyanın son 1 MB’ı değiştiyse, rsync sadece o 1 MB’ı gönderir.

Temel özellikleri şunlar:

  • Artımlı transfer: Sadece değişen blokları kopyalar, bant genişliği tasarrufu sağlar
  • SSH desteği: Ağ üzerinden şifreli aktarım yapabilir
  • Dosya izinleri ve sahiplik: chmod, chown bilgilerini korur
  • Sembolik link desteği: Symlink’leri düzgün şekilde işler
  • Silme senkronizasyonu: Kaynaktan silinen dosyaları hedeften de silebilir
  • Bant genişliği limitleme: --bwlimit ile transfer hızını kısıtlayabilirsiniz

En sık kullanılan parametreler:

  • -a (–archive): Archive modu, -rlptgoD kombinasyonuna eşdeğerdir
  • -v (–verbose): Hangi dosyaların transfer edildiğini gösterir
  • -z (–compress): Transfer sırasında sıkıştırma uygular
  • -h (–human-readable): İnsan okunabilir format kullanır
  • -P: İlerleme göstergesi ve yarıda kalan transferleri devam ettirir
  • –delete: Kaynakta silinmiş dosyaları hedefe de siler
  • –exclude: Belirtilen dosya/dizinleri dışarıda bırakır
  • –dry-run (-n): Gerçekte kopyalamadan neyin değişeceğini gösterir
  • –bwlimit: KB/s cinsinden bant genişliği limiti belirler
  • –log-file: rsync çıktısını dosyaya yazar

Temel rsync Kullanımı

Cron entegrasyonuna geçmeden önce rsync’i iyi anlamak gerekiyor. Birkaç pratik örnekle başlayalım.

Yerel dizin senkronizasyonu:

# /var/www/html dizinini /backup/www dizinine senkronize et
rsync -avh /var/www/html/ /backup/www/

# Dikkat: Kaynak dizinin sonundaki / işareti önemli!
# /var/www/html/ -> dizinin içeriğini kopyalar
# /var/www/html  -> dizinin kendisini kopyalar (backup/www/html/ oluşturur)

Uzak sunucuya SSH üzerinden senkronizasyon:

# Yerel dizini uzak sunucuya kopyala
rsync -avzh -e "ssh -p 2222" /var/www/html/ [email protected]:/backup/www/

# Uzak sunucudan yerel makineye çek
rsync -avzh [email protected]:/var/www/html/ /local/backup/

Belirli dosyaları dışarıda bırakarak senkronizasyon:

rsync -avzh 
  --exclude='*.log' 
  --exclude='*.tmp' 
  --exclude='.git/' 
  --exclude='node_modules/' 
  /var/www/myapp/ user@backup-server:/backup/myapp/

SSH Anahtar Tabanlı Kimlik Doğrulama Kurulumu

Cron job’larda rsync kullanacaksanız, parola gerektirmeyen SSH key authentication şart. Cron arka planda çalışır ve interaktif parola girişi bekleyemez.

# Root kullanıcısı için SSH key oluştur (cron genellikle root olarak çalışır)
ssh-keygen -t ed25519 -C "cron-rsync-backup" -f /root/.ssh/rsync_key -N ""

# Public key'i hedef sunucuya kopyala
ssh-copy-id -i /root/.ssh/rsync_key.pub -p 2222 [email protected]

# Bağlantıyı test et
ssh -i /root/.ssh/rsync_key -p 2222 [email protected] "echo 'Baglanti basarili'"

SSH config dosyasına alias ekleyerek işleri kolaylaştırabiliriz:

# /root/.ssh/config dosyasına ekle
cat >> /root/.ssh/config << 'EOF'
Host backup-server
    HostName 192.168.1.100
    User backupuser
    Port 2222
    IdentityFile /root/.ssh/rsync_key
    StrictHostKeyChecking no
    ConnectTimeout 30
EOF

Artık rsync komutlarında backup-server kısa adını kullanabiliriz:

rsync -avzh /var/www/html/ backup-server:/backup/www/

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

Diyelim ki production web sunucunuzun /var/www dizinini her gece bir backup sunucusuna senkronize etmek istiyorsunuz. Önce sağlam bir script yazalım, sonra cron’a ekleyelim.

#!/bin/bash
# /usr/local/bin/web-backup.sh

# Konfigürasyon
SOURCE_DIR="/var/www/"
DEST_HOST="backup-server"
DEST_DIR="/backup/web-servers/prod-web01/"
LOG_FILE="/var/log/rsync-web-backup.log"
LOCK_FILE="/var/run/rsync-web-backup.lock"
ALERT_EMAIL="[email protected]"

# Log fonksiyonu
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}

# Lock dosyası kontrolü (aynı anda iki instance çalışmasın)
if [ -f "$LOCK_FILE" ]; then
    OLD_PID=$(cat "$LOCK_FILE")
    if kill -0 "$OLD_PID" 2>/dev/null; then
        log "HATA: Baska bir rsync sureci zaten calisiyor (PID: $OLD_PID). Cikiliyor."
        exit 1
    else
        log "UYARI: Eski lock dosyasi bulundu ama process yok. Temizleniyor."
        rm -f "$LOCK_FILE"
    fi
fi

# Lock dosyası oluştur
echo $$ > "$LOCK_FILE"
trap "rm -f $LOCK_FILE" EXIT

log "Web yedekleme basliyor..."
START_TIME=$(date +%s)

# rsync komutunu çalıştır
rsync -avzh 
    --delete 
    --exclude='*.log' 
    --exclude='*.tmp' 
    --exclude='.git/' 
    --exclude='node_modules/' 
    --exclude='*.sock' 
    --bwlimit=50000 
    --log-file="$LOG_FILE" 
    "$SOURCE_DIR" "${DEST_HOST}:${DEST_DIR}"

EXIT_CODE=$?
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))

if [ $EXIT_CODE -eq 0 ]; then
    log "Yedekleme basariyla tamamlandi. Sure: ${DURATION} saniye"
elif [ $EXIT_CODE -eq 24 ]; then
    # Exit code 24: Kaynak dosyalar transfer sirasinda silindi (genellikle normal)
    log "UYARI: Bazi kaynak dosyalar transfer sirasinda degisti/silindi (exit: 24)"
else
    log "HATA: rsync basarisiz! Exit kodu: $EXIT_CODE"
    echo "Web yedekleme BASARISIZ! Exit kodu: $EXIT_CODE - Detaylar: $LOG_FILE" | 
        mail -s "[KRITIK] Web Backup Hatasi - $(hostname)" "$ALERT_EMAIL"
fi

Script’i çalıştırılabilir yapın:

chmod +x /usr/local/bin/web-backup.sh

Cron’a ekleyin (her gece 02:30’da):

crontab -e
# Şu satırı ekleyin:
30 2 * * * /usr/local/bin/web-backup.sh

Gerçek Dünya Senaryosu 2: Çift Yönlü Senkronizasyon

Bazı senaryolarda iki sunucu arasında çift yönlü senkronizasyon gerekir. Örneğin iki ofis arasındaki ortak dosya alanı. rsync tek başına çift yönlü senkronizasyon yapmaz ama unison aracını ya da akıllıca yazılmış bir rsync scripti kullanabilirsiniz. Burada rsync ile basit bir yaklaşım gösterelim:

#!/bin/bash
# /usr/local/bin/bidirectional-sync.sh
# NOT: Bu yaklaşım basit senaryolar için uygundur.
# Aynı dosya her iki tarafta da değiştiyse çakışma olabilir.

SERVER_A_DIR="/shared/office-files/"
SERVER_B="office-istanbul"
SERVER_B_DIR="/shared/office-files/"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
LOG="/var/log/bidirectional-sync-${TIMESTAMP}.log"

echo "=== Senkronizasyon Basladi: $(date) ===" >> "$LOG"

# Adım 1: B'den A'ya yeni/değişen dosyaları çek
echo "B -> A senkronizasyonu..." >> "$LOG"
rsync -avzh --update 
    "${SERVER_B}:${SERVER_B_DIR}" 
    "$SERVER_A_DIR" >> "$LOG" 2>&1

# Adım 2: A'dan B'ye gönder
echo "A -> B senkronizasyonu..." >> "$LOG"
rsync -avzh --update 
    "$SERVER_A_DIR" 
    "${SERVER_B}:${SERVER_B_DIR}" >> "$LOG" 2>&1

echo "=== Senkronizasyon Tamamlandi: $(date) ===" >> "$LOG"

–update parametresi burada kritik: Bu parametre, hedefteki dosya kaynaktakinden daha yeniyse üzerine yazmaz. Böylece her iki tarafın da değişiklikleri korunur.

Gerçek Dünya Senaryosu 3: Artımlı Yedekleme ile Snapshot Sistemi

rsync ile --link-dest parametresini kullanarak disk alanından tasarruf eden snapshot tabanlı yedekleme yapabilirsiniz. Bu yöntemde her gün yeni bir klasör oluşur ama değişmeyen dosyalar hard link kullanır, yani disk alanı kaplamaz.

#!/bin/bash
# /usr/local/bin/snapshot-backup.sh

SOURCE="/var/www/html/"
BACKUP_ROOT="/backup/snapshots"
DATE=$(date +%Y-%m-%d)
CURRENT="${BACKUP_ROOT}/${DATE}"
LAST_BACKUP=$(ls -1d ${BACKUP_ROOT}/20* 2>/dev/null | tail -1)

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "/var/log/snapshot-backup.log"
}

# Bugünkü backup klasörü zaten varsa atla
if [ -d "$CURRENT" ]; then
    log "Bugunun yedeği zaten mevcut: $CURRENT"
    exit 0
fi

log "Snapshot yedeği basliyor: $CURRENT"

if [ -n "$LAST_BACKUP" ]; then
    log "Onceki yedege link: $LAST_BACKUP"
    rsync -avh 
        --delete 
        --link-dest="$LAST_BACKUP" 
        "$SOURCE" 
        "$CURRENT/"
else
    log "Ilk yedek, full kopyalama yapiliyor..."
    rsync -avh "$SOURCE" "$CURRENT/"
fi

EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
    log "Snapshot basariyla olusturuldu: $CURRENT"
    # 30 günden eski yedekleri temizle
    find "$BACKUP_ROOT" -maxdepth 1 -type d -name "20*" -mtime +30 -exec rm -rf {} ;
    log "30 gunluk eski yedekler temizlendi"
else
    log "HATA: Snapshot olusturulamadi! Kod: $EXIT_CODE"
fi

Bu script’i günlük çalıştırmak için cron ayarı:

# Her gün gece 01:00'de çalıştır
0 1 * * * /usr/local/bin/snapshot-backup.sh >> /var/log/snapshot-backup.log 2>&1

Cron Job Yönetimi ve Best Practice’ler

Crontab Sözdizimi Hatırlatıcısı

# ┌───────────── dakika (0 - 59)
# │ ┌─────────── saat (0 - 23)
# │ │ ┌───────── ayın günü (1 - 31)
# │ │ │ ┌─────── ay (1 - 12)
# │ │ │ │ ┌───── haftanın günü (0 - 7, 0 ve 7 Pazar)
# │ │ │ │ │
# * * * * * komut

# Örnekler:
0 * * * *       # Her saat başı
*/15 * * * *    # Her 15 dakikada bir
0 2 * * *       # Her gece 02:00
0 2 * * 0       # Her Pazar gece 02:00
0 2 1 * *       # Her ayın 1'i gece 02:00
0 2,14 * * *    # Her gün 02:00 ve 14:00

MAILTO ile Email Bildirimleri

# Crontab'ın en başına ekle
MAILTO="[email protected]"
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# Hata çıktısını ayrı dosyaya yönlendir
0 2 * * * /usr/local/bin/web-backup.sh >> /var/log/web-backup.log 2>&1

Cron Job Monitoring ile Sağlık Kontrolü

Production ortamında cron job’ların çalışıp çalışmadığını da izlemeniz gerekir. Basit bir “heartbeat” yaklaşımı:

#!/bin/bash
# /usr/local/bin/check-backup-health.sh
# Backup log dosyasını kontrol eder, son 25 saatte backup yapıldı mı diye bakar

LOG_FILE="/var/log/rsync-web-backup.log"
ALERT_EMAIL="[email protected]"
HOSTNAME=$(hostname)

# Son başarılı backup timestamp'ini bul
LAST_SUCCESS=$(grep "basariyla tamamlandi" "$LOG_FILE" | tail -1 | awk '{print $1, $2}')

if [ -z "$LAST_SUCCESS" ]; then
    echo "KRITIK: Hic basarili yedek bulunamadi!" | 
        mail -s "[ALARM] $HOSTNAME - Backup Durumu Bilinmiyor" "$ALERT_EMAIL"
    exit 1
fi

# Son başarılı backup'ın kaç saat önce olduğunu hesapla
LAST_EPOCH=$(date -d "$LAST_SUCCESS" +%s 2>/dev/null)
NOW_EPOCH=$(date +%s)
HOURS_AGO=$(( (NOW_EPOCH - LAST_EPOCH) / 3600 ))

if [ $HOURS_AGO -gt 25 ]; then
    echo "UYARI: Son basarili yedek ${HOURS_AGO} saat once yapildi! Kontrol gerekiyor." | 
        mail -s "[ALARM] $HOSTNAME - Backup ${HOURS_AGO} Saattir Yapilmadi" "$ALERT_EMAIL"
fi
# Sağlık kontrolünü her sabah 09:00'da çalıştır
0 9 * * * /usr/local/bin/check-backup-health.sh

Log Rotasyonu ile Düzenli Bakım

rsync logları zamanla büyüyebilir. logrotate ile bunu otomatize edin:

# /etc/logrotate.d/rsync-backup dosyası oluştur
cat > /etc/logrotate.d/rsync-backup << 'EOF'
/var/log/rsync-*.log {
    weekly
    rotate 8
    compress
    delaycompress
    missingok
    notifempty
    create 640 root adm
}

/var/log/snapshot-backup.log {
    monthly
    rotate 6
    compress
    missingok
    notifempty
}
EOF

Yaygın Hatalar ve Çözümleri

Problem: Cron çalışıyor ama rsync SSH bağlantısı reddediliyor.

Çözüm: Cron’un PATH ve environment değişkenleri terminal oturumunuzdan farklıdır. Script’in başına şunu ekleyin:

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

# SSH agent sorunlarını önlemek için
export SSH_AUTH_SOCK=""

Problem: rsync çalışıyor ama izinler bozuluyor.

# -a flag'i yerine sadece ihtiyacınız olan flag'leri kullanın
rsync -rlth --no-perms --no-owner --no-group 
    /source/ user@server:/dest/

Problem: Büyük dosyaları senkronize ederken bant genişliği dolup diğer servisler etkileniyor.

# Bant genişliğini 10 MB/s ile sınırla
rsync -avzh --bwlimit=10240 /source/ server:/dest/

# Veya nice ile işlemciye öncelik düşür
nice -n 19 rsync -avzh /source/ server:/dest/

# Her ikisini birlikte kullan
nice -n 19 ionice -c 3 rsync -avzh --bwlimit=10240 /source/ server:/dest/

Problem: --delete flag’i yanlışlıkla önemli dosyaları siliyor.

# Önce dry-run ile test et
rsync -avzh --delete --dry-run /source/ server:/dest/

# Silinen dosyaları backup'a taşı, tamamen silme
rsync -avzh --delete --backup --backup-dir=/backup/deleted-$(date +%Y%m%d) 
    /source/ server:/dest/

Systemd Timer Alternatifi

Modern Linux sistemlerde cron yerine systemd timer kullanmak bazı avantajlar sağlar: daha iyi logging, dependency yönetimi ve başarısız job’ları yeniden deneme.

# /etc/systemd/system/rsync-backup.service
cat > /etc/systemd/system/rsync-backup.service << 'EOF'
[Unit]
Description=rsync Web Backup Service
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/web-backup.sh
User=root
StandardOutput=journal
StandardError=journal
EOF

# /etc/systemd/system/rsync-backup.timer
cat > /etc/systemd/system/rsync-backup.timer << 'EOF'
[Unit]
Description=rsync Web Backup Timer
Requires=rsync-backup.service

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true

[Install]
WantedBy=timers.target
EOF

# Timer'ı aktif et
systemctl daemon-reload
systemctl enable --now rsync-backup.timer

# Durum kontrolü
systemctl list-timers rsync-backup.timer
journalctl -u rsync-backup.service -f

Persistent=true özelliği sayesinde sistem kapalıyken kaçırılan timer’lar, sistem açıldığında otomatik olarak çalışır. Bu cron’un yapamadığı önemli bir özelliktir.

Sonuç

rsync ve cron kombinasyonu, sistem yöneticisinin araç kutusundaki en değerli ikililerden biridir. Delta transfer algoritması sayesinde bant genişliğinden tasarruf ederken, cron’un zamanlama esnekliği ile istediğiniz sıklıkta ve saatte senkronizasyon yapabilirsiniz.

Bu yazıda anlattıklarımızı özetleyecek olursak:

  • Lock dosyaları ile paralel çalışmayı engelleyin
  • SSH key authentication olmadan cron ile rsync çalışmaz, bunu ilk adım olarak kurun
  • Her script’e anlamlı log yazma ekleyin, sorun çıktığında can kurtarır
  • --dry-run ile önce test edin, özellikle --delete kullanıyorsanız
  • Snapshot yedekleme için --link-dest parametresi disk alanı mucizesi yaratır
  • Cron’un kısıtlı environment’ını aklınızda tutun, PATH ve HOME değişkenlerini script içinde set edin
  • Kritik sistemlerde systemd timer’ı cron’a tercih edin

Sağlam bir senkronizasyon altyapısı bir kez kurulduğunda yıllarca sorunsuz çalışabilir. Ama “çalışıyor olmalı” yerine monitoring ve alerting ile bunu doğruladığınızdan emin olun. En iyi yedekleme sistemi, başarısız olduğunu fark ettiğiniz değil, başarılı olduğunu bildiğiniz sistemdir.

Yorum yapın