Artımlı Yedekleme Stratejisi: rsync ile Verimli Yedek Alma

Yedekleme konusu sysadmin’lerin en çok “keşke daha önce yapsaydım” dediği konuların başında gelir. Diskler patlar, kullanıcılar yanlışlıkla dosya siler, fidye yazılımları şifreler. O an geldiğinde elinde düzgün bir yedek yoksa gece uyuyamazsın. Bu yazıda rsync ile artımlı yedekleme stratejisini, gerçek dünya senaryolarıyla birlikte ele alacağız.

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

rsync, kaynak ve hedef arasındaki farkları tespit edip yalnızca değişen blokları transfer eden bir senkronizasyon aracıdır. Tam yedeklemede her seferinde tüm veriyi kopyalarsın. rsync ise “bu dosya zaten orada var mı, değişti mi?” diye kontrol eder. Değişmediyse dokunmaz, değiştiyse sadece değişen kısımları gönderir. Bant genişliği ve disk alanı açısından bu fark devasa olabilir.

Delta transfer algoritması rsync’in kalbidir. Büyük bir dosyanın küçük bir kısmı değişmişse rsync tüm dosyayı yeniden göndermek yerine yalnızca değişen bloğu gönderir. 50 GB’lık bir veritabanı dump dosyasında küçük bir değişiklik varsa bu fark hayat kurtarır.

rsync’in temel çalışma modları şöyle özetlenebilir:

  • Yerel mod: Aynı makine üzerinde dizin kopyalama
  • Uzak shell modu: SSH üzerinden uzak sunucuya transfer
  • rsync daemon modu: rsync protokolü üzerinden direkt bağlantı

Artımlı Yedekleme Mantığı

Artımlı yedekleme stratejisinde temel fikir şudur: Her gün tüm veriyi tekrar kopyalamak yerine, son yedekten bu yana değişen dosyaları kopyalarsın. Ancak rsync ile bunu bir adım daha ileri taşıyabiliriz. Hard link tabanlı artımlı yedekleme, hem disk alanı tasarrufu sağlar hem de her yedek noktasının tam bir anlık görüntü gibi görünmesini mümkün kılar.

Mantık şöyle işler: Bir dosya iki farklı yedek noktasında değişmemişse, disk üzerinde sadece bir kez saklanır ama her iki yedek dizininden de erişilebilir. --link-dest parametresi tam olarak bunu yapar.

Temel rsync Parametreleri

Sık kullandığım ve bilmeni gereken parametreler:

  • -a (–archive): Archive modu. İzinleri, sahipliği, zaman damgasını, sembolik linkleri ve alt dizinleri korur
  • -v (–verbose): İşlem sırasında ne yapıldığını gösterir
  • -z (–compress): Transfer sırasında sıkıştırma uygular, yavaş bağlantılarda işe yarar
  • -P: --partial ve --progress kombinasyonu, büyük dosyalarda yarım kalan transferi devam ettirir
  • –delete: Kaynaktan silinen dosyaları hedefte de siler
  • –link-dest=DIR: Belirtilen dizindeki değişmeyen dosyalar için hard link oluşturur
  • –exclude: Belirli dosya veya dizinleri dışlar
  • –bwlimit=KBPS: Bant genişliği limitler
  • -n (–dry-run): Gerçekten kopyalamadan ne yapacağını gösterir, test için şart
  • –stats: Transfer istatistiklerini gösterir
  • -e: Kullanılacak uzak shell’i belirtir, genellikle SSH için

İlk Kurulum: Basit Bir Yerel Yedekleme

Her şeyden önce basit bir örnekle başlayalım. /home/kullanici dizinini /backup/home altına yedekleyelim:

rsync -avz --delete /home/kullanici/ /backup/home/

Burada dikkat edilmesi gereken kritik nokta: /home/kullanici/ sondaki slash ile /home/kullanici arasındaki fark. Slash varsa dizinin içeriği kopyalanır, yoksa dizinin kendisi kopyalanır. Bu küçük fark yedek yapısını tamamen değiştirir.

