Arşiv İşlemlerinde Çıktıyı Doğrudan Başka Bir Komuta Aktarma: tar ve Pipe Kullanımı

Üretim ortamında bir sunucudan diğerine dosya taşımak gerektiğinde, klasik yaklaşım şu üçlüyü takip eder: arşivle, kopyala, karşı tarafta aç. Yıllarca bu döngüyü yaşadıktan sonra fark ettim ki aradaki geçici dosya hem disk alanı istiyor hem de gereksiz bir adım ekliyor. tar ve pipe kombinasyonu bu sorunu ortadan kaldırıyor ve bir kez alışınca bir daha eski yönteme dönmek istemiyorsunuz.

Bu yazıda tar komutunun stdout’a yazma ve stdin’den okuma yeteneklerini, pipe mekanizmasıyla birlikte gerçek dünya senaryoları üzerinden ele alacağım.

Temel Mantığı Anlamak

tar normalde bir dosyaya yazar. Ama -f - dediğinizde “dosya olarak standart çıktıyı kullan” demek oluyor. Bu basit numara, tar‘ı pipeline’ın bir parçasına dönüştürüyor.

Benzer şekilde, bir tar akışını stdin’den okumak için de aynı -f - parametresini kullanıyorsunuz, sadece bu sefer x (extract) moduyla.

# Klasik yöntem: geçici dosya oluşturur
tar czf /tmp/yedek.tar.gz /var/www/html
scp /tmp/yedek.tar.gz sunucu2:/tmp/
ssh sunucu2 "tar xzf /tmp/yedek.tar.gz -C /var/www/"

# Pipe yöntemi: geçici dosya yok
tar czf - /var/www/html | ssh sunucu2 "tar xzf - -C /var/www/"

İkinci yöntemin farkı sadece satır sayısı değil. Disk üzerinde hiçbir zaman tam arşiv oluşturulmuyor, veri doğrudan akıyor. Büyük dizinlerde bu ciddi bir fark yaratıyor.

Sık Kullanılan Parametreler

Pipe senaryolarında öne çıkan tar parametrelerini bilmek gerekiyor:

  • -c: Arşiv oluştur (create)
  • -x: Arşivden çıkar (extract)
  • -z: gzip sıkıştırması uygula
  • -j: bzip2 sıkıştırması uygula
  • -J: xz sıkıştırması uygula
  • -f –: Dosya olarak standart giriş/çıkışı kullan
  • -C dizin: Belirtilen dizine çıkar
  • -p: İzinleri koru (extract sırasında)
  • -v: Ayrıntılı çıktı ver (pipe’da dikkatli kullanın, stdout’u kirletir)
  • –exclude=pattern: Belirtilen kalıpla eşleşen dosyaları dışla

-v parametresi hakkında önemli bir not düşeyim: Pipe kullandığınızda -v çıktısı stdout’a gider ve veri akışını bozabilir. Ya hiç kullanmayın ya da -v çıktısını stderr’e yönlendirin. Bazı durumlarda 2>/dev/null eklemek de işe yarar ama sorunları gizler, dikkatli olun.

Senaryo 1: Sunucular Arası Dizin Kopyalama

En yaygın kullanım alanı bu. Bir dizini, arşiv dosyası oluşturmadan başka bir sunucuya taşımak.

# /var/www/html dizinini uzak sunucuya kopyala
tar czf - /var/www/html | ssh [email protected] "tar xzf - -C /var/www/"

# İzinleri ve sahipliği koru
tar cpzf - /var/www/html | ssh [email protected] "tar xpzf - -C /var/www/"

Burada -p parametresi kritik. Özellikle web uygulaması dosyalarında, www-data veya nginx kullanıcısına ait izinlerin korunması gerekiyor. -p olmadan her şey aktaran kullanıcının sahipliğine geçer.

SSH bant genişliğini daha verimli kullanmak istiyorsanız, sıkıştırmayı SSH’ın kendi sıkıştırmasına bırakabilirsiniz:

# SSH kendi sıkıştırmasını yapsın (-C parametresi SSH'a ait)
tar cf - /var/www/html | ssh -C kullanici@sunucu "tar xf - -C /var/www/"

Ancak bu yaklaşımda dikkatli olun. Zaten sıkıştırılmış dosyalarınız varsa (jpg, mp4, zip gibi), çift sıkıştırma yapmak CPU’yu boşuna yorar ve zaman kaybettirir.

Senaryo 2: Anlık Yedek Alma ve Başka Komutlarla Entegrasyon

tar çıktısını sadece ssh‘a değil, herhangi bir komuta aktarabilirsiniz. Özellikle pv, gpg, openssl gibi araçlarla kombinasyon çok işe yarıyor.

# Yedek alırken ilerlemeyi göster (pv kurulu olmalı)
tar czf - /home/kullanici | pv | ssh yedek-sunucu "tar xzf - -C /home/"

# Yedek alırken şifrele ve uzak sunucuya gönder
tar czf - /etc | gpg --symmetric --passphrase "gizlisifre" | 
  ssh yedek-sunucu "cat > /yedek/etc_$(date +%Y%m%d).tar.gz.gpg"

# openssl ile şifreleme
tar czf - /var/lib/mysql | 
  openssl enc -aes-256-cbc -pbkdf2 -k "guclusifre" | 
  ssh yedek-sunucu "cat > /yedek/mysql_$(date +%Y%m%d).enc"

pv (pipe viewer) özellikle büyük dizinlerde çok işe yarıyor. Kaç MB geçti, hız ne, tahmini süre ne, bunların hepsini gösteriyor. Üretim yedeklerinde kullanılabilirlik açısından değerli bir araç.

Senaryo 3: Yerel Pipeline ile Dönüştürme

Aynı makine üzerinde de pipe kullanımı mantıklı olabiliyor. Örneğin bir sıkıştırma formatından diğerine geçmek istiyorsunuz:

# gzip'ten bzip2'ye dönüştür
zcat arsiv.tar.gz | bzip2 > arsiv.tar.bz2

# Daha doğrusu tar ile:
tar czf - kaynak_dizin | bzip2 > arsiv.tar.bz2

# xz ile maksimum sıkıştırma
tar cf - /var/log/nginx | xz -9 > nginx_loglar.tar.xz

Ya da belirli dosyaları filtreleyerek arşivlemek:

# Sadece .conf dosyalarını arşivle ve başka bir dizine çıkar
tar cf - /etc --include="*.conf" | tar xf - -C /tmp/konfigurasyon_yedek/

Bu son örnek biraz alışılmadık görünebilir ama “tar’dan tar’a” aktarım bazı filtreleme senaryolarında gerçekten kullanışlı.

Senaryo 4: Netcat ile Hızlı Transfer

SSH şifrelemesi bazen darboğaz oluşturuyor. Güvenli bir iç ağda (örneğin aynı vlan üzerindeki sunucular arasında) hızlı transfer gerektiğinde netcat devreye giriyor:

# Alıcı taraf (önce başlatılmalı)
nc -l -p 9999 | tar xzf - -C /hedef/dizin/

# Gönderici taraf
tar czf - /kaynak/dizin | nc alici-sunucu-ip 9999

SSH’a kıyasla ciddi hız farkı var. Özellikle 10GbE ağlarda, şifreleme yükü olmadan transferler çok daha hızlı tamamlanıyor. Tabii ki bu yaklaşım sadece güvendiğiniz ağlarda kullanılmalı, veri şifresiz gidiyor.

Biraz daha güvenli ama yine de hızlı bir yöntem için mbuffer veya socat tercih edilebilir:

# socat ile TLS olmadan ama daha kontrollü
socat TCP-LISTEN:9999,fork - | tar xzf - -C /hedef/

# Gönderici
tar czf - /kaynak | socat - TCP:alici:9999

Senaryo 5: Docker ve Container Senaryoları

Modern altyapılarda tar pipe kullanımı container dünyasında da karşımıza çıkıyor.

# Çalışan container'dan dosyaları çıkar
docker exec konteyner_adi tar czf - /app/data | tar xzf - -C /tmp/konteyner_yedek/

