inode Nedir ve inode Tükenmesi Sorunları Nasıl Çözülür

Bir gece yarısı üretim sunucusuna bağlandığınızda “No space left on device” hatası alıyorsunuz, ama df -h çıktısına bakıyorsunuz ve diskler rahat rahat nefes alıyor. %60, %70 doluluk. Peki sorun ne? İşte o an inode tükenmesiyle tanışıyorsunuz. Yıllar içinde bu senaryoyu onlarca kez yaşadım ve her seferinde “keşke bunu daha önce bilseydim” diyen bir sistem yöneticisi gördüm. Bu yazıda inode kavramını kökleriyle anlatacağım, tükenme senaryolarını gerçek örneklerle inceleyeceğiz ve çözüm yollarını adım adım göreceğiz.

inode Nedir?

Linux dosya sisteminde her dosya ve dizin iki ayrı şeyden oluşur: verinin kendisi ve veriye ait meta bilgiler. Meta bilgileri tutan yapıya inode (index node) denir. Dosya adı bile inode’da saklanmaz; dosya adı sadece dizin girdisinde bulunur ve o girdi ilgili inode numarasına işaret eder.

Bir inode içinde şunlar saklanır:

  • Dosya türü: regular file, directory, symlink, block device vs.
  • İzinler: rwxr-xr-x gibi
  • Sahip bilgisi: UID ve GID
  • Boyut: byte cinsinden
  • Zaman damgaları: atime, mtime, ctime
  • Hard link sayısı
  • Veri bloklarının adresleri: Dosyanın disk üzerindeki fiziksel konumu

Dosya adı bu listede yok dikkat ettiyseniz. Bu yüzden aynı inode’a birden fazla dizin girdisi bağlanabilir, yani hard link mantığı tam olarak buradan geliyor.

inode Sayısı Neden Sınırlıdır?

ext4, XFS gibi dosya sistemleri oluşturulurken inode tablosu için ayrılan alan sabit tutulur. mkfs.ext4 ile bir disk bölümü formatladığınızda, toplam alan ve inode yoğunluğu parametrelerine göre belirli sayıda inode slotu önceden tahsis edilir. Varsayılan ayarlarda her 16 KB disk alanı için bir inode üretilir. 100 GB’lık bir bölümde teorik olarak yaklaşık 6.5 milyon inode olur. Bu çok gibi görünür, değil mi? Ama yanlış iş yüklerinde bu sayı şaşırtıcı hızda biter.

inode Durumunu Kontrol Etmek

Önce mevcut durumu görmek lazım. Temel araçlarla başlayalım.

df -i

Bu komut her mount noktası için inode kullanımını gösterir. Çıktı şöyle görünür:

Filesystem      Inodes  IUsed   IFree IUse% Mounted on
/dev/sda1      6553600 6553598       2  100% /
/dev/sdb1      1310720  245000 1065720  19% /data
tmpfs           988234     854  987380   1% /run

İlk satırda drama var: 6.5 milyon inode’dan sadece 2 tanesi kalmış. Disk doluluk oranına bakmak yetmez, her zaman inode durumuna da bakmak gerekir.

Belirli bir dizinde kaç tane inode kullanıldığını görmek için:

df -i /var

Sistemdeki tüm dosya sistemlerini tek seferde inode kullanımıyla görmek isterseniz:

df -i --output=source,itotal,iused,iavail,ipcent,target

Bir dosyanın inode numarasını öğrenmek için:

ls -i /etc/passwd

Çıktı şöyle olur: 131073 /etc/passwd – soldaki sayı inode numarası.

Hangi Dizin Kaç inode Yiyor?

inode tükenmesi yaşandığında suçluyu bulmak gerekir. Bunun için dizin bazında inode sayısını hesaplayan komutlara ihtiyacınız var.

En pratik yöntem şu:

find / -xdev -printf '%hn' | sort | uniq -c | sort -rn | head -20

Bu komut her dosyanın üst dizinini alır, sayar ve en çok dosya barındıran 20 dizini sıralar. -xdev parametresi önemli, o olmadan mount noktalarının içine de girer ve karmaşık çıktı alırsınız.

