Arşivleme İşlemlerinde Sparse Dosyaları Yönetme: Delik İçeren Dosyaları tar ve cp ile Verimli Yedekleme

Yıllar önce bir müşterimizin production ortamında ciddi bir yedekleme sorunu yaşadık. Sanal makine disklerini her gece tar ile yedekliyorduk, her şey güzeldi, ta ki geri yükleme zamanı gelene kadar. Yedek boyutları disk üzerindeki gerçek kullanımın çok üzerindeydi ve restore işlemi saatler alıyordu. Sorunun kaynağı sparse dosyalardı. O günden bu yana sparse dosyaları anlamak ve doğru yönetmek benim için temel bir sysadmin yetkinliği haline geldi.

Sparse Dosya Nedir, Neden Önemlidir?

Sparse dosya (delikli dosya), içinde büyük boş alanlar (zero blok dizileri) bulunduran ama bu boş alanların diske fiziksel olarak yazılmadığı özel dosyalardır. Dosya sistemi, bu “delikleri” metadata olarak takip eder; sıfır bloklarını gerçekten depolamaz.

Bunu somutlaştıralım:

# 1GB boyutunda ama sadece birkaç KB gerçek veri içeren sparse dosya oluşturma
dd if=/dev/zero of=/tmp/sparse_test.img bs=1 count=0 seek=1G
ls -lh /tmp/sparse_test.img    # Görünen boyut: 1GB
du -sh /tmp/sparse_test.img    # Gerçek disk kullanımı: 4KB (veya 0)

Çıktı şuna benzer bir şey gösterir:

-rw-r--r-- 1 root root 1.0G Kas 15 09:23 /tmp/sparse_test.img
4.0K    /tmp/sparse_test.img

ls size 1GB diyor, du ise 4KB. İşte bu fark sparse dosyaların özü. Sanal makine disk imajları (QEMU/KVM .qcow2 ham img dosyaları), veritabanı dosyaları, Docker katman dosyaları bunların hepsi sıklıkla sparse yapıdadır.

Sparse durumunu daha ayrıntılı görmek için:

# FIEMAP/FIBMAP ile delik haritasını çıkart
filefrag -v /tmp/sparse_test.img

# Daha okunaklı çıktı için
stat /tmp/sparse_test.img | grep -E "Size|Blocks"
# Size: 1073741824   Blocks: 8   IO Block: 4096

Blocks: 8 değeri, dosyanın disk üzerinde yalnızca 4096 baytlık (8 x 512 byte) gerçek blok kapladığını gösteriyor.

Sorunun Kaynağı: Naive Arşivleme

Burası kritik nokta. Varsayılan ayarlarla tar veya cp kullandığınızda ne olur?

# TEHLİKELİ: Sparse dosyayı olduğu gibi arşivle
tar -cvf /backup/sparse_test.tar /tmp/sparse_test.img
ls -lh /backup/sparse_test.tar
# Sonuç: ~1GB tar dosyası oluştu!

tar bu dosyayı arşivlerken delikleri doldurmak için sıfır baytlar yazdı ve 1GB’lık gerçek bir tar dosyası oluştu. 100 adet böyle VM disk imajınız varsa yedekleme sisteminiz geceleri 100GB yerine terabaytlar üretmeye başlar.

cp de aynı hatayı yapar:

# TEHLİKELİ: cp ile sparse özelliğini kaybetme
cp /tmp/sparse_test.img /backup/sparse_test_copy.img
du -sh /backup/sparse_test_copy.img
# 1.0G  -- Tüm alan dolu!

tar ile Sparse Dosyaları Doğru Yedekleme

tar komutunun bu sorunu çözecek özel bir parametresi var: -S veya --sparse.

# DOĞRU: Sparse-aware arşivleme
tar -cSvf /backup/sparse_test.tar /tmp/sparse_test.img
ls -lh /backup/sparse_test.tar
# Sonuç: ~10KB tar dosyası (sadece gerçek veri + metadata)

Bu kadar basit. -S bayrağı tar‘a “bu dosyadaki delikleri tespit et, sıfır bloklarını depolama, sadece pozisyon bilgisini kaydet” der.

