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’de0 2 0: Her Pazar saat 02:00’de0 2 1: Her ayın 1’inde saat 02:00’de/15 *: Her 15 dakikada bir0 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:
~/.bashrccron 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>&1eklemezseniz çıktılar kaybolur ya da e-postayla gelir. - Göreceli yollar: Cron’un çalışma dizini her zaman
/değildir. Script içindecd /istenen/dizinile 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.