Arşivleme İşlemlerinde FIFO ve Named Pipe Kullanımı: Geçici Dosya Oluşturmadan Sıkıştırma Pipeline’ı Kurma
Disk alanı her zaman sınırlıdır. Özellikle büyük log arşivleri, veritabanı yedekleri veya medya dosyaları üzerinde çalışırken “önce sıkıştır, sonra aktar” ya da “önce oluştur, sonra işle” gibi geçici dosya bağımlı iş akışları hem zaman hem de disk kaybına yol açar. Oysa Linux’un bize sunduğu FIFO (named pipe) mekanizması, bu geçici dosya cehenneminden çıkmanın en zarif yolu. Bugün bu konuyu hem teorik hem de gerçek dünya senaryolarıyla masaya yatıracağız.
FIFO Nedir ve Neden Önemlidir?
FIFO, “First In, First Out” kelimelerinin kısaltmasıdır ve Unix/Linux dünyasında named pipe olarak da bilinir. Anonim pipe’lardan (| operatörü) temel farkı, dosya sistemi üzerinde gerçek bir isimle görünmesidir. Yani bir süreç bir uca yazarken, tamamen bağımsız başka bir süreç diğer uçtan okuyabilir.
Normal bir pipe’ta iki komutun aynı komut satırında bulunması gerekir. FIFO’da ise süreçler birbirinden bağımsız olabilir, farklı terminallerde hatta farklı zamanlarda çalışabilirler. Bu özellik, arşivleme pipeline’larında olağanüstü esneklik sağlar.
# FIFO oluşturmanın iki yolu
mkfifo /tmp/arsiv_pipe
mknod /tmp/arsiv_pipe p # Eski yöntem, ama hala çalışır
FIFO’yu oluşturduktan sonra ls -la ile baktığınızda dosya tipinin p olduğunu görürsünüz:
ls -la /tmp/arsiv_pipe
# prw-r--r-- 1 user user 0 Ara 15 14:23 /tmp/arsiv_pipe
Başındaki p harfi, bu “dosyanın” aslında bir pipe olduğunu gösterir. Boyutu sıfır, çünkü bu bir depolama alanı değil, veri akış kanalıdır.
Geçici Dosya Olmadan Sıkıştırma: Temel Mantık
Klasik iş akışında şöyle bir senaryo düşünün: 50GB’lık bir dizini önce tar ile arşivleyip ardından sıkıştırıyorsunuz.
# GEÇİCİ DOSYA YAKLAŞIMI - kaçınılması gereken
tar -cf /tmp/yedek_gecici.tar /var/log/uygulama/
gzip /tmp/yedek_gecici.tar
mv /tmp/yedek_gecici.tar.gz /backup/yedek.tar.gz
Bu yaklaşımda hem 50GB geçici dosya oluşturdunuz hem de işlem iki aşamalı olduğu için iki kez disk I/O yaptınız. Sistem yöneticiliğinde disk I/O genellikle darboğazın ta kendisidir.
Anonim pipe ile bunu şöyle yapabilirsiniz:
# ANONİM PIPE - tek komut satırı sınırlaması var
tar -cf - /var/log/uygulama/ | gzip -9 > /backup/yedek.tar.gz
Buradaki -cf - ifadesinde ikinci tire, stdout’a yaz anlamına gelir. Peki ya bu iki işlemi birbirinden bağımsız yönetmeniz gerekiyorsa? İşte FIFO devreye giriyor.
FIFO ile Bağımsız Süreç Pipeline’ı
Gerçek bir senaryo düşünelim: Bir üretim sunucusunda geceleri çalışan bir yedekleme scripti var. Sıkıştırma işlemi CPU açısından ağır, arşivleme ise I/O ağır. Bu iki işlemi farklı nice değerleriyle çalıştırmak istiyorsunuz.
# FIFO oluştur
mkfifo /tmp/yedek_kanal
# Terminal 1'de - I/O ağır iş, düşük öncelik gerekmez
tar -cf /tmp/yedek_kanal /var/log/uygulama/ &
# Terminal 2'de - CPU ağır iş, yüksek nice değeri ile
nice -n 15 gzip -9 < /tmp/yedek_kanal > /backup/yedek.tar.gz
# İşlem bittikten sonra temizlik
rm /tmp/yedek_kanal
Burada güzel olan şu: tar süreci /tmp/yedek_kanal‘a yazıyor, gzip süreci ise aynı kanaldan okuyor. İkisi arasında hiçbir geçici dosya yok. Veri doğrudan kernel’ın pipe buffer’ı üzerinden akıyor.
Paralel Sıkıştırma ile Performans Optimizasyonu
Modern sunucularda çok çekirdekli işlemciler neredeyse standart. Ama klasik gzip tek çekirdek kullanır. FIFO kullanarak paralel sıkıştırma araçlarıyla çok daha verimli pipeline’lar kurabilirsiniz.
# pigz (parallel gzip) ile FIFO kullanımı
mkfifo /tmp/paralel_pipe
# Arka planda tar başlat
tar -cf /tmp/paralel_pipe /veri/buyuk_dizin/ &
TAR_PID=$!
# Tüm çekirdekleri kullanan pigz ile sıkıştır
pigz -9 -p $(nproc) < /tmp/paralel_pipe > /backup/buyuk_arsiv.tar.gz
# tar'ın düzgün kapandığını kontrol et
wait $TAR_PID
echo "Çıkış kodu: $?"
rm /tmp/paralel_pipe
$(nproc) ifadesi sistemdeki mantıksal çekirdek sayısını döndürür. 8 çekirdekli bir sistemde gzip‘e kıyasla 4-6 kat hızlanma görmek mümkün. Bu fark, özellikle günlük terabayt ölçeğinde yedekleme yapan ortamlarda çok ciddi bir operasyonel kazanım.
Çok Aşamalı Pipeline: Bir FIFO’dan Fazlası
Bazen arşivleme işlemi sadece sıkıştırmaktan ibaret değildir. Şifreli ve sıkıştırılmış yedek oluşturmak istiyorsunuz diyelim. Klasik yöntemde önce sıkıştırırsınız, sonra şifrelersiniz, iki geçici dosya daha. FIFO zinciri ile:
# İki FIFO oluştur
mkfifo /tmp/pipe_sikistir
mkfifo /tmp/pipe_sifrele
# Aşama 1: tar -> pipe_sikistir
tar -cf /tmp/pipe_sikistir /kritik_veriler/ &
# Aşama 2: pipe_sikistir -> bzip2 -> pipe_sifrele
bzip2 -9 < /tmp/pipe_sikistir > /tmp/pipe_sifrele &
# Aşama 3: pipe_sifrele -> openssl -> hedef dosya
openssl enc -aes-256-cbc -pbkdf2 -pass pass:$YEDEK_SIFRE
< /tmp/pipe_sifrele > /backup/kritik_sifreliyedek.tar.bz2.enc
# Temizlik
rm /tmp/pipe_sikistir /tmp/pipe_sifrele
Bu pipeline’da üç bağımsız süreç çalışıyor ve aralarında sıfır geçici dosya var. Veri kernel buffer’larından geçerek akıyor. 100GB’lık kritik veriyi bu şekilde işlediğinizde disk tasarrufu da ciddi boyutlara ulaşıyor.
Gerçek Dünya Senaryosu: Log Rotasyonu ve Uzak Sunucuya Transfer
Birçok ortamda log dosyaları yerel olarak sıkıştırılıp uzak bir arşiv sunucusuna gönderilir. Bu senaryo tipik olarak şöyle işler: sıkıştır, aktif ağ bağlantısını bekle, gönder, sil. FIFO ile bu işlemi tek seferde yapabilirsiniz:
#!/bin/bash
# log_arsivle.sh
LOG_DIZIN="/var/log/nginx"
UZAK_SUNUCU="[email protected]"
UZAK_YOL="/arsiv/nginx"
TARIH=$(date +%Y%m%d_%H%M%S)
PIPE_YOLU="/tmp/log_pipe_$$" # $$ = PID, benzersiz isim
# Temizlik fonksiyonu
temizle() {
rm -f "$PIPE_YOLU"
}
trap temizle EXIT
# FIFO oluştur
mkfifo "$PIPE_YOLU"
# Yerel sıkıştırma yok, doğrudan SSH üzerinden aktar
tar -czf "$PIPE_YOLU" "$LOG_DIZIN"/ &
TAR_PID=$!
# FIFO'dan oku ve SSH ile uzak sunucuya yaz
ssh "$UZAK_SUNUCU" "cat > ${UZAK_YOL}/nginx_${TARIH}.tar.gz" < "$PIPE_YOLU"
# tar'ın durumunu kontrol et
wait $TAR_PID
if [ $? -eq 0 ]; then
echo "Başarılı: nginx_${TARIH}.tar.gz aktarıldı"
find "$LOG_DIZIN" -name "*.log" -mtime +7 -delete
else
echo "HATA: tar başarısız oldu" >&2
exit 1
fi
Bu scriptte birkaç önemli nokta var. $$ ile pipe dosyasına benzersiz isim veriliyor, böylece birden fazla script eş zamanlı çalışsa da çakışma olmaz. trap temizle EXIT ile script herhangi bir şekilde sonlansa da (hata dahil) FIFO temizleniyor. Uzak sunucuda herhangi bir özel yazılım gerektirmiyor, salt cat yeterli.
tee Komutu ile Çoklu Hedef
Bazen aynı arşivi hem yerel hem de uzak hedefe göndermek, ya da hem sıkıştırılmış hem de sıkıştırılmamış halini saklamak gerekir. tee komutu FIFO ile birleşince bu çok temiz bir hal alıyor:
# Aynı arşivi hem yerel hem uzak hedefe gönder
mkfifo /tmp/tee_pipe1
mkfifo /tmp/tee_pipe2
tar -cf - /onemli_veriler/ | tee /tmp/tee_pipe1 /tmp/tee_pipe2 > /dev/null &
# Pipe 1'den gzip ile yerel yedek
gzip -9 < /tmp/tee_pipe1 > /backup/yerel_yedek.tar.gz &
# Pipe 2'den xz ile uzak yedek (daha iyi sıkıştırma oranı, yavaş)
xz -9 < /tmp/tee_pipe2 | ssh backup@uzak-sunucu "cat > /backup/uzak_yedek.tar.xz"
wait
rm /tmp/tee_pipe1 /tmp/tee_pipe2
Burada tee komutu gelen veriyi iki farklı FIFO’ya aynı anda yazıyor. Bir tarafta hız odaklı gzip, diğer tarafta sıkıştırma oranı odaklı xz çalışıyor. İkisi de aynı kaynak veriden beslendiği için tar yalnızca bir kez çalışıyor, disk sadece bir kez okunuyor.
Dikkat Edilmesi Gereken Durumlar
FIFO kullanırken bazı kritik noktalar var. Bunları görmezden gelmek script’lerinizin sessizce askıda kalmasına neden olabilir.
Blocking davranışı: FIFO, okuyucu ve yazıcı hazır olmadan bloke eder. Eğer bir ucu açarsanız ve diğer ucu kimse açmazsa süreciniz bekler. Bu yüzden genellikle bir tarafı & ile arka plana almak gerekir.
Hata yönetimi: Pipe’ın bir ucundaki süreç çökerse, diğer taraf SIGPIPE sinyali alır. Scriptlerinizde set -o pipefail kullanmak bu durumları yakalamak için kritik:
#!/bin/bash
set -euo pipefail
PIPE="/tmp/guvenli_pipe_$$"
mkfifo "$PIPE"
trap "rm -f $PIPE" EXIT
tar -cf "$PIPE" /veri/ &
TAR_PID=$!
gzip -9 < "$PIPE" > /backup/cikti.tar.gz
GZIP_STATUS=$?
wait $TAR_PID
TAR_STATUS=$?
if [ $TAR_STATUS -ne 0 ] || [ $GZIP_STATUS -ne 0 ]; then
echo "Pipeline hatası: tar=$TAR_STATUS, gzip=$GZIP_STATUS" >&2
rm -f /backup/cikti.tar.gz # Eksik dosyayı temizle
exit 1
fi
İzin sorunları: FIFO dosyası oluşturan kullanıcı ile onu kullanan kullanıcı farklı olabilir. Özellikle cron job’larında bu sorun sık yaşanır. Varsayılan izinler 644, ama genellikle yazıcı için 622 ya da doğru grup ataması ile bu sorun çözülür.
Pipe buffer boyutu: Linux’ta varsayılan pipe buffer boyutu 64KB (bazı sistemlerde 1MB’a kadar çıkabilir). Çok yavaş tüketilen bir FIFO, yazıcı süreci buffer dolduğunda bloke eder. Bu genellikle sorun değildir ama gerçek zamanlı izleme yapıyorsanız göz önünde bulundurulmalı.
Process Substitution ile Karşılaştırma
Bash’in process substitution özelliği (<() ve >()) de benzer şeyler yapabilir, ancak önemli farklar var:
# Process substitution ile (bash'e özgü)
tar -cf >(gzip -9 > /backup/yedek.tar.gz) /veri/
# FIFO ile (POSIX uyumlu, daha taşınabilir)
mkfifo /tmp/p
tar -cf /tmp/p /veri/ &
gzip -9 < /tmp/p > /backup/yedek.tar.gz
rm /tmp/p
Process substitution daha kısa ve okunabilir, ama bash’e özgü. Scriptiniz dash, sh veya başka bir POSIX kabuğunda çalışacaksa FIFO tercih edilmeli. Ayrıca FIFO, süreçlerin tamamen bağımsız lifecycle’lara sahip olduğu karmaşık senaryolarda daha iyi kontrol sunar.
Büyük Ölçekli Yedekleme: Veritabanı Dump + Sıkıştırma
PostgreSQL veya MySQL dump işlemlerinde FIFO kullanımı son derece yaygın ve etkilidir:
#!/bin/bash
# db_yedekle.sh - Geçici dosya olmadan veritabanı yedeği
DB_ADI="uretim_db"
YEDEK_DIZIN="/backup/db"
TARIH=$(date +%Y%m%d)
PIPE="/tmp/db_pipe_$$"
mkfifo "$PIPE"
trap "rm -f $PIPE" EXIT
# PostgreSQL dump -> FIFO -> paralel sıkıştırma
pg_dump -U postgres -Fc "$DB_ADI" > "$PIPE" &
DUMP_PID=$!
# FIFO'dan al, sıkıştır ve şifrele
pigz -9 < "$PIPE" |
openssl enc -aes-256-gcm -pbkdf2 -iter 100000
-pass file:/etc/backup/sifre.key
> "${YEDEK_DIZIN}/${DB_ADI}_${TARIH}.dump.gz.enc"
wait $DUMP_PID
if [ $? -eq 0 ]; then
echo "DB yedeği başarılı: ${DB_ADI}_${TARIH}"
# Son 7 günden eski yedekleri temizle
find "$YEDEK_DIZIN" -name "*.dump.gz.enc" -mtime +7 -delete
fi
Bu yaklaşımla veritabanı dump’ı RAM’e ya da diske hiç yazılmadan, doğrudan sıkıştırılıp şifreleniyor. Büyük prod veritabanlarında bu, işlem süresini ve disk kullanımını dramatik biçimde düşürüyor.
FIFO Kullanımını İzleme ve Debug
FIFO ile çalışırken bazen pipeline’ın nerede takıldığını anlamak güçleşebilir. Birkaç pratik debug tekniği:
# lsof ile FIFO'ya hangi süreçlerin bağlı olduğunu gör
lsof /tmp/yedek_pipe
# FIFO'nun kernel içindeki durumunu kontrol et
cat /proc/$(pgrep tar)/fdinfo/1 # tar'ın stdout fd'si
# pv ile pipe üzerinden geçen veriyi izle (pipe viewer)
mkfifo /tmp/izlenen_pipe
tar -cf /tmp/izlenen_pipe /buyuk_veri/ &
pv < /tmp/izlenen_pipe | gzip -9 > /backup/cikti.tar.gz
rm /tmp/izlenen_pipe
pv (pipe viewer) aracı, FIFO üzerinden geçen veri hızını, tahmini tamamlanma süresini ve işlenen toplam veriyi gerçek zamanlı gösterir. Uzun süren yedekleme işlemlerinde ne kadarının tamamlandığını görmek için vazgeçilmez.
Sonuç
FIFO ve named pipe’lar, Linux sistem yönetiminin en az fark edilen ama en zarif araçlarından biri. Arşivleme ve sıkıştırma pipeline’larında kullanıldığında üç kritik avantaj sağlıyor: disk alanı tasarrufu, azaltılmış I/O yükü ve bağımsız süreçler arasında temiz veri akışı.
Pratik özeti şöyle çıkarabiliriz:
- Tek komut satırında halledilebilen işlemler için anonim pipe (
|) yeterli - Bağımsız süreçler veya farklı terminal oturumları arasında veri aktarımı için FIFO kullanın
- Çok çekirdekli sistemlerde
pigzve FIFO kombinasyonu tek çekirdekligzip‘e göre dramatik hız artışı sağlar - Her zaman
trapile temizlik mekanizması ekleyin, askıda kalan FIFO’lar sorun yaratır set -o pipefailile pipeline hatalarını mutlaka yakalayınpvaracını uzun işlemlerin izlenmesi için pipeline’a ekleyin
Geçici dosya bağımlı eski alışkanlıklar, özellikle disk alanının kısıtlı olduğu ya da I/O’nun darboğaz oluşturduğu ortamlarda ciddi operasyonel maliyete dönüşüyor. FIFO tabanlı pipeline’lar bu maliyeti ortadan kaldırıyor ve scriptlerinizi daha okunabilir, daha güvenilir hale getiriyor. Bir sonraki yedekleme scriptini yazarken geçici dosya oluşturmadan önce bir dakika durup “Bunu bir FIFO ile yapabilir miyim?” diye sormaya değer.