Parametreler şöyle açıklanabilir:

  • -S: Sparse dosyaları algıla ve verimli şekilde depola
  • -c: Yeni arşiv oluştur
  • -v: Verbose mod, işlemleri listele
  • -f: Çıktı dosyasını belirt

Sıkıştırma ile birlikte kullanım:

# Sparse + gzip sıkıştırma
tar -cSzvf /backup/vm_disks.tar.gz /var/lib/libvirt/images/

# Sparse + bzip2 (daha iyi sıkıştırma, daha yavaş)
tar -cSjvf /backup/vm_disks.tar.bz2 /var/lib/libvirt/images/

# Sparse + xz (en iyi sıkıştırma, en yavaş)
tar -cSJvf /backup/vm_disks.tar.xz /var/lib/libvirt/images/

Önemli not: Sıkıştırma algoritmaları zaten sıfır byte dizilerini çok iyi sıkıştırır. Dolayısıyla -Sz kombinasyonu çoğu zaman iyi sonuç verir ama -S olmadan yapılan sıkıştırmada CPU’yu boşuna yorarsınız.

Geri Yükleme Sırasında Sparse Yapıyı Koruma

Arşivden çıkarırken de sparse özelliğini korumak gerekir:

# Sparse yapıyı koruyarak geri yükle
tar -xSvf /backup/sparse_test.tar -C /restore/

# Kontrol et
ls -lh /restore/sparse_test.img    # Görünen: 1GB
du -sh /restore/sparse_test.img    # Gerçek: ~4KB

-S hem arşivleme hem çıkarma sırasında çalışır. Çıkarma sırasında tar, arşivde kayıtlı delik pozisyonlarını okuyarak dosyayı yeniden sparse olarak oluşturur.

cp ile Sparse Dosyaları Kopyalama

cp komutunda sparse davranışını --sparse seçeneği kontrol eder:

  • --sparse=always: Her zaman sparse dosya oluşturmaya çalış (sıfır blokları delik haline getir)
  • --sparse=never: Hiçbir zaman sparse oluşturma, her şeyi yaz
  • --sparse=auto: Varsayılan, kaynak sparse ise hedefi de sparse yap
# Sparse özelliğini koruyarak kopyala
cp --sparse=always /tmp/sparse_test.img /backup/sparse_copy.img

# Doğrula
du -sh /backup/sparse_copy.img  # Küçük göstermeli
ls -lh /backup/sparse_copy.img  # Büyük göstermeli

--sparse=always seçeneği özellikle ilginç: Kaynak sparse olmasa bile, hedef dosyada sıfır bloklarını gerçek blok yerine delik olarak yazar. Bu şu anlama gelir: Büyük bir veritabanı dump dosyasını kopyalarken bile boş alanları sparse’a dönüştürebilirsiniz.

# Sparse olmayan bir dosyayı sparse hale getirerek kopyala
dd if=/dev/zero of=/tmp/non_sparse.img bs=1M count=100
du -sh /tmp/non_sparse.img   # 100MB
cp --sparse=always /tmp/non_sparse.img /tmp/converted_sparse.img
du -sh /tmp/converted_sparse.img  # Muhtemelen 4KB veya daha az

Gerçek Dünya Senaryosu: KVM Sanal Makine Yedekleme

Benim en çok kullandığım alan KVM/libvirt ortamlarıdır. Production’da onlarca sanal makine varken yedekleme stratejisi ciddi önem kazanır.

#!/bin/bash
# kvm_backup.sh - KVM disk imajlarını sparse-aware yedekle

BACKUP_DIR="/mnt/backup/kvm"
IMAGE_DIR="/var/lib/libvirt/images"
DATE=$(date +%Y%m%d_%H%M%S)
LOG="/var/log/kvm_backup.log"

mkdir -p "$BACKUP_DIR"

echo "[$DATE] Backup başladı" >> "$LOG"

