Arşivleme İşlemlerinde Değişen Dosyaları Tespit Etme: diff ve tar ile Fark Yedeklemesi
Üretim ortamında bir şeyler ters gidip de “dün geceden bu yana ne değişti?” sorusunu sormak zorunda kaldıysanız, bu yazı tam size göre. Yedekleme stratejileri konuşulurken çoğu zaman akıllara hemen tam yedek (full backup) gelir. Oysa gerçek dünyada disk alanı kısıtlıdır, ağ bant genişliği pahalıdır ve her gece 50 GB’lık bir sunucuyu baştan yedeklemek hem zaman hem kaynak israfıdır. İşte bu noktada fark yedeklemesi (differential/incremental backup) devreye girer ve Linux’un bize sunduğu temel araçlar olan diff ve tar bu işi şaşırtıcı derecede iyi yapar.
Fark Yedeklemesini Anlamak
Önce kavramları netleştirelim. Çoğu kaynak “differential” ve “incremental” backup terimlerini birbirine karıştırır.
- Tam yedek (Full Backup): Her şeyin eksiksiz kopyası. Geri dönmesi en kolay, yer kaplayan en büyük yöntem.
- Fark yedeği (Differential Backup): Son tam yedekten bu yana değişen her şeyi alır. Her geçen gün büyür ama geri dönmek için sadece tam yedek + son fark yedeği yeterli.
- Artımlı yedek (Incremental Backup): Yalnızca bir önceki yedekten (ister tam ister artımlı olsun) bu yana değişenleri alır. En az yer kaplar ama geri dönmek için tüm zinciri uygulamak gerekir.
Bu yazıda her ikisini de tar ile nasıl yapacağımızı göreceğiz. Ama önce değişen dosyaları tespit etmenin temel yöntemlerine bakalım.
diff ile Dizin Karşılaştırma
diff denince akla genellikle iki metin dosyası arasındaki fark gelir. Ama -r (recursive) seçeneğiyle dizinleri karşılaştırmak da son derece kullanışlıdır.
diff -rq /yedek/onceki /production/dizin
Buradaki -q (brief) seçeneği sadece hangi dosyaların farklı olduğunu söyler, içerik farkını vermez. Bu, değişiklik tespiti için idealdir.
diff -rq /yedek/2024-01-15 /var/www/html
# Çıktı örneği:
# Files /yedek/2024-01-15/index.php and /var/www/html/index.php differ
# Only in /var/www/html: yeni_dosya.php
# Only in /yedek/2024-01-15: silinen_dosya.php
Daha ayrıntılı bir karşılaştırma yapmak istediğinizde -u seçeneğiyle unified format kullanabilirsiniz:
diff -ru /yedek/2024-01-15/config /var/www/html/config
Bu çıktıyı bir patch dosyasına yönlendirip ileride değişiklikleri geri almak için kullanmak da mümkündür:
diff -ru /yedek/2024-01-15 /var/www/html > degisiklikler.patch
diff’in Sınırları
diff metin dosyaları için mükemmeldir ama büyük dizinlerde performansı düşer. Binary dosyalar için anlamsız sonuçlar verebilir. Üretim ortamındaki büyük veri dizinleri için tar tabanlı yaklaşım daha pratiktir.
tar ile Artımlı Yedekleme: –newer Seçeneği
tar komutunun en güçlü özelliklerinden biri, belirli bir tarihten sonra değiştirilmiş dosyaları alabilmesidir. Bunun için iki yöntem vardır.
Tarih Bazlı Filtreleme
# 2024-01-15 tarihinden sonra değişen dosyaları arşivle
tar -czf yedek_artimli.tar.gz --newer="2024-01-15" /var/www/html
Ya da bir referans dosyasını baz alabilirsiniz. Bu yöntem daha güvenilirdir çünkü sistem saatine bağımlılığı azaltır:
# Referans dosyasını oluştur (tam yedek sonrasında)
touch /tmp/son_tam_yedek_zamani
# Bir süre sonra, referans dosyasından daha yeni olanları al
tar -czf artimli_yedek_$(date +%Y%m%d).tar.gz
--newer=/tmp/son_tam_yedek_zamani
/var/www/html
Bu yaklaşım özellikle cron job’larla çalışırken işe yarar. Tam yedek aldıktan hemen sonra bir timestamp dosyası oluşturuyorsunuz, artımlı yedekler de hep o dosyaya bakıyor.
tar ile Snapshot Tabanlı Artımlı Yedekleme
tar‘ın en gelişmiş artımlı yedekleme özelliği, --listed-incremental (kısa formu -g) seçeneğidir. Bu seçenek bir “snapshot dosyası” kullanır ve tar her çalıştığında bu dosyayı günceller.
İlk Tam Yedek (Level 0)
tar --listed-incremental=/var/backup/snapshot.snar
-czf /var/backup/tam_yedek_$(date +%Y%m%d).tar.gz
/var/www /etc /home
Bu komut çalıştıktan sonra snapshot.snar dosyası oluşur. Bu dosya içinde o andaki dizin yapısı, dosya isimlerinin hash’leri ve metadata bilgileri tutulur.
Artımlı Yedekler (Level 1+)
Snapshot dosyası yerinde durdukça, tar her çalıştığında sadece değişenleri alır:
tar --listed-incremental=/var/backup/snapshot.snar
-czf /var/backup/artimli_$(date +%Y%m%d_%H%M).tar.gz
/var/www /etc /home
Kritik nokta: snapshot.snar dosyasını silmeden veya bozmadan bu komutun her çalışması bir artımlı yedek üretir. Eğer sıfırdan tam yedek almak istiyorsanız snapshot dosyasını silmeniz gerekir.
Geri Yükleme
Artımlı yedeklerden geri yükleme yaparken sırayı korumak şarttır:
# Önce tam yedeği aç
tar --listed-incremental=/dev/null
-xzf /var/backup/tam_yedek_20240115.tar.gz
-C /restore/hedef
# Sonra artımlı yedekleri sırayla uygula
tar --listed-incremental=/dev/null
-xzf /var/backup/artimli_20240116_0200.tar.gz
-C /restore/hedef
tar --listed-incremental=/dev/null
-xzf /var/backup/artimli_20240117_0200.tar.gz
-C /restore/hedef
Burada --listed-incremental=/dev/null kullanmamızın sebebi: geri yükleme sırasında snapshot dosyasını güncellemek istemiyoruz, sadece arşivi açıyoruz.
Pratik Senaryo: Web Sunucusu Yedekleme Scripti
Gerçek bir senaryo üzerinden gidelim. Bir e-ticaret sitesi yönetiyorsunuz. /var/www altında PHP dosyaları var, /etc/nginx ve /etc/php konfigürasyonları var. Her gece 02:00’de artımlı yedek alınacak, her Pazar 01:00’de tam yedek alınacak.
#!/bin/bash
# /usr/local/bin/yedek.sh
YEDEK_DIZIN="/var/backup/web"
SNAPSHOT="/var/backup/snapshot.snar"
LOG="/var/log/yedek.log"
KAYNAK="/var/www /etc/nginx /etc/php"
TARIH=$(date +%Y%m%d_%H%M)
GUN=$(date +%u) # 7 = Pazar
mkdir -p "$YEDEK_DIZIN"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG"
}
if [ "$GUN" -eq 7 ]; then
log "Tam yedek basliyor..."
rm -f "$SNAPSHOT"
tar --listed-incremental="$SNAPSHOT"
-czf "$YEDEK_DIZIN/tam_${TARIH}.tar.gz"
$KAYNAK 2>> "$LOG"
log "Tam yedek tamamlandi: tam_${TARIH}.tar.gz"
else
log "Artimli yedek basliyor..."
tar --listed-incremental="$SNAPSHOT"
-czf "$YEDEK_DIZIN/artimli_${TARIH}.tar.gz"
$KAYNAK 2>> "$LOG"
log "Artimli yedek tamamlandi: artimli_${TARIH}.tar.gz"
fi
# 30 gunden eski yedekleri temizle
find "$YEDEK_DIZIN" -name "*.tar.gz" -mtime +30 -delete
log "Eski yedekler temizlendi."
Bu scripti cron’a eklemek için:
# crontab -e
0 2 * * * /usr/local/bin/yedek.sh
find ile Değişen Dosyaları Tespit Etme
Sadece hangi dosyaların değiştiğini listelemek istiyorsanız, tar kullanmadan find komutu da bu iş için son derece kullanışlıdır. Bunu tar‘ın --newer seçeneğiyle birleştirdiğinizde güçlü bir iş akışı elde edersiniz.
# Son 24 saatte değişen dosyaları listele
find /var/www -newer /tmp/son_yedek_zamani -type f
# Bu listeyi tar'a besle
find /var/www -newer /tmp/son_yedek_zamani -type f -print0 |
tar --null -czf artimli_yedek.tar.gz --files-from=-
--null ve -print0 kombinasyonu, içinde boşluk veya özel karakter olan dosya isimlerini güvenli biçimde işlemek için kullanılır. Üretim ortamında mutlaka bu şekilde kullanın, yoksa bir gün “neden şu dizin yedeklenmemiş?” sorusunu sorar bulursunuz kendinizi.
mtime vs ctime vs atime
find ile çalışırken hangi zaman damgasını baz aldığınız önemlidir:
- -newer (mtime): Dosya içeriğinin son değiştirilme zamanı. Genellikle istenen bu.
- -cnewer (ctime): İnode değişim zamanı. İzin, sahiplik değişiklikleri de burada görünür.
- -anewer (atime): Son erişim zamanı. Backup için pek kullanışlı değil, çünkü sadece okumak bile bu zamanı değiştirir.
Fark yedekleme için -newer veya --newer kullanın. Konfigürasyon değişikliklerini de yakalamak istiyorsanız -cnewer düşünülebilir.
Yedek Bütünlüğü Doğrulama
Aldığınız yedeğin gerçekten açılabilir olup olmadığını doğrulamak, yedekleme kadar önemlidir. Bunun için iki yöntem:
# tar arşivini test et (açmadan)
tar -tzf artimli_yedek.tar.gz > /dev/null && echo "Arşiv sağlam" || echo "Arşiv bozuk"
# Daha ayrıntılı doğrulama
tar -tzf artimli_yedek.tar.gz | wc -l
# Kaç dosya var?
# MD5 ile bütünlük kontrolü
md5sum artimli_yedek.tar.gz > artimli_yedek.tar.gz.md5
# Sonra doğrularken:
md5sum -c artimli_yedek.tar.gz.md5
Üretim ortamında her yedeğin ardından bu doğrulamayı otomatik yapan bir script kullanmak ciddi bir olgunluk göstergesidir. Yıllar içinde görmüştüm: ekip haftalarca yedek almış ama hiçbirini test etmemiş, ihtiyaç duyulduğunda arşivlerin yarısı bozuk çıkmış.
Snapshot Dosyasını Anlama ve Yönetme
--listed-incremental ile oluşan .snar dosyası binary bir formatta değildir, içine bakabilirsiniz:
# Snapshot dosyasının içeriğini görüntüle
cat /var/backup/snapshot.snar | head -20
Çıktıda her satır için şunları görürsünüz: dizin adı, içerdiği dosyalar ve zaman bilgileri. Eğer snapshot dosyası bozulursa ya da kaybolursa, bir sonraki tar çalışması otomatik olarak tam yedek alır. Bu aslında bir güvenlik ağı işlevi görür.
Snapshot Dosyasını Yedeklemek
Snapshot dosyasını da yedeklemeyi unutmayın. Artımlı yedekler zincirinizin geri yükleme için bu dosyaya ihtiyacı yoktur (geri yüklerken /dev/null kullandık) ama hangi yedeğin hangi snapshot’a karşılık geldiğini bilmek, ileride kafa karışıklığını önler.
# Snapshot dosyasını yedek dizinine kopyala
cp /var/backup/snapshot.snar
/var/backup/snapshot_$(date +%Y%m%d).snar.bak
Gerçek Dünya Senaryosu: Veritabanı Dışı Konfigürasyon Takibi
Bir başka yaygın kullanım: sunucu konfigürasyon dosyalarındaki değişiklikleri takip etmek. Bu hem audit hem de hızlı geri alma senaryolarında çok işe yarar.
#!/bin/bash
# Konfigürasyon değişikliklerini tespit et ve kaydet
ONCEKI="/var/audit/etc_snapshot"
GUNCEL="/etc"
RAPOR="/var/audit/degisiklik_$(date +%Y%m%d_%H%M).txt"
echo "=== Konfigürasyon Degisiklik Raporu ===" > "$RAPOR"
echo "Tarih: $(date)" >> "$RAPOR"
echo "" >> "$RAPOR"
# Yeni veya değişen dosyalar
echo "--- Yeni veya Degisen Dosyalar ---" >> "$RAPOR"
diff -rq "$ONCEKI" "$GUNCEL" 2>/dev/null |
grep -v "^Only in $ONCEKI" >> "$RAPOR"
# Silinen dosyalar
echo "" >> "$RAPOR"
echo "--- Silinen Dosyalar ---" >> "$RAPOR"
diff -rq "$ONCEKI" "$GUNCEL" 2>/dev/null |
grep "^Only in $ONCEKI" >> "$RAPOR"
# Eğer değişiklik varsa, snapshot'ı güncelle ve yedek al
if [ -s "$RAPOR" ] && grep -q "differ|Only in" "$RAPOR"; then
tar -czf "/var/audit/etc_yedek_$(date +%Y%m%d_%H%M).tar.gz" /etc
rsync -a --delete "$GUNCEL/" "$ONCEKI/"
echo "Degisiklik tespit edildi, yedek alindi." >> "$RAPOR"
else
echo "Degisiklik yok." >> "$RAPOR"
fi
Bu scripti cron ile saatlik çalıştırırsanız, hangi konfigürasyonun ne zaman değiştiğine dair net bir audit trail elde edersiniz. Özellikle birden fazla kişinin erişebildiği sistemlerde hayat kurtarır.
Performans İpuçları
Büyük dosya sistemlerinde fark yedekleme yaparken dikkat edilmesi gerekenler:
- Sıkıştırma seviyesi:
-czfyerine daha az CPU kullanan-cjf(bzip2) veya-cJf(xz) yerine hiç sıkıştırmadan-cfkullanmayı düşünün. Ağ üzerinden kopyalayacaksanız sıkıştırma mantıklı, yerel depolamada disk hızı genellikle CPU’dan daha kısıtlayıcıdır.
- Exclude listesi: Geçici dosyaları, log’ları ve cache dizinlerini dışarıda bırakmak hem zaman hem yer kazandırır:
tar --listed-incremental=/var/backup/snapshot.snar
--exclude="/var/www/cache"
--exclude="*.tmp"
--exclude="*.log"
--exclude-tag-under=".nobackup"
-czf artimli_$(date +%Y%m%d).tar.gz
/var/www
--exclude-tag-under özellikle güzel: bir dizinin içine .nobackup adında boş bir dosya koyarsanız, o dizin yedekleme kapsamı dışına çıkar. Geliştirici ortamlarında node_modules veya .git dizinleri için biçilmiş kaftan.
- nice ve ionice: Yedekleme I/O’su üretim trafiğini etkilemesin diye:
nice -n 19 ionice -c 3 tar --listed-incremental=...
Sonuç
diff ve tar, göründüklerinden çok daha güçlü araçlar. --listed-incremental seçeneği sayesinde tar, kendi başına kurumsal kalitede artımlı yedekleme çözümü sunabilir. diff -rq ise özellikle iki zaman noktası arasındaki değişiklikleri hızlıca anlamak için pratik bir araçtır.
Elbette Bacula, Amanda, Restic gibi özel yedekleme araçları var ve büyük ölçekli ortamlarda bunlara geçmek mantıklı. Ancak onlarca sunucuyu yönetirken script tabanlı bir çözümün ne kadar şeffaf ve özelleştirilebilir olduğunu takdir etmeye başlarsınız. Her şeyi siz yazıyorsunuz, her şeyi siz okuyabiliyorsunuz.
Son olarak şunu söylemeden geçemeyeceğim: hiç test edilmemiş yedek, yedek değildir. Ayda en az bir kez yedeğinizden geri yükleme yapın. Gerçek bir sorun anında ilk kez denemek istemezsiniz. Snapshot dosyasını, zaman damgalarını, sırayı, hepsini canlı ortamda test edin. Yedekleme sisteminize güveniniz, bir gün sizi kurtaracak olan şeydir.
