Cron ile Otomatik Yedekleme Görevi Tanımlama

Sunucular uyurken bile sistemin çalışıyor olması gerekir. Yedekleme işlemleri de tam olarak bu kategoriye girer: gece 3’te başlayıp kimseyi rahatsız etmeden tamamlanan, sessiz ve güvenilir bir süreç. Cron, Unix/Linux dünyasının bu sessiz kahramanıdır ve doğru yapılandırıldığında yedekleme görevlerini mükemmel şekilde yönetir. Bu yazıda gerçek dünya senaryolarıyla cron tabanlı yedekleme sistemleri kuracağız.

Cron Nedir ve Nasıl Çalışır?

Cron, zamana bağlı görevleri otomatik olarak çalıştıran bir daemon servisidir. Linux sistemlerde crond ya da cron adıyla çalışır. Her kullanıcının kendi crontab dosyası olabilir ve sistem genelinde /etc/cron.d/, /etc/cron.daily/, /etc/cron.weekly/ gibi dizinler mevcuttur.

Cron ifadelerinin anatomisini anlamadan sağlıklı bir zamanlama yapılandırması kurmak mümkün değil. Bir cron satırı şu beş alandan oluşur:

# ┌───────────── 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)
# │ │ │ │ │
# * * * * * çalıştırılacak_komut

Sık kullanılan zaman ifadeleri:

  • 0 2 *: Her gün saat 02:00’de
  • 0 2 0: Her Pazar saat 02:00’de
  • 0 2 1 : Her ayın 1’inde saat 02:00’de
  • /15 *: Her 15 dakikada bir
  • 0 2,14 *: Her gün saat 02:00 ve 14:00’de
  • @daily: Her gün gece yarısı (0 0 * ile eşdeğer)
  • @weekly: Her hafta Pazar gece yarısı
  • @reboot: Sistem başlangıcında bir kez

Crontab ile Çalışmak

Mevcut kullanıcının crontab dosyasını düzenlemek için:

crontab -e

Mevcut crontab içeriğini listelemek için:

crontab -l

Belirli bir kullanıcının crontab’ını root olarak düzenlemek için:

crontab -u www-data -e

Crontab dosyasını silmek için:

crontab -r

Sistem genelinde geçerli olması gereken görevler için /etc/cron.d/ altına dosya oluşturabilirsiniz. Bu dosyaların formatı biraz farklıdır, kullanıcı adı da belirtilmesi gerekir:

# /etc/cron.d/yedekleme dosyası
0 2 * * * root /usr/local/bin/yedekle.sh >> /var/log/yedekleme.log 2>&1

Temel Yedekleme Script’i

Önce basit ama işlevsel bir yedekleme scripti yazalım. Bu script, belirli dizinleri tar ile sıkıştırıp tarihli bir arşiv oluşturur.

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

# Yapılandırma değişkenleri
KAYNAK_DIZIN="/var/www/html /etc /home"
HEDEF_DIZIN="/backup/gunluk"
LOG_DOSYASI="/var/log/yedekleme.log"
TARIH=$(date +%Y%m%d_%H%M%S)
SUNUCU_ADI=$(hostname -s)
SAKLAMA_SURESI=7  # Gün cinsinden

# Hedef dizin yoksa oluştur
mkdir -p "$HEDEF_DIZIN"

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

log "Yedekleme başlıyor..."

# Yedekleme al
ARSIV_ADI="${HEDEF_DIZIN}/${SUNUCU_ADI}_${TARIH}.tar.gz"

if tar -czf "$ARSIV_ADI" $KAYNAK_DIZIN 2>> "$LOG_DOSYASI"; then
    BOYUT=$(du -sh "$ARSIV_ADI" | cut -f1)
    log "Yedekleme başarılı: $ARSIV_ADI ($BOYUT)"
else
    log "HATA: Yedekleme başarısız oldu!"
    exit 1
fi

# Eski yedekleri temizle
log "Eski yedekler temizleniyor (${SAKLAMA_SURESI} günden eski)..."
find "$HEDEF_DIZIN" -name "*.tar.gz" -mtime +$SAKLAMA_SURESI -delete
log "Temizlik tamamlandı."

log "Yedekleme işlemi tamamlandı."

Script’e çalıştırma izni vermeyi unutmayın:

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