for img in "$IMAGE_DIR"/*.img "$IMAGE_DIR"/*.qcow2; do
    [ -f "$img" ] || continue
    
    basename=$(basename "$img")
    backup_file="$BACKUP_DIR/${basename}_${DATE}.tar.xz"
    
    echo "Yedekleniyor: $basename" | tee -a "$LOG"
    
    # Sparse-aware tar ile yedekle
    tar -cSJvf "$backup_file" -C "$IMAGE_DIR" "$basename" 2>> "$LOG"
    
    if [ $? -eq 0 ]; then
        original_size=$(du -sh "$img" | cut -f1)
        backup_size=$(du -sh "$backup_file" | cut -f1)
        echo "Tamamlandı: $basename | Gerçek: $original_size | Yedek: $backup_size" | tee -a "$LOG"
    else
        echo "HATA: $basename yedeklenemedi!" | tee -a "$LOG"
    fi
done

echo "[$DATE] Backup tamamlandı" >> "$LOG"

Bu script’i cron’a ekleyip gece yarısı çalıştırabilirsiniz. Sıfır bayrak olmadan yapılan yedeklemeyle kıyasladığınızda disk tasarrufu dramatik olacaktır.

rsync ile Sparse Dosya Senkronizasyonu

rsync de sparse dosyalar için özel destek sunar. -S veya --sparse bayrağı burada da aynı görevi görür:

# rsync ile sparse-aware senkronizasyon
rsync -avS /var/lib/libvirt/images/ backup-server:/backup/kvm/

# Uzak sunucuya sparse dosya transfer et
rsync -avS --progress /tmp/sparse_test.img user@remote:/backup/

# Bant genişliği sıkıştırması ile birlikte
rsync -avSz /var/lib/libvirt/images/ backup-server:/backup/kvm/

Parametreler:

  • -a: Archive mod (izinler, timestamps, sembolik linkler korunur)
  • -v: Verbose
  • -S: Sparse dosyaları verimli ilet
  • -z: Transfer sırasında sıkıştır

Dikkat: -S ve -z birlikte kullanıldığında rsync önce sıkıştırır, bu da sparse tespitini zorlaştırabilir. Yerel ağda -S yeterlidir; WAN üzerinde -Sz kombinasyonu dengeyi test edilerek kullanılmalıdır.

Sparse Dosya Analizi ve Tanı Araçları

Bir dosyanın sparse olup olmadığını ve ne kadar “delikli” olduğunu anlamak için birkaç araç işe yarar:

# Dosya sistem bloğu bilgisi
stat /tmp/sparse_test.img

# Parça haritası (extent map)
filefrag -v /tmp/sparse_test.img

# Gerçek ve görünen boyut farkını hesapla
apparent=$(ls -s --block-size=1 /tmp/sparse_test.img | awk '{print $1}')
actual=$(stat --format="%b * %B" /tmp/sparse_test.img | bc)
echo "Görünen: $apparent bytes"
echo "Gerçek: $actual bytes"
echo "Tasarruf: $(( (apparent - actual) * 100 / apparent ))%"

# Dizin içindeki tüm sparse dosyaları bul
find /var/lib/libvirt/images -type f -exec bash -c '
  apparent=$(ls -s --block-size=512 "$1" | awk "{print $1}")
  actual=$(stat --format="%b" "$1")
  if [ "$actual" -lt "$apparent" ]; then
    echo "SPARSE: $1 (görünen: $apparent blok, gerçek: $actual blok)"
  fi
' _ {} ;

Bu son komut biraz ağır ama büyük bir storage sunucusunda hangi dosyaların sparse olduğunu listelemek için kullanışlı.

Dosya Sistemi Uyumluluğu Meselesi

Sparse dosyalar her dosya sisteminde aynı verimde çalışmaz. Bazı önemli notlar:

  • ext4: Tam sparse desteği, en yaygın kullanım
  • XFS: Mükemmel sparse desteği, büyük dosyalar için tercih edilir
  • Btrfs: Sparse destekler, ayrıca COW özelliği ile sparse kullanımı verimli
  • FAT32/exFAT: Sparse desteği yok, tüm sıfırlar yazılır
  • NTFS (Linux’ta): Sınırlı sparse desteği
  • NFS: Sunucuya bağlıdır
# Hedef dosya sisteminin sparse destekleyip desteklemediğini test et
test_sparse_support() {
    local target_dir="$1"
    local test_file="$target_dir/.sparse_test_$$"
    
    dd if=/dev/zero of="$test_file" bs=1 count=0 seek=1M 2>/dev/null
    actual_blocks=$(stat --format="%b" "$test_file")
    rm -f "$test_file"
    
    if [ "$actual_blocks" -eq 0 ] || [ "$actual_blocks" -eq 8 ]; then
        echo "$target_dir: Sparse DESTEKLENIYOR"
        return 0
    else
        echo "$target_dir: Sparse DESTEKLENMIYOR (blok sayısı: $actual_blocks)"
        return 1
    fi
}

test_sparse_support /backup
test_sparse_support /mnt/nas_backup

Yedekleme Doğrulama: Bütünlük Kontrolü

Sparse yedeklemelerden sonra mutlaka bütünlük kontrolü yapın:

#!/bin/bash
# sparse_backup_verify.sh

BACKUP_FILE="$1"

if [ -z "$BACKUP_FILE" ]; then
    echo "Kullanım: $0 <backup_dosyasi.tar.xz>"
    exit 1
fi

echo "=== Yedek Dosya Bilgisi ==="
ls -lh "$BACKUP_FILE"
du -sh "$BACKUP_FILE"

echo ""
echo "=== Arşiv İçeriği ==="
tar -tSJvf "$BACKUP_FILE"

echo ""
echo "=== Bütünlük Testi ==="
if tar -tSJf "$BACKUP_FILE" > /dev/null 2>&1; then
    echo "BAŞARILI: Arşiv bütünlüğü doğrulandı"
else
    echo "HATA: Arşiv bozuk olabilir!"
    exit 1
fi

# MD5 checksum
echo ""
echo "=== Checksum ==="
md5sum "$BACKUP_FILE"

Pratik Özet ve Öneriler

Production’da bu konuda öğrendiklerimi maddeler halinde bırakayım:

  • Her zaman -S kullanın: tar ile VM disk, veritabanı dosyası veya herhangi büyük binary dosya yedeklerken -S bayrağını atlamamak alışkanlık haline getirin
  • du vs ls farkını anlayın: Disk kullanımını her zaman du ile kontrol edin, ls -lh yanıltıcıdır
  • Hedef dosya sistemi önemli: NAS’a veya USB disk’e yedek alırken hedefin sparse destekleyip desteklemediğini test edin
  • cp’de --sparse=always güçlüdür: Sparse olmayan büyük dosyaları da dönüştürmek için kullanılabilir
  • rsync ile uzak yedeklerde -S ekleyin: Aksi hâlde hem bant genişliğini hem uzak disk alanını boşa kullanırsınız
  • Geri yükleme testini unutmayın: Sadece yedek almak yetmez, düzenli aralıklarla geri yükleme testi yaparak sparse yapının korunduğunu doğrulayın

Sonuç

Sparse dosyalar, sistem yöneticiliğinde görünmez ama son derece etkili bir konudur. Yanlış yönetildiğinde yedekleme sistemlerinizin diskini gereksiz yere doldurur, yedek transferlerini yavaşlatır ve geri yükleme sürelerini uzatır. Doğru yönetildiğinde ise önemli miktarda disk alanı ve bant genişliği tasarrufu sağlar.

tar -S, cp --sparse=always ve rsync -S üçlüsünü iş akışınıza dahil etmek için gereken tek şey bunları bir kere doğru anlamak ve alışkanlık hâline getirmek. Özellikle KVM/QEMU ortamlarında, büyük veritabanı yedeklemelerinde ve Docker depolama katmanlarıyla çalışırken bu bilgi doğrudan operasyonel maliyetlerinizi etkiler.

Konu basit görünebilir ama yıllar içinde gördüm ki bu “küçük” detayı atlamak, terabaytlık gereksiz yedek verisiyle sonuçlanabiliyor. Bir kere öğren, her zaman uygula.

Bir yanıt yazın

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