# Yerel dizini container'a aktar
tar czf - /yerel/config | docker exec -i konteyner_adi tar xzf - -C /app/config/

# Bir container'dan diğerine kopyala
docker exec kaynak_konteyner tar czf - /app | 
  docker exec -i hedef_konteyner tar xzf - -C /app

docker cp komutunu biliyorum, kullanıyorum da. Ama bazen --exclude ile belirli dosyaları dışarıda bırakmak ya da izinleri korumak gerektiğinde tar pipe çok daha fazla esneklik sunuyor.

Senaryo 6: Yedek Doğrulama Pipeline’ı

Bir yedek alıp aynı anda bütünlüğünü doğrulamak isteyebilirsiniz. Bunun için tee komutunu pipeline’a dahil edebilirsiniz:

# Yedek alırken aynı anda MD5 hesapla
tar czf - /kritik/veri | tee /yedek/kritik_veri.tar.gz | md5sum > /yedek/kritik_veri.md5

# SHA256 ile
tar czf - /kritik/veri | tee /yedek/kritik_veri.tar.gz | sha256sum > /yedek/kritik_veri.sha256

# İlerlemeyi gösterirken hem kaydet hem doğrula
tar czf - /kritik/veri | pv | tee /yedek/kritik_veri.tar.gz | sha256sum

Bu yaklaşımda tee komutu veriyi ikiye kopyalıyor: bir kopyası dosyaya gidiyor, diğeri md5sum‘a aktarılıyor. Böylece tek geçişte hem yedek alınmış hem de checksum üretilmiş oluyor.

Hata Yönetimi ve Pipeline Güvenilirliği

Pipe kullanımında hata yönetimi kritik bir konu. Varsayılan bash davranışında, pipeline’daki herhangi bir komut başarısız olsa bile script çalışmaya devam edebilir.

# Pipefail ile sıkı hata kontrolü
set -o pipefail

tar czf - /kaynak/dizin | ssh hedef "tar xzf - -C /hedef/"
if [ $? -ne 0 ]; then
  echo "Transfer basarisiz!" >&2
  exit 1
fi

set -o pipefail eklendiğinde, pipeline’daki herhangi bir komut sıfır dışı çıkış kodu döndürürse tüm pipeline başarısız sayılır. Script yazarken bunu alışkanlık haline getirmek gerekiyor.

Ayrıca transfer sırasındaki bağlantı kesilmelerini handle etmek için:

#!/bin/bash
set -euo pipefail

KAYNAK="/var/www/html"
HEDEF_SUNUCU="192.168.1.50"
HEDEF_DIZIN="/var/www/"

echo "Transfer basliyor: $(date)"

if tar czf - "$KAYNAK" | ssh -o ConnectTimeout=30 
   -o ServerAliveInterval=15 
   root@"$HEDEF_SUNUCU" "tar xzf - -C $HEDEF_DIZIN"; then
  echo "Transfer basarili: $(date)"
else
  echo "HATA: Transfer basarisiz! Cikis kodu: $?" >&2
  exit 1
fi

-o ServerAliveInterval=15 parametresi SSH bağlantısının büyük transferlerde zaman aşımına uğramasını önlüyor. Uzun süren transferlerde bu can kurtarıcı.

Performans Karşılaştırması ve Doğru Sıkıştırma Seçimi

Hangi sıkıştırma algoritmasını seçeceğiniz duruma göre değişiyor:

  • gzip (-z): Hız ve sıkıştırma oranı arasında iyi denge. Çoğu senaryoda ilk tercih.
  • bzip2 (-j): gzip’e göre daha iyi sıkıştırma, daha yavaş. Log dosyaları için tercih edilebilir.
  • xz (-J): En iyi sıkıştırma oranı, en yavaş. Arşivleme ve uzun süreli depolama için.
  • Sıkıştırmasız (-f -): Zaten sıkıştırılmış veriler veya çok hızlı ağlarda mantıklı olabilir.