MySQL/MariaDB Veritabanı Yedekleme

Web uygulamaları için veritabanı yedeklemesi kritik önem taşır. Sadece dosya yedeklemek yetmez, veritabanını da kapsamanız gerekir.

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

# Veritabanı yapılandırması
DB_HOST="localhost"
DB_USER="yedekleme_kullanici"
DB_PASS="guclu_sifre_buraya"
DB_LISTESI="uygulama_db musteri_db rapor_db"
HEDEF_DIZIN="/backup/veritabani"
TARIH=$(date +%Y%m%d_%H%M%S)
LOG_DOSYASI="/var/log/db_yedekleme.log"
SAKLAMA_SURESI=14

mkdir -p "$HEDEF_DIZIN"

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

BASARILI=0
BASARISIZ=0

for DB in $DB_LISTESI; do
    CIKTI_DOSYASI="${HEDEF_DIZIN}/${DB}_${TARIH}.sql.gz"
    
    log "Yedekleniyor: $DB"
    
    if mysqldump 
        -h "$DB_HOST" 
        -u "$DB_USER" 
        -p"$DB_PASS" 
        --single-transaction 
        --routines 
        --triggers 
        --events 
        "$DB" | gzip > "$CIKTI_DOSYASI" 2>> "$LOG_DOSYASI"; then
        
        BOYUT=$(du -sh "$CIKTI_DOSYASI" | cut -f1)
        log "Başarılı: $DB ($BOYUT)"
        BASARILI=$((BASARILI + 1))
    else
        log "HATA: $DB yedeklenemedi!"
        BASARISIZ=$((BASARISIZ + 1))
    fi
done

# Eski yedekleri temizle
find "$HEDEF_DIZIN" -name "*.sql.gz" -mtime +$SAKLAMA_SURESI -delete

log "Özet: $BASARILI başarılı, $BASARISIZ başarısız"

Veritabanı yedekleme kullanıcısına sadece gerekli izinleri verin, asla root kullanmayın:

CREATE USER 'yedekleme_kullanici'@'localhost' IDENTIFIED BY 'guclu_sifre_buraya';
GRANT SELECT, SHOW VIEW, TRIGGER, LOCK TABLES, RELOAD, REPLICATION CLIENT ON *.* TO 'yedekleme_kullanici'@'localhost';
FLUSH PRIVILEGES;

E-posta Bildirimi ile Yedekleme Takibi

Yedekleme çalıştı mı, başarılı mı oldu? Sabah işe geldiğinizde bunu bilmek istersiniz. Cron varsayılan olarak komutun çıktısını MAILTO değişkenine e-posta gönderir. Bunu yapılandıralım:

# Crontab içinde e-posta bildirimi
MAILTO="[email protected]"
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# Her gece 02:30'da dosya yedekleme
30 2 * * * /usr/local/bin/yedekle.sh

# Her gece 03:00'de veritabanı yedekleme
0 3 * * * /usr/local/bin/db_yedekle.sh

Ancak daha gelişmiş bildirimler için script içinde e-posta gönderebilirsiniz. mailutils ya da sendmail kurulu olduğunu varsayalım:

#!/bin/bash
# Yedekleme sonrası bildirim scripti

HEDEF_DIZIN="/backup/gunluk"
MAIL_ALICI="[email protected]"
SUNUCU_ADI=$(hostname -f)
TARIH=$(date '+%Y-%m-%d %H:%M:%S')

# Yedekleme script'ini çalıştır ve çıktıyı yakala
CIKTI=$(/usr/local/bin/yedekle.sh 2>&1)
CIKIS_KODU=$?

# Disk kullanımını hesapla
DISK_KULLANIM=$(df -h "$HEDEF_DIZIN" | tail -1 | awk '{print $5}')
BACKUP_BOYUT=$(du -sh "$HEDEF_DIZIN" | cut -f1)

if [ $CIKIS_KODU -eq 0 ]; then
    DURUM="BAŞARILI"
    KONU="[OK] $SUNUCU_ADI Yedekleme Raporu - $TARIH"
else
    DURUM="BAŞARISIZ"
    KONU="[HATA] $SUNUCU_ADI Yedekleme BAŞARISIZ - $TARIH"
fi