Sadece belirli bir dizin altına bakmak istiyorsanız:

find /var -xdev -printf '%hn' | sort | uniq -c | sort -rn | head -20

Alternatif olarak dizin başına toplam dosya sayısını şöyle görebilirsiniz:

for dir in /var/*/; do
    count=$(find "$dir" -maxdepth 5 | wc -l)
    echo "$count $dir"
done | sort -rn | head -10

Bu biraz daha yavaş çalışır ama okunması kolay çıktı verir.

Gerçek Hayat Senaryoları

Senaryo 1: PHP Session Dosyaları

PHP uygulamalarında session yönetimi varsayılan olarak dosya tabanlıdır. Her kullanıcı oturumu /var/lib/php/sessions/ ya da /tmp/ altında küçük bir dosya oluşturur. Trafik yoğun bir sitede günde milyonlarca session dosyası birikebilir. Eski sessionları temizleyen garbage collection her zaman istenen hızda çalışmaz.

Bunu tespit etmek için:

ls /var/lib/php/sessions/ | wc -l

Çıktı olarak 2 milyon gördüm bir keresinde. Dosyaların çoğu 24 saatten eskiydi:

find /var/lib/php/sessions/ -type f -mtime +1 | wc -l

Temizlemek için:

find /var/lib/php/sessions/ -type f -mtime +1 -delete

Bu komut 24 saatten eski session dosyalarını siler. Bunu cron job’a bağlamak lazım tabii:

# /etc/cron.d/php-session-cleanup
0 3 * * * root find /var/lib/php/sessions/ -type f -mtime +1 -delete

Senaryo 2: Mail Queue Patlaması

Postfix veya başka bir MTA kullanan sunucularda mail kuyruğu patlaması inode’ları bitirebilir. /var/spool/postfix/ altındaki kuyruk dizinleri binlerce küçük dosyayla dolabilir.

find /var/spool/postfix/ -type f | wc -l

Mail queue temizleme ayrı bir konu ama acil durum için:

postsuper -d ALL deferred

Senaryo 3: Log ve Temp Dosyaları

/tmp ve /var/tmp altında biriken küçük geçici dosyalar da ciddi inode tüketebilir. Özellikle hatalı yazılmış uygulamalar temizlik yapmadan çıktığında bu dizinler kabarır.

find /tmp -type f -atime +7 -delete
find /var/tmp -type f -atime +7 -delete

inode Tükenmesini Çözmek: Kalıcı Yöntemler

Temizlik geçici çözüm, esas mesele tekrar yaşanmaması. Bunu sağlamak için birkaç farklı yol var.

Dosya Sistemini Yeniden Oluşturmak

Eğer diskte yeteri kadar alan var ama inode sayısı yetersizse ve diski kullanılabilir durumdaysa, dosya sistemini daha fazla inode ile yeniden oluşturmak gerekir. Bu tabii ki veri kaybı riski taşıdığından önce yedek almak şart.

mkfs.ext4 ile inode yoğunluğunu değiştirmek:

# bytes-per-inode parametresi küçüldükçe daha fazla inode oluşturulur
# Varsayılan 16384, bunu 4096'ya indirmek 4x daha fazla inode sağlar
mkfs.ext4 -i 4096 /dev/sdb1

-i 4096: Her 4 KB disk alanı için bir inode oluşturur, varsayılan 16 KB’a göre çok daha fazla inode kapasitesi sağlar.

Bu işlemi yapmadan önce mevcut inode sayısını ve oranı görmek için:

tune2fs -l /dev/sda1 | grep -i inode

XFS Kullanmayı Değerlendirin

XFS dosya sistemi inode’ları dinamik olarak tahsis eder. Yani başlangıçta sabit bir inode tablosu ayırmaz, ihtiyaç oldukça inode alanı büyür. Bu ext4’e göre ciddi bir avantajdır. Eğer inode tükenmesi kronik bir sorun haline geldiyse ve yeni bir disk bölümü oluşturma şansınız varsa XFS’i tercih edebilirsiniz:

mkfs.xfs /dev/sdc1

XFS’te inode durumunu görmek için:

xfs_info /data

Çıktıda icount değeri mevcut inode sayısını, ifree ise boş olanları gösterir.

/tmp için tmpfs Kullanmak

/tmp dizinini RAM’de tmpfs olarak mount etmek hem performans hem de inode yönetimi açısından avantajlıdır. /etc/fstab‘a şu satırı ekleyebilirsiniz:

tmpfs /tmp tmpfs defaults,noatime,nosuid,size=2G 0 0

tmpfs inode sayısını nr_inodes parametresiyle kontrol edebilirsiniz:

tmpfs /tmp tmpfs defaults,noatime,nosuid,size=2G,nr_inodes=1M 0 0

Bu ayar 1 milyon inode sınırı koyar. Özellikle uygulama sunucularında /tmp altındaki dosya patlamasını sınırlamak için işe yarar.

İzleme ve Uyarı Sistemi Kurmak

Yangın çıktıktan sonra değil, çıkmadan önce müdahale etmek lazım. Basit bir bash script ile inode kullanımını izleyebilirsiniz:

#!/bin/bash
# /usr/local/bin/check_inodes.sh

THRESHOLD=85
ALERT_EMAIL="[email protected]"

df -i | grep -vE '^Filesystem|tmpfs|udev|devtmpfs' | while read line; do
    usage=$(echo "$line" | awk '{print $5}' | tr -d '%')
    mount=$(echo "$line" | awk '{print $6}')
    
    if [ -n "$usage" ] && [ "$usage" -gt "$THRESHOLD" ]; then
        echo "UYARI: $mount inode kullanimi %$usage" | 
            mail -s "inode Uyarisi - $(hostname)" "$ALERT_EMAIL"
    fi
done

Bu script’i cron ile saatte bir çalıştırın:

0 * * * * root /usr/local/bin/check_inodes.sh

Prometheus ve Grafana kullanıyorsanız node_exporter zaten node_filesystem_files_free ve node_filesystem_files metriklerini toplar. Grafana’da alert kuralı oluşturmak için:

(node_filesystem_files_free{mountpoint="/"} / node_filesystem_files{mountpoint="/"}) < 0.15

Bu kural kök dosya sisteminde inode’ların %85’i kullanıldığında alarm verir.

Derin Analiz: İnode Avcılığı

Bazen sorunun kaynağı hemen belli olmaz. Özellikle karmaşık uygulama ortamlarında hangi process’in dosya ürettiğini bulmak gerekir. inotifywait bu iş için biçilmiş kaftan:

inotifywait -m -r --format '%w%f %e' /tmp 2>/dev/null | 
    grep -E "CREATE" | 
    awk '{print $1}' | 
    while read file; do
        ls -la "$file" 2>/dev/null
    done

Bu komut /tmp altında anlık olarak oluşturulan dosyaları izler. Kimin ne ürettiğini görmek için auditd daha güçlü bir seçenek:

auditctl -w /var/spool/ -p w -k spool_writes
ausearch -k spool_writes | tail -50

lsof ile çok sayıda dosya açan process’leri bulmak da faydalıdır:

lsof | awk '{print $2}' | sort | uniq -c | sort -rn | head -10

Sol sütun açık dosya sayısını, sağ sütun PID’i gösterir. Anormal bir sayı görürseniz o PID’i incelemeniz gerekir.

Anlık Kurtarma: Disk Doluyken Dosya Silmek

inode tükendiğinde yeni dosya oluşturamaz, hatta bazı komutlar çalışmaz hale gelir. Şöyle bir durum hayal edin: disk inode’ları doldu, siz de temizlik yapmak için bir shell script yazmak istiyorsunuz ama “cannot create temp file” diyor. Ne yapacaksınız?

Önce var olan bir dosyayı boşaltarak başlayın:

> /var/log/büyük_log_dosyası.log

Bu komut yeni bir dosya oluşturmaz, var olanı sıfırlar. inode kullanmadan yer açar. Ardından gerçek temizliği yapabilirsiniz.

Başka bir taktik: eğer silmeniz gereken dosyaların hangi dizinde olduğunu biliyorsanız ve dizin girdisi sayısı çok fazlaysa, find ile silmek yerine dizini tamamen taşıyıp yenisini oluşturabilirsiniz:

mv /var/lib/php/sessions /var/lib/php/sessions.old
mkdir /var/lib/php/sessions
chmod 1733 /var/lib/php/sessions
# Arka planda eski dizini temizleyin
rm -rf /var/lib/php/sessions.old &

Çok sayıda dosyayı silmek için find -delete yerine rsync hilesini de kullanabilirsiniz:

mkdir /tmp/empty_dir
rsync -a --delete /tmp/empty_dir/ /var/lib/php/sessions/
rmdir /tmp/empty_dir

Bu yöntem özellikle milyonlarca dosyası olan dizinlerde rm -rf‘den çok daha hızlı çalışır çünkü rsync’in dizin okuma algoritması farklıdır.

Hard Link ve Sembolik Link ile inode Anlamak

Şu komutu çalıştırın ve çıktıya bakın:

ls -li /bin/sh /bin/bash /bin/dash

Hard link sayısına bakın. Eğer /bin/sh ve /bin/dash aynı inode numarasına sahipse bunlar aynı dosyanın iki farklı ismidir. Tek bir inode iki farklı dizin girdisiyle erişilebilir durumdadır.

Sembolik linkler ise kendi inode’larına sahiptir ve hedef dosyaya bir işaret içerirler. Bu yüzden büyük sayıda sembolik link de inode tüketir. Sisteminizdeki tüm sembolik linkleri saymak için:

find / -xdev -type l | wc -l

Broken symlink’leri bulmak da zaman zaman temizlik açısından işe yarar:

find /usr /etc /var -xdev -type l -xtype l 2>/dev/null

Proaktif Önlemler

Tükenmeden önce alınabilecek önlemler:

  • Uygulama log rotasyonu: Logrotate’in düzgün çalıştığından emin olun. Çok sayıda küçük log dosyası birikiyor olabilir.
  • Paket manager cache: apt clean veya yum clean all ile paket önbelleklerini düzenli temizleyin.
  • Docker image ve container temizliği: Her docker run komutu dosya sistemi katmanları oluşturabilir. docker system prune düzenli çalıştırılmalı.
  • Coredump yönetimi: Çöken uygulamalar core dump bırakır. /proc/sys/kernel/core_pattern ve systemd-coredump ayarlarını gözden geçirin.
  • Uygulama geliştirme standartları: Geliştirme ekibine geçici dosya oluşturup silme konusunda standartlar koyun. Her dosya oluşturma işlemi için cleanup mekanizması zorunlu olmalı.

Sonuç

inode tükenmesi, disk doluluk sorununa göre çok daha sinsi davranır çünkü klasik disk izleme araçları sizi uyarmaz. df -h yeşil, du -sh normal, ama sistem dosya oluşturamıyor. Bu yüzden monitoring altyapınıza inode izlemeyi mutlaka ekleyin.

Özetlemek gerekirse:

  • Sorun tespiti için her zaman df -i ile başlayın
  • Suçluyu bulmak için find / -xdev -printf '%hn' | sort | uniq -c | sort -rn komutunu kullanın
  • PHP session, mail queue ve temp dosyaları en yaygın suçlulardır
  • Kalıcı çözüm için ya dosya sistemi yeniden oluşturulur ya da XFS’e geçilir
  • tmpfs ile /tmp mount etmek birçok sorunu kökten çözer
  • İzleme olmadan yönetim olmaz, %85 eşiğinde alarm kurun

Bir gece yarısı paniğiyle değil, önceden hazırlıklı olarak bu sorunla karşılaşmanız dileğiyle. İnode tükenme olaylarınız ve çözüm yöntemleriniz varsa yorumlarda paylaşın, birlikte öğrenelim.

Bir yanıt yazın

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