Veritabanı Yedekleme Entegrasyonu: rsync ile Eksiksiz Rehber
Veritabanlarını yedeklemek, “bir gün lazım olur” diye yapılan bir rutin değil, sistemin can sigortasıdır. Peki ya bu yedekleri güvenli bir şekilde uzak sunucuya, NAS cihazına veya bulut depolamaya taşımak? İşte burada rsync devreye giriyor. Sadece dosya kopyalamakla kalmayıp, değişen blokları tespit eden, bant genişliğini verimli kullanan ve kesintisiz çalışabilen rsync, veritabanı yedekleme pipeline’larının vazgeçilmez aracı haline gelmiştir. Bu yazıda PostgreSQL, MySQL ve genel veritabanı dump dosyalarını rsync ile nasıl entegre edeceğinizi, production ortamlarında karşılaşılan gerçek sorunları ve bunların çözümlerini adım adım inceleyeceğiz.
rsync Neden Veritabanı Yedeklemede Tercih Edilir?
Klasik cp veya scp ile yedekleme yapıyorsanız, her seferinde tüm dosyayı baştan aktarırsınız. 50 GB’lık bir PostgreSQL dump dosyası için bu durum hem zaman hem de bant genişliği israfı demektir. rsync’in fark aktarımı (delta transfer) algoritması sayesinde, sadece değişen bloklar iletilir.
Veritabanı yedekleme senaryolarında rsync’i öne çıkaran başlıca özellikler:
- Delta transfer algoritması: Önceki yedekle karşılaştırarak yalnızca değişen kısımları gönderir
- Atomik operasyonlar:
--inplaceve--partialbayraklarıyla yarıda kalan aktarımları devam ettirir - Sıkıştırma desteği:
-zbayrağıyla aktarım sırasında sıkıştırma yapar - SSH tüneli: Şifreli kanal üzerinden güvenli aktarım sağlar
- Checksum doğrulama:
-cbayrağıyla dosya bütünlüğünü garanti altına alır - Bant genişliği limitleme:
--bwlimitile üretim trafiğini etkilemez
Temel Kurulum ve Hazırlık
Önce ortamınızı düzgün hazırlamak, sonraki her adımı kolaylaştırır.
SSH Anahtar Tabanlı Kimlik Doğrulama
Otomatik yedekleme scriptlerinde parola sormayan SSH bağlantısı şarttır.
# Yedekleme sunucusunda anahtar çifti oluştur
ssh-keygen -t ed25519 -C "backup-rsync-key" -f ~/.ssh/backup_rsa -N ""
# Public key'i hedef sunucuya kopyala
ssh-copy-id -i ~/.ssh/backup_rsa.pub [email protected]
# Bağlantıyı test et
ssh -i ~/.ssh/backup_rsa [email protected] "echo 'SSH bağlantısı başarılı'"
SSH config dosyasına kısayol eklemek uzun vadede işinizi kolaylaştırır:
# ~/.ssh/config dosyasına ekle
cat >> ~/.ssh/config << 'EOF'
Host backup-server
HostName 192.168.1.100
User backup
IdentityFile ~/.ssh/backup_rsa
StrictHostKeyChecking no
ServerAliveInterval 60
ServerAliveCountMax 3
EOF
Dizin Yapısını Oluşturma
Düzenli bir dizin yapısı, yedeklerinizi ileride bulmayı ve yönetmeyi kolaylaştırır.
# Kaynak sunucuda (veritabanı sunucusu)
mkdir -p /var/backup/databases/{postgresql,mysql,dumps}
mkdir -p /var/backup/logs
# Hedef sunucuda (yedekleme sunucusu)
mkdir -p /data/backups/{postgresql,mysql}/{daily,weekly,monthly}
chown -R backup:backup /data/backups
chmod 750 /data/backups
PostgreSQL Yedekleme + rsync Entegrasyonu
PostgreSQL’de canlı veritabanı dosyalarını doğrudan kopyalamak tutarsız yedeklere yol açar. Önce pg_dump veya pg_basebackup ile tutarlı bir snapshot almalı, ardından rsync ile taşımalısınız.
pg_dump ile Dump Alıp rsync ile Aktarma
#!/bin/bash
# /usr/local/bin/pg_backup_rsync.sh
set -euo pipefail
# Değişkenler
DB_HOST="localhost"
DB_PORT="5432"
DB_USER="postgres"
BACKUP_DIR="/var/backup/databases/postgresql"
REMOTE_HOST="backup-server"
REMOTE_DIR="/data/backups/postgresql/daily"
LOG_FILE="/var/backup/logs/pg_backup_$(date +%Y%m%d).log"
RETENTION_DAYS=7
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
log "PostgreSQL yedekleme başladı"
# Tüm veritabanlarını listele ve yedekle
for DB in $(psql -h "$DB_HOST" -U "$DB_USER" -t -c "SELECT datname FROM pg_database WHERE datistemplate = false AND datname != 'postgres'"); do
DB=$(echo $DB | tr -d ' ')
DUMP_FILE="${BACKUP_DIR}/${DB}_$(date +%Y%m%d_%H%M%S).dump"
log "Yedekleniyor: $DB -> $DUMP_FILE"
# Custom format ile dump al (sıkıştırılmış, paralel restore destekler)
pg_dump -h "$DB_HOST" -U "$DB_USER" -Fc -Z 9 "$DB" -f "$DUMP_FILE"
if [ $? -eq 0 ]; then
log "$DB dump başarıyla alındı: $(du -sh $DUMP_FILE | cut -f1)"
else
log "HATA: $DB dump alınamadı!"
exit 1
fi
done
# rsync ile uzak sunucuya aktar
log "rsync aktarımı başlıyor..."
rsync -avz
--progress
--partial
--checksum
--delete
--bwlimit=50000
--log-file="${LOG_FILE}"
"${BACKUP_DIR}/"
"${REMOTE_HOST}:${REMOTE_DIR}/"
log "rsync aktarımı tamamlandı"
# Yerel eski yedekleri temizle
find "$BACKUP_DIR" -name "*.dump" -mtime "+${RETENTION_DAYS}" -delete
log "Yerel temizlik tamamlandı (${RETENTION_DAYS} günden eski dosyalar silindi)"
log "Yedekleme işlemi başarıyla tamamlandı"
pg_basebackup ile Fiziksel Yedekleme
Büyük veritabanlarında ya da Point-in-Time Recovery (PITR) gereksiniminde pg_basebackup tercih edilir:
#!/bin/bash
# Fiziksel PostgreSQL yedeklemesi
PGDATA="/var/lib/postgresql/14/main"
BACKUP_DIR="/var/backup/databases/postgresql/basebackup"
REMOTE_HOST="backup-server"
DATE=$(date +%Y%m%d)
# Mevcut base backup varsa rotate et
if [ -d "${BACKUP_DIR}/current" ]; then
mv "${BACKUP_DIR}/current" "${BACKUP_DIR}/prev_${DATE}"
fi
# pg_basebackup ile yedek al
pg_basebackup
-h localhost
-U replication_user
-D "${BACKUP_DIR}/current"
-Fp
-Xs
-P
-R
# rsync ile aktarırken hard link kullan (disk alanı tasarrufu)
rsync -avz
--link-dest="${REMOTE_HOST}:/data/backups/postgresql/basebackup/prev"
--delete
"${BACKUP_DIR}/current/"
"${REMOTE_HOST}:/data/backups/postgresql/basebackup/current/"
MySQL / MariaDB Yedekleme + rsync Entegrasyonu
MySQL’de mysqldump, Percona XtraBackup veya mydumper kullanabilirsiniz. Her biri farklı senaryolara hitap eder.
#!/bin/bash
# /usr/local/bin/mysql_backup_rsync.sh
set -euo pipefail
MYSQL_USER="backup_user"
MYSQL_PASS_FILE="/etc/mysql/backup.cnf" # Güvenli: parolayı scriptte tutma!
BACKUP_DIR="/var/backup/databases/mysql"
REMOTE_HOST="backup-server"
REMOTE_DIR="/data/backups/mysql/daily"
DATE=$(date +%Y%m%d_%H%M%S)
LOG_FILE="/var/backup/logs/mysql_backup_$(date +%Y%m%d).log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# MySQL bağlantı dosyası içeriği:
# [client]
# user=backup_user
# password=güçlü_parola_buraya
log "MySQL yedekleme başladı"
# Tüm veritabanlarını al
DATABASES=$(mysql --defaults-file="$MYSQL_PASS_FILE" -e "SHOW DATABASES;" | grep -Ev "(Database|information_schema|performance_schema|sys)")
for DB in $DATABASES; do
DUMP_FILE="${BACKUP_DIR}/${DB}_${DATE}.sql.gz"
log "Yedekleniyor: $DB"
mysqldump
--defaults-file="$MYSQL_PASS_FILE"
--single-transaction
--routines
--triggers
--events
--hex-blob
"$DB" | gzip -9 > "$DUMP_FILE"
# Dosya sıfır byte değilse başarılı say
if [ -s "$DUMP_FILE" ]; then
log "$DB başarıyla yedeklendi: $(du -sh $DUMP_FILE | cut -f1)"
else
log "HATA: $DB dump dosyası boş veya hatalı!"
rm -f "$DUMP_FILE"
exit 1
fi
done
# rsync aktarımı
log "rsync başlıyor: ${BACKUP_DIR} -> ${REMOTE_HOST}:${REMOTE_DIR}"
rsync -avz
--partial
--partial-dir=".rsync-partial"
--timeout=300
--delete
--exclude="*.tmp"
--exclude=".rsync-partial/"
--bwlimit=30000
"${BACKUP_DIR}/"
"${REMOTE_HOST}:${REMOTE_DIR}/"
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
log "rsync başarıyla tamamlandı"
elif [ $EXIT_CODE -eq 24 ]; then
log "UYARI: rsync tamamlandı ancak bazı kaynak dosyalar aktarım sırasında silindi (exit 24 - normal)"
else
log "HATA: rsync başarısız! Exit code: $EXIT_CODE"
exit $EXIT_CODE
fi
log "MySQL yedekleme tamamlandı"
Incremental Yedekleme: Hard Link Yöntemi (Snapshot Backup)
Günlük tam yedek almak disk alanını hızla doldurur. Hard link tabanlı incremental yedekleme, her gün “tam yedek gibi görünen” ama aslında sadece değişen dosyaları saklayan bir yapı kurar.
#!/bin/bash
# /usr/local/bin/incremental_backup.sh
# Her gün ayrı dizin, yalnızca değişen dosyalar gerçekten kopyalanır
BACKUP_SRC="/var/backup/databases"
REMOTE_HOST="backup-server"
REMOTE_BASE="/data/backups/incremental"
TODAY=$(date +%Y%m%d)
YESTERDAY=$(date -d "yesterday" +%Y%m%d)
# Önceki yedeği link-dest olarak kullan
# Bu sayede değişmeyen dosyalar hard link olarak işaret edilir (disk kullanmaz)
rsync -avz
--link-dest="${REMOTE_BASE}/${YESTERDAY}"
--delete
--checksum
--exclude="*.tmp"
--exclude="*.lock"
--log-file="/var/backup/logs/incremental_${TODAY}.log"
"${BACKUP_SRC}/"
"${REMOTE_HOST}:${REMOTE_BASE}/${TODAY}/"
# Uzak sunucuda 30 günden eski incremental yedekleri temizle
ssh "$REMOTE_HOST" "find ${REMOTE_BASE} -maxdepth 1 -type d -name '????????' | sort | head -n -30 | xargs rm -rf"
echo "Incremental yedekleme tamamlandı: $(date)"
Cron ile Otomatik Zamanlama
Scriptleri yazmak yetmez, doğru zamanlama kritiktir. Veritabanı yük profilinize göre zamanlama yapmalısınız.
# /etc/cron.d/database-backups dosyası
# Saat formatı: dakika saat gün_ay ay haftanın_günü kullanıcı komut
# PostgreSQL: Her gece 02:00'da
0 2 * * * postgres /usr/local/bin/pg_backup_rsync.sh >> /var/backup/logs/cron_pg.log 2>&1
# MySQL: Her gece 02:30'da (PG ile çakışmasın)
30 2 * * * root /usr/local/bin/mysql_backup_rsync.sh >> /var/backup/logs/cron_mysql.log 2>&1
# Haftalık incremental: Pazar 03:00
0 3 * * 0 root /usr/local/bin/incremental_backup.sh >> /var/backup/logs/cron_incremental.log 2>&1
# Yedek doğrulama: Her sabah 07:00
0 7 * * * root /usr/local/bin/verify_backups.sh >> /var/backup/logs/cron_verify.log 2>&1
Yedekleme Doğrulama Scripti
“Yedek aldım” demek yetmez. Aldığınız yedeğin gerçekten restore edilebilir olduğunu kanıtlamanız gerekir.
#!/bin/bash
# /usr/local/bin/verify_backups.sh
REMOTE_HOST="backup-server"
REMOTE_DIR="/data/backups"
ALERT_EMAIL="[email protected]"
MIN_FILE_SIZE=1024 # Minimum 1KB (byte cinsinden)
LOG_FILE="/var/backup/logs/verify_$(date +%Y%m%d).log"
ERRORS=0
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
log "Yedek doğrulama başladı"
# Uzak sunucudaki dosyaları kontrol et
ssh "$REMOTE_HOST" "find ${REMOTE_DIR} -name '*.dump' -o -name '*.sql.gz' | head -20" | while read REMOTE_FILE; do
# Dosya boyutunu kontrol et
FILE_SIZE=$(ssh "$REMOTE_HOST" "stat -c%s '${REMOTE_FILE}' 2>/dev/null || echo 0")
if [ "$FILE_SIZE" -lt "$MIN_FILE_SIZE" ]; then
log "HATA: Şüpheli küçük dosya: ${REMOTE_FILE} (${FILE_SIZE} bytes)"
ERRORS=$((ERRORS + 1))
else
log "OK: ${REMOTE_FILE} ($(numfmt --to=iec ${FILE_SIZE}))"
fi
done
# PostgreSQL dump dosyalarını syntax kontrolü ile doğrula
for DUMP in /var/backup/databases/postgresql/*.dump; do
if [ -f "$DUMP" ]; then
pg_restore --list "$DUMP" > /dev/null 2>&1
if [ $? -eq 0 ]; then
log "PostgreSQL dump geçerli: $(basename $DUMP)"
else
log "HATA: Geçersiz PostgreSQL dump: $(basename $DUMP)"
ERRORS=$((ERRORS + 1))
fi
fi
done
# MySQL dump gzip bütünlüğünü kontrol et
for GZIP_FILE in /var/backup/databases/mysql/*.sql.gz; do
if [ -f "$GZIP_FILE" ]; then
gzip -t "$GZIP_FILE" 2>/dev/null
if [ $? -eq 0 ]; then
log "MySQL dump gzip geçerli: $(basename $GZIP_FILE)"
else
log "HATA: Bozuk gzip dosyası: $(basename $GZIP_FILE)"
ERRORS=$((ERRORS + 1))
fi
fi
done
# Hata varsa e-posta gönder
if [ $ERRORS -gt 0 ]; then
log "TOPLAM HATA: $ERRORS - E-posta gönderiliyor"
mail -s "[KRITIK] Yedek Doğrulama Hatası: $ERRORS adet sorun tespit edildi"
"$ALERT_EMAIL" < "$LOG_FILE"
else
log "Tüm doğrulamalar başarılı"
fi
Gerçek Dünya Sorunları ve Çözümleri
Sorun 1: “Broken Pipe” Hatası Büyük Dosyalarda
Production’da sıkça karşılaşılan bu sorun, ağ kesintisi veya SSH timeout kaynaklıdır.
# Çözüm: --partial ve timeout ayarları
rsync -avz
--partial
--partial-dir=".rsync-partial"
--timeout=600
--compress-level=6
-e "ssh -o ServerAliveInterval=30 -o ServerAliveCountMax=10 -o ConnectTimeout=60"
/var/backup/databases/
backup-server:/data/backups/
Sorun 2: rsync Sonrası Disk Doluluğu
--delete bayrağı olmadan rsync eski dosyaları biriktirerek disk doldurabilir. Kontrollü bir yaklaşım:
# Silmeden önce ne silineceğini göster (dry-run)
rsync -avz --delete --dry-run /var/backup/databases/ backup-server:/data/backups/
# Eğer çıktı makul görünüyorsa gerçek çalıştır
rsync -avz
--delete
--delete-before
--delete-excluded
--exclude="*.tmp"
/var/backup/databases/
backup-server:/data/backups/
Sorun 3: Aktarım Sırasında Değişen Dump Dosyaları
Uzun süren dump aktarımlarında kaynak dosya yazılmaya devam ediyor olabilir. Çözüm: önce dump’ı tamamla, sonra rsync’i çalıştır. Ek güvence için checksum kullanın:
# Checksumle doğrulamalı aktarım
rsync -avzc
--whole-file
/var/backup/databases/
backup-server:/data/backups/
# --whole-file: Delta algoritmasını devre dışı bırakır, tüm dosyayı aktarır
# -c (--checksum): MD5 ile doğrulama yapar
İzleme ve Uyarı Sistemi
Yedekleme işlemini izlemeden bırakmak, emniyet kemeri takmadan araba sürmek gibidir.
#!/bin/bash
# rsync çıkış kodlarını yorumla ve Slack/mail bildir
RSYNC_EXIT_CODE=$?
WEBHOOK_URL="https://hooks.slack.com/services/XXXXXX"
HOSTNAME=$(hostname)
interpret_rsync_exit() {
case $1 in
0) echo "Başarılı" ;;
1) echo "HATA: Syntax hatası" ;;
2) echo "HATA: Protokol uyumsuzluğu" ;;
11) echo "HATA: Dosya I/O hatası" ;;
23) echo "UYARI: Bazı dosyalar aktarılamadı (izin hatası)" ;;
24) echo "UYARI: Bazı kaynak dosyalar aktarım sırasında silindi" ;;
25) echo "HATA: --max-delete limiti aşıldı" ;;
30) echo "HATA: Bağlantı timeout" ;;
35) echo "HATA: Bekleme timeout" ;;
*) echo "HATA: Bilinmeyen exit kodu: $1" ;;
esac
}
MESSAGE=$(interpret_rsync_exit $RSYNC_EXIT_CODE)
if [ $RSYNC_EXIT_CODE -ne 0 ] && [ $RSYNC_EXIT_CODE -ne 24 ]; then
# Slack bildirimi
curl -s -X POST "$WEBHOOK_URL"
-H 'Content-type: application/json'
-d "{"text": ":rotating_light: *${HOSTNAME}* yedekleme hatası: ${MESSAGE} (exit: ${RSYNC_EXIT_CODE})"}"
fi
Güvenlik Hususları
Yedekleme scriptleri genellikle root veya özel servis kullanıcılarıyla çalışır. Birkaç kritik güvenlik kuralı:
- Ayrı backup kullanıcısı oluşturun: root ile rsync çalıştırmaktan kaçının
- Hedef sunucuda read-only rsync:
--read-onlymodunda rsyncd daemon kurun - Parola dosyaları: MySQL bağlantı bilgilerini
/etc/mysql/backup.cnfiçinde saklayın, chmod 600 yapın - SSH authorized_keys kısıtlaması: Public key’e komut kısıtlaması ekleyin
# ~/.ssh/authorized_keys içinde rsync'i kısıtla
command="rsync --server --sender -avz . /data/backups/",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAAA... backup-rsync-key
- Yedek şifreleme: Hassas veriler için
gpgile şifreli dump alın
# GPG şifreli dump
pg_dump -Fc mydb | gpg --cipher-algo AES256 -e -r [email protected] > mydb_encrypted.dump.gpg
Sonuç
rsync, doğru parametreler ve iyi yazılmış wrapper scriptlerle veritabanı yedekleme altyapınızın bel kemiği olabilir. Özetlemek gerekirse dikkat edilmesi gereken noktalara değinelim:
- Dump önce, rsync sonra: Canlı veritabanı dosyalarını asla doğrudan rsync ile kopyalamayın
--partialher zaman: Büyük dosyalarda yarıda kesilen aktarımlar için şart--bwlimitile nazik olun: Production saatlerinde bant genişliğini kısıtlayın- Doğrulama zorunlu: Yedek aldım demek yetmez, restore test edin
- Log her şeyi:
--log-fileile rsync loglarını saklayın, sorun çözümünde hayat kurtarır - Exit kodlarını yorumlayın: exit 0 ve exit 24 dışındaki tüm kodlar alarm vermeli
- Bant genişliği planlaması: Incremental ve hard-link yöntemi ile hem disk hem de ağ kullanımını optimize edin
İyi bir yedekleme sistemi, felaket anında paniği önler. rsync ile kurduğunuz bu pipeline’ı düzenli aralıklarla test edin, restore senaryolarını deneyin ve loglarınızı takip edin. Yedek almak rutin, restore etmek ise gerçek testtir.