# E-posta gönder
{
    echo "Yedekleme Durumu: $DURUM"
    echo "Sunucu: $SUNUCU_ADI"
    echo "Tarih: $TARIH"
    echo "Backup Dizin Boyutu: $BACKUP_BOYUT"
    echo "Disk Doluluk Oranı: $DISK_KULLANIM"
    echo ""
    echo "---- Script Çıktısı ----"
    echo "$CIKTI"
} | mail -s "$KONU" "$MAIL_ALICI"

Uzak Sunucuya Rsync ile Yedekleme

Yedeklerinizi aynı sunucuda tutmak yeterince güvenli değil. Sunucu yanarsa yedekler de gider. Rsync ile uzak bir sunucuya yedek aktaralım:

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

UZAK_SUNUCU="yedek-sunucu.sirket.com"
UZAK_KULLANICI="backup"
UZAK_DIZIN="/backup/$(hostname -s)"
KAYNAK_DIZINLER="/var/www /etc /home /backup/veritabani"
SSH_ANAHTAR="/root/.ssh/backup_rsa"
LOG_DOSYASI="/var/log/uzak_yedekleme.log"
BANT_GENISLIGI="50000"  # KB/s cinsinden, 0 = sınırsız

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

log "Uzak yedekleme başlıyor: $UZAK_SUNUCU"

# Önce uzak dizinin varlığını kontrol et
ssh -i "$SSH_ANAHTAR" -o ConnectTimeout=10 
    "${UZAK_KULLANICI}@${UZAK_SUNUCU}" 
    "mkdir -p $UZAK_DIZIN" 2>> "$LOG_DOSYASI"

if [ $? -ne 0 ]; then
    log "HATA: Uzak sunucuya bağlanılamadı!"
    exit 1
fi

# Rsync ile senkronize et
rsync -avz 
    --delete 
    --bwlimit="$BANT_GENISLIGI" 
    --log-file="$LOG_DOSYASI" 
    -e "ssh -i $SSH_ANAHTAR -o StrictHostKeyChecking=no" 
    $KAYNAK_DIZINLER 
    "${UZAK_KULLANICI}@${UZAK_SUNUCU}:${UZAK_DIZIN}/"

if [ $? -eq 0 ]; then
    log "Uzak yedekleme başarıyla tamamlandı."
else
    log "HATA: Uzak yedekleme başarısız!"
    exit 1
fi

SSH anahtarı oluşturma ve yetkisiz erişimi engellemek için:

# Yedekleme için özel SSH anahtarı oluştur
ssh-keygen -t rsa -b 4096 -f /root/.ssh/backup_rsa -N ""

# Public key'i uzak sunucuya kopyala
ssh-copy-id -i /root/.ssh/backup_rsa.pub [email protected]

# Uzak sunucuda ~/.ssh/authorized_keys dosyasına kısıtlama ekle
# (Uzak sunucuda düzenlenir)
# command="/usr/bin/rsync --server --daemon .",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-rsa AAAA...

Gelişmiş Crontab Yapılandırması

Gerçek bir üretim ortamında birden fazla yedekleme görevini koordineli çalıştırmanız gerekir. İşte kapsamlı bir crontab örneği:

# /etc/cron.d/yedekleme-gorevleri
# Sysadmin: [email protected] | Son güncelleme: 2024-01-15

SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO="[email protected]"

# -----------------------------------------------
# GÜNLÜK YEDEKLEMELER
# -----------------------------------------------

# Veritabanı yedekleme - her gece 01:00
0 1 * * * root /usr/local/bin/db_yedekle.sh >> /var/log/db_yedekleme.log 2>&1

# Dosya sistemi yedekleme - her gece 02:00
0 2 * * * root /usr/local/bin/yedekle.sh >> /var/log/yedekleme.log 2>&1

# Uzak sunucuya aktarım - her gece 04:00 (yedekleme bittikten sonra)
0 4 * * * root /usr/local/bin/uzak_yedekle.sh >> /var/log/uzak_yedekleme.log 2>&1

# -----------------------------------------------
# HAFTALIK YEDEKLEMELER
# -----------------------------------------------

# Tam sistem arşivi - Pazar gece 03:00
0 3 * * 0 root /usr/local/bin/haftalik_yedekle.sh >> /var/log/haftalik_yedekleme.log 2>&1