Dry-run ile önce ne olacağını test et:

rsync -avzn --delete /home/kullanici/ /backup/home/

SSH Üzerinden Uzak Sunucu Yedekleme

Gerçek dünyada yedekleri başka bir sunucuya göndermen gerekir. Yerel disk yedekleri diskle birlikte gider. SSH ile uzak sunucuya yedek göndermek:

rsync -avz -e "ssh -p 2222 -i /root/.ssh/backup_key" 
    --delete 
    /var/www/html/ 
    [email protected]:/backup/web/html/

SSH anahtarı kurulumu için önce anahtar çifti oluşturalım:

# Backup için ayrı bir SSH anahtarı oluştur
ssh-keygen -t ed25519 -f /root/.ssh/backup_key -C "backup-script" -N ""

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

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

Hedef sunucuda bu backup kullanıcısının yetkilerini kısıtlamak iyi bir pratiktir. ~/.ssh/authorized_keys dosyasına anahtarı eklerken başına kısıtlamalar ekleyebilirsin:

# Hedef sunucunun authorized_keys dosyasina ekle
command="/usr/bin/rsync --server --daemon .",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-ed25519 AAAA...

Hard Link Tabanlı Artımlı Yedekleme Scripti

İşte asıl konuya geldik. Bu yöntem Time Machine benzeri bir yapı oluşturur. Her gün tam bir anlık görüntü gibi görünen ama gerçekte yalnızca değişen dosyaların kopyalandığı yedekler alırsın.

#!/bin/bash

# Konfigürasyon
SOURCE="/var/www"
BACKUP_ROOT="/backup/web"
REMOTE_USER="backup_user"
REMOTE_HOST="192.168.1.100"
REMOTE_BACKUP_ROOT="/backup/web"
SSH_KEY="/root/.ssh/backup_key"
LOG_FILE="/var/log/rsync_backup.log"
DATE=$(date +%Y-%m-%d_%H-%M-%S)
BACKUP_DEST="${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_BACKUP_ROOT}/${DATE}"
LINK_DEST="${REMOTE_BACKUP_ROOT}/latest"

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

log "Yedekleme basladi: $DATE"

# rsync komutunu calistir
rsync -avz 
    --delete 
    --link-dest="${LINK_DEST}" 
    -e "ssh -i ${SSH_KEY} -o StrictHostKeyChecking=no" 
    --exclude="*.tmp" 
    --exclude="*.log" 
    --exclude=".git" 
    --exclude="node_modules/" 
    --stats 
    "${SOURCE}/" 
    "${BACKUP_DEST}/"

RSYNC_EXIT=$?

if [ $RSYNC_EXIT -eq 0 ]; then
    log "rsync basariyla tamamlandi"
    
    # latest sembolik linkini guncelle
    ssh -i "${SSH_KEY}" "${REMOTE_USER}@${REMOTE_HOST}" 
        "ln -snf ${REMOTE_BACKUP_ROOT}/${DATE} ${LINK_DEST}"
    
    log "Latest link guncellendi: ${DATE}"
else
    log "HATA: rsync basarisiz oldu. Exit kodu: $RSYNC_EXIT"
    exit 1
fi

log "Yedekleme tamamlandi"

Bu scriptte --link-dest parametresi latest sembolik linkine bakarak önceki yedeği referans alır. Değişmeyen dosyalar hard link olarak oluşturulur, yani disk üzerinde ek yer kaplamaz.

Eski Yedekleri Temizleme

Yedekler birikirken disk alanı tükenebilir. Eski yedekleri otomatik temizleyen bir fonksiyon ekleyelim:

#!/bin/bash

# Eski yedekleri temizle - 30 gundan eski olanlari sil
REMOTE_USER="backup_user"
REMOTE_HOST="192.168.1.100"
SSH_KEY="/root/.ssh/backup_key"
REMOTE_BACKUP_ROOT="/backup/web"
RETENTION_DAYS=30
LOG_FILE="/var/log/rsync_backup.log"

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