Bant genişliği kısıtlıysa daha iyi sıkıştırma, işlemci kısıtlıysa daha hızlı sıkıştırma tercih edin. 1 Gbps üzeri ağlarda genellikle işlemci darboğaz oluşturuyor, bu yüzden sıkıştırmasız ya da gzip düzey 1 ile gitmek daha mantıklı:

# Hızlı ağ için minimum sıkıştırma
tar cf - /kaynak | gzip -1 | ssh hedef "tar xzf - -C /hedef/"

# Paralel gzip ile çok çekirdekli işlemci kullanımı (pigz kurulu olmalı)
tar cf - /kaynak | pigz | ssh hedef "tar xzf - -C /hedef/"

pigz (parallel gzip) çok çekirdekli sistemlerde sıkıştırma hızını önemli ölçüde artırıyor. Büyük arşivlerde farkı hissediyorsunuz.

Exclude Kalıplarıyla Akıllı Arşivleme

Pipeline senaryolarında genellikle her şeyi değil, belirli dosyaları taşımak istiyorsunuz:

# node_modules ve .git dizinlerini dışla
tar czf - /proje 
  --exclude="./node_modules" 
  --exclude="./.git" 
  --exclude="*.log" | ssh hedef "tar xzf - -C /projeler/"

# Belirli bir tarihten sonra değişen dosyaları arşivle
tar czf - /var/log --newer-mtime="2024-01-01" | 
  ssh yedek-sunucu "cat > /yedek/son_loglar.tar.gz"

--newer-mtime parametresi incremental yedekleme benzeri bir senaryo oluşturuyor. Tam incremental yedekleme için rsync daha uygun olsa da, belirli durumlarda bu yaklaşım işe yarıyor.

Gerçek Bir Göç Senaryosu

Birkaç yıl önce, 200 GB’lık bir web uygulaması veritabanı ve dosya sunucusunu yeni bir makineye taşımam gerekti. Klasik yöntemle gitsem geçici dosya için disk yerim yoktu. Şöyle bir pipeline kurdum:

#!/bin/bash
set -euo pipefail

KAYNAK_SUNUCU="eski-sunucu"
HEDEF_SUNUCU="yeni-sunucu"

# Önce DB yedei al ve aktar
ssh root@$KAYNAK_SUNUCU "mysqldump --single-transaction --all-databases | gzip" | 
  ssh root@$HEDEF_SUNUCU "gzip -d | mysql"

# Ardından dosyaları taşı
ssh root@$KAYNAK_SUNUCU "tar czf - /var/www" | 
  pv -s $(ssh root@$KAYNAK_SUNUCU "du -sb /var/www" | cut -f1) | 
  ssh root@$HEDEF_SUNUCU "tar xzf - -C /"

echo "Goc tamamlandi."

pv -s parametresine toplam boyutu verince ilerleme çubuğu da doğru gösteriyor. O 200 GB transfer sırasında ekranda gerçek zamanlı ilerleme görmek çok rahatlık veriyor.

Sonuç

tar ve pipe kombinasyonu, sistem yöneticisinin araç kutusunda sessiz sedasız ama çok değerli bir yer tutuyor. Geçici dosya oluşturma zorunluluğunu ortadan kaldırması, birden fazla aracı tek bir veri akışında zincirleyebilmesi ve ağ transferlerini şifreleme, sıkıştırma, doğrulama adımlarıyla tek hamlede yapabilmesi bu yaklaşımı vazgeçilmez kılıyor.

Özellikle şu durumlar için bu yöntemi güçlü bir alternatif olarak değerlendirmenizi öneririm:

  • Disk alanının kısıtlı olduğu sunucu göç senaryoları
  • Büyük dizinlerin ağ üzerinden hızlı transferi
  • Şifreli ve doğrulanmış yedekleme pipeline’ları
  • Container ortamlarında dosya transferleri

Alışınca bir daha geçici dosya oluşturup silme döngüsüne dönmek istemiyorsunuz. Pipeline düşünmek, Linux felsefesinin özünü yaşamak gibi bir şey: küçük araçları birbirine bağla, büyük işler yap.

Bir yanıt yazın

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