# -----------------------------------------------
# AYLIK YEDEKLEMELER
# -----------------------------------------------

# Arşiv yedekleme (uzun süreli saklama) - Ayın 1'i 05:00
0 5 1 * * root /usr/local/bin/aylik_arsiv.sh >> /var/log/aylik_arsiv.log 2>&1

# -----------------------------------------------
# LOG ROTASYON VE TEMİZLİK
# -----------------------------------------------

# Yedekleme loglarını haftalık temizle
0 6 * * 1 root find /var/log -name "*yedekleme*.log" -mtime +30 -delete

Yedekleme Script’inde Hata Yönetimi ve Kilit Mekanizması

Üretim ortamında en sık karşılaşılan sorunlardan biri, bir yedekleme görevi bitmeden yeni bir görevin başlamasıdır. Bu durumu flock ile engelleyin:

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

KILIT_DOSYASI="/var/run/yedekleme.lock"
LOG_DOSYASI="/var/log/yedekleme.log"

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

# Kilit dosyasını kontrol et
exec 200>"$KILIT_DOSYASI"
if ! flock -n 200; then
    log "UYARI: Başka bir yedekleme işlemi çalışıyor. Çıkılıyor."
    exit 0
fi

# Beklenmedik çıkışlarda temizlik yap
temizlik() {
    log "Script sonlandırılıyor, temizlik yapılıyor..."
    rm -f "$KILIT_DOSYASI"
}
trap temizlik EXIT INT TERM

log "=== Yedekleme başlıyor ==="

# Disk alanı kontrolü
HEDEF_DIZIN="/backup"
MUSAIT_ALAN=$(df -BG "$HEDEF_DIZIN" | tail -1 | awk '{print $4}' | sed 's/G//')
MINIMUM_ALAN=10  # GB

if [ "$MUSAIT_ALAN" -lt "$MINIMUM_ALAN" ]; then
    log "HATA: Yetersiz disk alanı! Mevcut: ${MUSAIT_ALAN}GB, Minimum: ${MINIMUM_ALAN}GB"
    # Acil durum: en eski yedekleri sil
    log "Eski yedekler siliniyor..."
    find "$HEDEF_DIZIN" -name "*.tar.gz" -mtime +3 -delete
fi

# Ana yedekleme işlemi
TARIH=$(date +%Y%m%d_%H%M%S)
ARSIV="/backup/sistem_${TARIH}.tar.gz"

tar --exclude='/backup' 
    --exclude='/proc' 
    --exclude='/sys' 
    --exclude='/dev' 
    --exclude='/run' 
    --exclude='/tmp' 
    -czf "$ARSIV" / 2>> "$LOG_DOSYASI"

CIKIS=$?

if [ $CIKIS -eq 0 ] || [ $CIKIS -eq 1 ]; then
    # tar exit code 1 = bazı dosyalar değişti, normal kabul edilebilir
    log "Yedekleme tamamlandı: $ARSIV"
    
    # MD5 kontrol toplamı oluştur
    md5sum "$ARSIV" > "${ARSIV}.md5"
    log "MD5 kontrol toplamı oluşturuldu."
else
    log "HATA: tar komutu $CIKIS koduyla çıktı!"
    rm -f "$ARSIV"
    exit 1
fi

log "=== Yedekleme tamamlandı ==="

Cron Görevlerini İzleme ve Sorun Giderme

Cron görevleri sessizce çalışır, bu da sorunları tespit etmeyi zorlaştırır. İşte bazı izleme teknikleri:

# Cron loglarını kontrol et (Debian/Ubuntu)
grep CRON /var/log/syslog | tail -50

# CentOS/RHEL sistemlerde
grep CRON /var/log/cron | tail -50

# Yedekleme logunu gerçek zamanlı izle
tail -f /var/log/yedekleme.log

# Son 24 saatte çalışan cron görevlerini listele
grep "$(date +%b %e)" /var/log/syslog | grep CRON

# Bir scripti cron ortamında test et (çevre değişkenleri de simüle edilir)
env -i HOME=/root LOGNAME=root PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin SHELL=/bin/bash /usr/local/bin/yedekle.sh