# Uzak sunucuda eski yedekleri bul ve sil
ssh -i "${SSH_KEY}" "${REMOTE_USER}@${REMOTE_HOST}" bash << EOF
    cd "${REMOTE_BACKUP_ROOT}"
    
    # 30 gundan eski backup dizinlerini bul ve sil
    find . -maxdepth 1 -type d -name "20*" -mtime +${RETENTION_DAYS} | while read dir; do
        echo "Siliniyor: $dir"
        rm -rf "$dir"
    done
    
    # Kac yedek kaldi
    REMAINING=$(find . -maxdepth 1 -type d -name "20*" | wc -l)
    echo "Kalan yedek sayisi: $REMAINING"
EOF

log "Eski yedek temizleme tamamlandi"

Farklı Senaryo: MySQL Veritabanı Yedekleme

Çalışan bir MySQL veritabanını rsync ile direkt kopyalamak tehlikelidir. Önce dump almalı, sonra rsync ile transfer etmelisin:

#!/bin/bash

DB_USER="root"
DB_PASS="supersecretpassword"
DB_NAME="uretim_db"
DUMP_DIR="/tmp/db_backup"
BACKUP_DEST="[email protected]:/backup/database"
SSH_KEY="/root/.ssh/backup_key"
DATE=$(date +%Y-%m-%d_%H-%M-%S)
LOG_FILE="/var/log/db_backup.log"

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

# Dump dizini olustur
mkdir -p "${DUMP_DIR}"

log "MySQL dump alinıyor: ${DB_NAME}"

# Gzipli dump al
mysqldump 
    -u "${DB_USER}" 
    -p"${DB_PASS}" 
    --single-transaction 
    --routines 
    --triggers 
    --events 
    "${DB_NAME}" | gzip > "${DUMP_DIR}/${DB_NAME}_${DATE}.sql.gz"

if [ $? -ne 0 ]; then
    log "HATA: MySQL dump basarisiz"
    exit 1
fi

log "Dump boyutu: $(du -sh ${DUMP_DIR}/${DB_NAME}_${DATE}.sql.gz | cut -f1)"

# rsync ile uzak sunucuya gonder
log "Transfer baslıyor..."
rsync -avz 
    --link-dest="/backup/database/latest" 
    -e "ssh -i ${SSH_KEY}" 
    "${DUMP_DIR}/" 
    "${BACKUP_DEST}/${DATE}/"

if [ $? -eq 0 ]; then
    log "Transfer tamamlandi"
    
    # Latest guncelle
    ssh -i "${SSH_KEY}" [email protected] 
        "ln -snf /backup/database/${DATE} /backup/database/latest"
    
    # Yerel gecici dosyalari temizle
    rm -f "${DUMP_DIR}/${DB_NAME}_${DATE}.sql.gz"
    log "Yerel gecici dosyalar temizlendi"
else
    log "HATA: rsync transfer basarisiz"
    exit 1
fi

--single-transaction parametresi InnoDB tabloları için kilitlemeden tutarlı bir dump almanı sağlar. Üretim ortamında bu kritik öneme sahiptir.

Cron ile Otomatikleştirme

Scriptleri yazdık, şimdi bunları otomatik çalıştıralım:

# Crontab düzenle
crontab -e

# Gunluk tam yedek - her gece 02:00'de
0 2 * * * /usr/local/bin/rsync_backup.sh >> /var/log/rsync_backup.log 2>&1

# Saatlik artimli yedek - her saat basi
0 * * * * /usr/local/bin/rsync_incremental.sh >> /var/log/rsync_incremental.log 2>&1

# Haftalik temizlik - Pazar gunu 04:00'de
0 4 * * 0 /usr/local/bin/backup_cleanup.sh >> /var/log/backup_cleanup.log 2>&1

# DB yedegi - her gece 01:00'de
0 1 * * * /usr/local/bin/db_backup.sh >> /var/log/db_backup.log 2>&1

Cron’dan çalışan scriptlerde PATH genellikle kısıtlıdır. Script başına şunu ekle:

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