Cron görevlerinde en çok karşılaşılan sorunlar ve çözümleri:

  • PATH sorunu: Cron minimal bir PATH kullanır. Script içinde tam yol belirtin ya da crontab başına PATH= tanımlayın.
  • Çevre değişkenleri eksik: ~/.bashrc cron ortamında yüklenmez. Gerekli değişkenleri script içinde tanımlayın.
  • İzin sorunları: Script dosyasının çalıştırılabilir olduğundan ve doğru sahibinin belirlendiğinden emin olun.
  • Çıktı yönlendirmesi: >> /var/log/gorev.log 2>&1 eklemezseniz çıktılar kaybolur ya da e-postayla gelir.
  • Göreceli yollar: Cron’un çalışma dizini her zaman / değildir. Script içinde cd /istenen/dizin ile başlayın.

Yedekleme Doğrulama

Yedek aldık, peki çalışıyor mu gerçekten? Yedekleri düzenli aralıklarla test etmek şart:

#!/bin/bash
# /usr/local/bin/yedek_dogrula.sh
# Her Pazar çalıştırılır

YEDEK_DIZIN="/backup/gunluk"
TEST_DIZIN="/tmp/yedek_test"
LOG_DOSYASI="/var/log/yedek_dogrulama.log"

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

# En son yedek dosyasını bul
SON_YEDEK=$(find "$YEDEK_DIZIN" -name "*.tar.gz" -type f | sort | tail -1)

if [ -z "$SON_YEDEK" ]; then
    log "HATA: Hiç yedek dosyası bulunamadı!"
    exit 1
fi

log "Test ediliyor: $SON_YEDEK"

# MD5 kontrolü
if [ -f "${SON_YEDEK}.md5" ]; then
    if md5sum -c "${SON_YEDEK}.md5" >> "$LOG_DOSYASI" 2>&1; then
        log "MD5 doğrulama başarılı."
    else
        log "HATA: MD5 doğrulama başarısız! Yedek bozuk olabilir."
        exit 1
    fi
fi

# Arşiv bütünlüğünü test et
mkdir -p "$TEST_DIZIN"

if tar -tzf "$SON_YEDEK" >> "$LOG_DOSYASI" 2>&1; then
    log "Arşiv yapısı geçerli."
else
    log "HATA: Arşiv bozuk!"
    exit 1
fi

# Test dizinini temizle
rm -rf "$TEST_DIZIN"

log "Doğrulama tamamlandı. Yedek sağlıklı görünüyor."

Sonuç

Cron tabanlı yedekleme sistemi kurmak ilk bakışta basit görünür, ama üretime hazır, güvenilir bir yapı oluşturmak dikkat ve planlama gerektirir. En kritik noktaları tekrar vurgulayalım:

Çeşitlilik şart: 7-2-1 kuralını uygulayın. 7 günlük yedek, 2 farklı medya tipi, 1 farklı lokasyonda. Sadece lokal yedek almak sizi kurtarmaz.

Loglama ihmal edilemez: Her yedekleme işlemi log üretmeli ve bu loglar düzenli kontrol edilmeli. Log rotate yapılandırmanızı da yapmayı unutmayın.

Bildirim sistemi kurun: Yedekleme başarısız olduğunda bunu ertesi sabah öğrenmek değil, o an öğrenmek istersiniz. E-posta bildirimleri, Slack entegrasyonu ya da basit bir monitoring scripti bu amaçla kullanılabilir.

Test etmeden yedek almak anlamsız: Ayda en az bir kez yedekten geri yükleme yapın. Geri yükleme testini de otomatize edebilirsiniz.

Disk alanını izleyin: Yedek dizini dolunca script sessizce hata verir ya da yer kalmadığı için yedek alınamaz. Disk doluluk uyarıları kurun.

Güvenliği atlamayın: Veritabanı yedekleme için ayrı, kısıtlı yetkili kullanıcı oluşturun. SSH anahtarlarını şifreleyin. Yedek dosyalarına erişimi sınırlayın.

Cron ile yedekleme otomasyonu, sysadmin’in temel becerilerinden biridir. Yukarıdaki örnekleri kendi ortamınıza uyarlayın, test edin ve üretime alın. Gece 3’te çalışan, sabah raporunu posta kutunuza bırakan bir yedekleme sistemi, en değerli otomasyon yatırımlarından biridir.

Yorum yapın