Yedek Bütünlüğünü Kontrol Etme

Yedek aldın güzel, peki o yedek gerçekten çalışıyor mu? Felaket anında bozuk bir yedek keşfetmek çok acı verir. Basit bir doğrulama scripti:

#!/bin/bash

REMOTE_USER="backup_user"
REMOTE_HOST="192.168.1.100"
SSH_KEY="/root/.ssh/backup_key"
REMOTE_BACKUP_ROOT="/backup/web"
ALERT_EMAIL="[email protected]"
LOG_FILE="/var/log/backup_verify.log"

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

# Son yedek dizinini bul
LATEST_BACKUP=$(ssh -i "${SSH_KEY}" "${REMOTE_USER}@${REMOTE_HOST}" 
    "ls -t ${REMOTE_BACKUP_ROOT} | grep -v latest | head -1")

if [ -z "$LATEST_BACKUP" ]; then
    log "KRITIK HATA: Hic yedek bulunamadi!"
    echo "KRITIK: Yedek sunucusunda yedek dizini bulunamadi!" | 
        mail -s "YEDEK ALARM: ${REMOTE_HOST}" "${ALERT_EMAIL}"
    exit 1
fi

log "Son yedek: $LATEST_BACKUP"

# Son yedegin boyutunu kontrol et
BACKUP_SIZE=$(ssh -i "${SSH_KEY}" "${REMOTE_USER}@${REMOTE_HOST}" 
    "du -sh ${REMOTE_BACKUP_ROOT}/${LATEST_BACKUP} | cut -f1")

log "Son yedek boyutu: $BACKUP_SIZE"

# Son yedegin kac saat once alindigini kontrol et
BACKUP_AGE=$(ssh -i "${SSH_KEY}" "${REMOTE_USER}@${REMOTE_HOST}" 
    "find ${REMOTE_BACKUP_ROOT}/${LATEST_BACKUP} -maxdepth 0 -mmin +1440 | wc -l")

if [ "$BACKUP_AGE" -gt 0 ]; then
    log "UYARI: Son yedek 24 saatten eski!"
    echo "UYARI: ${REMOTE_HOST} icin son yedek 24 saatten eski! Son yedek: ${LATEST_BACKUP}" | 
        mail -s "YEDEK UYARI: Eski Yedek" "${ALERT_EMAIL}"
fi

log "Dogrulama tamamlandi"

Bandwidth Throttling: Üretim Ortamında Dikkat

Üretim sunucusunda gündüz saatlerinde büyük bir rsync transferi başlatırsan network’ü doldurabilirsin. --bwlimit parametresi bu sorunu çözer:

# Bant genişligini 10 MB/s ile sinirla
rsync -avz 
    --bwlimit=10240 
    --delete 
    -e "ssh -i /root/.ssh/backup_key" 
    /var/www/ 
    [email protected]:/backup/web/

Değer KB/s cinsinden girilir. 10240 KB/s = 10 MB/s demek. Gece vakti daha yüksek limit, gündüz düşük limit için cron’a iki ayrı zamanlama ekleyebilirsin.

Exclude Dosyası Kullanımı

Dışlama kuralları çoğaldığında bunları ayrı bir dosyada tutmak daha temiz olur:

# /etc/rsync_excludes.txt dosyasi
*.tmp
*.log
*.pid
*.sock
.git/
.svn/
node_modules/
vendor/
__pycache__/
*.pyc
.DS_Store
Thumbs.db
/tmp/
/proc/
/sys/
/dev/
/run/

Bu dosyayı kullan:

rsync -avz 
    --exclude-from="/etc/rsync_excludes.txt" 
    --delete 
    -e "ssh -i /root/.ssh/backup_key" 
    / 
    [email protected]:/backup/full-system/

Sistem yedeklemesinde /proc, /sys, /dev gibi sanal dosya sistemlerini mutlaka dışla. Bunları yedeklemeye çalışırsan hem anlamsız hem de sorun çıkarır.

Gerçek Dünya Senaryosu: E-ticaret Sitesi Yedekleme

Bir e-ticaret firmasında sysadmin olduğunu düşün. Üretimde şunlar var: Web sunucusu, MySQL veritabanı, yüklenen görseller ve konfigürasyon dosyaları. İşte kapsamlı bir strateji:

Saatlik: Yüklenen görseller ve kullanıcı dosyaları rsync ile artımlı yedekleme Günlük: MySQL dump + tüm web root + konfigürasyonlar Haftalık: Tüm sistemin tam yedeklenmesi, uzak lokasyona gönderme Aylık: Soğuk depolama arşivi

Bu katmanlı strateji hem RTO (kurtarma süresi hedefi) hem de RPO (kurtarma noktası hedefi) açısından makul değerler verir. Saatlik yedekle en fazla bir saatlik veri kaybı yaşarsın.

Rsync Daemon Modu ile Daha Hızlı Transfer

Çok büyük hacimli transferlerde SSH overhead sorun olabilir. rsync daemon modu daha hızlı çalışır ama güvenlik duvarı ve ağ tasarımını dikkatli yapman gerekir. Hedef sunucuda /etc/rsyncd.conf:

# /etc/rsyncd.conf
uid = backup_user
gid = backup_group
use chroot = yes
max connections = 4
log file = /var/log/rsyncd.log
pid file = /var/run/rsyncd.pid

[web-backup]
    path = /backup/web
    comment = Web Backup Area
    read only = no
    list = no
    auth users = rsyncbackup
    secrets file = /etc/rsyncd.secrets
    hosts allow = 10.0.1.50
    hosts deny = *

/etc/rsyncd.secrets dosyası:

rsyncbackup:guclu_bir_sifre
chmod 600 /etc/rsyncd.secrets

İstemci tarafında kullanım:

rsync -avz --password-file=/etc/rsync.password 
    /var/www/ 
    [email protected]::web-backup/

Monitoring ve Alerting

Yedek scriptlerin çalışıp çalışmadığını izlemek için basit bir kontrol mekanizması. Her başarılı yedek sonrası bir “heartbeat” dosyası oluştur:

# Backup script sonuna ekle
echo "$(date)" > /backup/status/last_successful_backup

Monitoring tarafında bu dosyayı kontrol et. Prometheus, Nagios, Zabbix veya basit bir cron job ile kontrol edebilirsin. Birçok ekip Slack veya Telegram’a bildirim gönderen basit scriptler kullanır. Karmaşık monitoring kurmadan önce bile bu basit yaklaşım seni haberdar eder.

Sonuç

rsync ile artımlı yedekleme stratejisi kurmanın teknik engeli düşük ama disiplini yüksektir. Scripti yazmak bir günde biter, asıl mesele bunu düzenli test etmek, logları izlemek ve yedekten geri dönüş tatbikatı yapmaktır.

Özetle dikkat etmen gereken noktalar:

  • –link-dest kullan: Disk alanı tasarrufu için hard link tabanlı artımlı yedekleme şart
  • SSH anahtarı ile çalış: Şifre kullanma, scriptlerde şifre güvenlik açığıdır
  • Dry-run ile test et: Scripti canlıya almadan önce -n ile ne yapacağını gör
  • Yedek bütünlüğünü doğrula: Sadece yedek aldım demek yetmez, çalışıp çalışmadığını test et
  • Retention politikası koy: Sonsuz yedek almak disk dolana kadar güzel görünür
  • 3-2-1 kuralına uy: 3 kopya, 2 farklı ortam, 1 uzak lokasyon
  • Tatbikat yap: Yılda en az bir kez yedekten tam sistem restore dene

Felaket planı kağıt üzerinde güzel görünen ama hiç test edilmeyen bir belgeden ibaret olmamalı. rsync scriptlerini kur, cron’a ekle, logları izle ve geri dönüş prosedürünü belgele. O gün geldiğinde sakin kafayla hareket edebilirsin.

Bir yanıt yazın

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