Yüksek Load Average ile Düşük CPU Kullanımı: iowait Analizi

Sunucuya bağlandın, top çıktısına baktın ve load average değerleri 8, 12, 15 gibi bir şeylerde. Ama CPU kullanımı yüzde 10-15 civarında. “Bu nasıl iş?” diye düşünüyorsun. İşte bu klasik iowait senaryosu ve birçok sysadmin’in kafasını karıştıran bir durum. Sisteminiz CPU’dan değil, disk veya ağ I/O’sundan boğuluyor. Bu yazıda bu durumu nasıl tespit edeceğimizi, neyin sebep olduğunu ve nasıl çözeceğimizi adım adım inceleyeceğiz.

iowait Nedir, Load Average’ı Neden Yükseltir?

Önce kavramsal kısmı netleştirelim. Load average, sistemdeki çalışan ve çalışmayı bekleyen process sayısının ortalamasıdır. Pek çok kişi bunu sadece CPU kullanımıyla özdeşleştirir, oysa bu yanlıştır.

Linux çekirdeği, bir process’i iki farklı durumda “çalışmayı bekliyor” kategorisine koyar:

  • R (Running/Runnable): CPU almayı bekleyen process’ler, CPU kuyruğundalar
  • D (Uninterruptible Sleep): Bir I/O işleminin tamamlanmasını bekleyen process’ler, uyku halindeler ama uyandırılamıyorlar

İşte bu D state process’ler load average’a dahil edilir. Yani diskinizden 100 MB okuma yapan ve bu işlemin bitmesini bekleyen 10 process varsa, CPU’nuz boşta bile olsa load average 10 görünür. Bu durum tamamen normalin dışında değildir ama belirli bir eşiği aştığında ciddi performans sorunlarına yol açar.

iowait ise CPU’nun bir I/O işleminin tamamlanmasını beklerken boşta kaldığı sürenin yüzdesidir. CPU’nun “ben hazırım ama disk veya ağ henüz veriyi getirmedi” dediği zamandır.

İlk Tespiti Yapmak: Sisteme Bağlandığınızda Ne Yaparsınız?

Sisteme bağlandığınızda ilk yapmanız gereken hızlı bir genel resim almaktır.

# Anlık durum
uptime
top -b -n 1 | head -20

# Process state'lerine göz atmak
ps aux --sort=-%cpu | head -20
ps aux | awk '{print $8}' | sort | uniq -c | sort -rn

ps aux çıktısındaki STAT sütununa bakın. D harfini görüyorsanız iowait probleminizin somut kanıtını buldunuz demektir. Çok sayıda D state process yüksek load’un kaynağını net olarak gösterir.

# Sadece D state process'leri listele
ps aux | awk '$8 ~ /^D/ {print $0}'

# Daha okunabilir format
ps -eo pid,stat,user,comm,wchan | grep "^[0-9]* *D"

wchan sütunu özellikle değerlidir. Process’in hangi kernel fonksiyonunda beklediğini gösterir. ext4_file_read_iter veya blk_mq_get_request gibi değerler görüyorsanız disk I/O beklentisini teyit etmiş olursunuz.

vmstat ile Temel I/O Analizi

vmstat iowait analizi için en hızlı araçlardan biridir. Saniyede bir kez çalıştırıp çıktıyı izleyin:

# 1 saniye aralıklarla 20 kez çalıştır
vmstat 1 20

Çıktının önemli sütunları:

  • r: CPU kuyruğundaki process sayısı (runnable)
  • b: Bloklanmış process sayısı, yani D state’teki sayı
  • wa: iowait yüzdesi
  • bi: Blokların saniyede disk’ten okunma sayısı (blocks in)
  • bo: Blokların saniyede diske yazılma sayısı (blocks out)

b sütununun sürekli yüksek, wa değerinin yüzde 20-30’un üzerinde olduğunu görüyorsanız kesinlikle I/O probleminiz var demektir. r sütunu düşükken b yüksekse zaten CPU bottleneck değil, I/O bottleneck yaşıyorsunuzdur.

iostat: Hangi Disk, Ne Kadar Dolu?

vmstat bize genel resmi verdi. Şimdi hangi diskin sorun çıkardığını bulmamız gerekiyor. Bunun için iostat kullanıyoruz:

# sysstat paketi gerekli: apt install sysstat veya yum install sysstat
iostat -xz 1 10

Bu komut bize her disk cihazı için detaylı metrikler verir. Dikkat etmeniz gereken değerler:

  • %util: Diskin ne kadar meşgul olduğu, yüzde 80-90 üzeri sorunlu
  • await: I/O işleminin ortalama bekleme süresi (ms), HDD için 10-20ms normal, SSD için 1ms altı beklenir
  • r_await / w_await: Okuma ve yazma ayrı ayrı bekleme süreleri
  • aqu-sz (avgqu-sz): Ortalama I/O kuyruğu uzunluğu, 1’in üzeri sorunlu
# Belirli bir diski izlemek
iostat -xz 1 -d /dev/sda

# Sadece yüksek kullanımlı diskleri görmek için
iostat -xz 1 5 | awk '/^sd|^nvme|^vd/ && $NF > 50 {print}'

Gerçek dünya senaryosundan bir örnek verelim: Bir e-ticaret sitesinde gece yedekleme scripti çalıştığında /dev/sdb diskinin %util değeri yüzde 100’e çıkıyor, await 200ms’yi aşıyor ve load average 20’lere fırlıyordu. CPU kullanımı yüzde 5 civarındaydı. Problem yedekleme scriptinin rsync kullanırken --checksum parametresiyle her dosyayı okuyup hash hesaplamasıydı. ionice ile öncelik düşürüldü ve sorun çözüldü.

iotop: Process Bazında I/O Takibi

Hangi process’in diski boğduğunu bulmak için iotop biçilmiş kaftan:

# Root yetkisi gerekli
iotop -o -P

# Sadece aktif I/O yapanları göster, batch mode
iotop -b -o -n 5 -P
  • -o: Sadece aktif I/O yapan process’leri göster
  • -P: Thread yerine process göster
  • -b: Batch mode, script içinde kullanmak için uygun

iotop çıktısında DISK READ ve DISK WRITE sütunları anlık transfer hızını gösterir. IO> sütunu ise o process için toplam I/O bant genişliği kullanımını yüzde olarak verir.

# Yüksek I/O yapan process'leri loglayalım
iotop -b -o -n 30 -d 2 -P >> /var/log/iotop_$(date +%Y%m%d_%H%M%S).log 2>&1 &

Bu komutu bir sorun yaşandığında arka planda çalıştırırsanız, problem 2 dakika boyunca loglanır ve sonradan analiz edebilirsiniz.

/proc Dosya Sisteminden Derinlemesine Analiz

Araçlar güzel ama bazen doğrudan kernel’in bize söylediklerine bakmak daha açıklayıcı olabilir.

# Disk I/O istatistikleri
cat /proc/diskstats

# Belirli bir process'in I/O istatistikleri (PID gerekli)
cat /proc/1234/io

# Tüm process'lerin I/O istatistiklerini toplu görmek
for pid in /proc/[0-9]*/io; do
    proc=$(dirname $pid)
    name=$(cat $proc/comm 2>/dev/null)
    read_bytes=$(grep read_bytes $pid 2>/dev/null | awk '{print $2}')
    write_bytes=$(grep write_bytes $pid 2>/dev/null | awk '{print $2}')
    echo "$name ($(basename $proc)): read=${read_bytes}B write=${write_bytes}B"
done | sort -t= -k2 -rn | head -20

/proc/PID/io dosyasının içindekiler:

  • rchar: Process’in okuduğu toplam byte (buffer/cache dahil)
  • wchar: Process’in yazdığı toplam byte
  • read_bytes: Gerçekten diskten okunan byte
  • write_bytes: Gerçekten diske yazılan byte

rchar ile read_bytes arasındaki fark büyükse process büyük ölçüde cache’den okuyor demektir. Bu iyi bir işarettir. Ama read_bytes yüksekse ve cache hit oranı düşükse asıl problem burada gizlidir.

Dosya Sistemi Açısından Değerlendirme

Bazen sorun tek bir disk değil, dosya sistemi seviyesindedir. Özellikle aynı disk üzerinde çok sayıda küçük dosyaya erişim söz konusuysa metadata işlemleri darboğaz yaratır.

# Hangi dosyalara erişildiğini görmek için
lsof | awk '{print $9}' | sort | uniq -c | sort -rn | head -20

# Belirli bir process'in açık dosyaları
lsof -p 1234

# Bir dizindeki I/O'ya neden olan process'ler
lsof +D /var/log/

Bir başka gerçek dünya senaryosu: Bir MySQL sunucusunda ibdata1 dosyasına sürekli yazma yapıldığı görüldü. lsof ile mysqld process’inin bu dosyaya saniyede binlerce kez eriştiği tespit edildi. Sorunun kökü innodb_flush_log_at_trx_commit=1 ayarıydı. Her transaction’da diske flush yapılıyordu. Değer 2 olarak değiştirildi ve yüksek I/O yükü dramatik biçimde düştü.

blktrace ve blkparse ile Kernel Seviyesi Analiz

Çok daha derin analiz gerekiyorsa blktrace devreye girer. Bu araç kernel’in I/O kuyruğuna ait olayları yakalar:

# /dev/sda üzerindeki I/O'yu 10 saniye kaydet
blktrace -d /dev/sda -w 10 -o /tmp/blktrace_output

# Kaydedilen veriyi analiz et
blkparse -i /tmp/blktrace_output -d /tmp/blktrace.bin
btt -i /tmp/blktrace.bin | head -50

btt (blkparse trace tool) çıktısı size I/O işlemlerinin hangi aşamada ne kadar zaman harcadığını gösterir. Queue time, dispatch time, complete time gibi değerler HDD’nin mekanik gecikme problemlerini veya SSD’nin garbage collection süreçlerini ortaya çıkarabilir.

Bu araç genellikle storage ekibiyle çalışırken veya SAN/NAS problemi şüphesi olduğunda kullanılır. Günlük sorun gidermede genellikle iostat ve iotop yeterlidir.

Yaygın iowait Senaryoları ve Çözümleri

Senaryo 1: Yedekleme Scriptleri

Gece saatlerinde çalışan yedekleme scriptleri en yaygın iowait nedenidir. tar, rsync, mysqldump gibi araçlar büyük miktarda I/O üretir.

# Yedekleme işlemini I/O önceliği düşük çalıştır
ionice -c 3 tar czf /backup/data.tar.gz /var/data/

# rsync ile düşük öncelikli yedekleme
ionice -c 2 -n 7 rsync -av /data/ /backup/

# mysqldump için
ionice -c 3 mysqldump -u root -p database > /backup/db.sql

ionice sınıfları:

  • -c 1: Realtime, en yüksek öncelik
  • -c 2: Best-effort, normal öncelik, -n ile 0-7 arası ince ayar
  • -c 3: Idle, sistem boşta olduğunda çalışır

Senaryo 2: Log Yazma Fırtınası

Bir uygulama hata durumuna girdiğinde saniyede megabyte’larca log yazabilir. Bu hem disk dolumuna hem de iowait’e neden olur.

# Hangi dosyalar hızlı büyüyor
watch -n 2 'ls -la /var/log/*.log | sort -k5 -rn | head -10'

# Log yazma hızını izle
inotifywait -m /var/log/ -e close_write 2>/dev/null | while read dir event file; do
    echo "$(date '+%H:%M:%S') $file değişti"
done

Çözüm genellikle log seviyesini düşürmek veya log rotation’ı daha agresif yapmaktır. logrotate yapılandırmasına compress ve delaycompress eklemek de disk yazma yükünü azaltır.

Senaryo 3: Swap Kullanımı ve Disk Thrashing

Bellek dolduğunda sistem swap’a yazmaya başlar ve bu çok yoğun I/O üretir. Swap üzerinde sürekli okuma/yazma olan bir sistem gerçek anlamda “thrashing” yapıyor demektir.

# Swap kullanımını kontrol et
free -h
swapon --show

# Hangi process'ler swap kullanıyor
for pid in /proc/[0-9]*/status; do
    name=$(grep Name $pid | awk '{print $2}')
    swap=$(grep VmSwap $pid | awk '{print $2}')
    if [ ! -z "$swap" ] && [ "$swap" -gt 0 ] 2>/dev/null; then
        echo "$swap KB - $name ($(basename $(dirname $pid)))"
    fi
done | sort -rn | head -10

Bu senaryoda çözüm disk I/O’yu optimize etmek değil, bellek sorununu çözmektir. Bellek eklemek, memory leak’i olan uygulamayı düzeltmek veya en az bellek kullanan process’leri başka sunucuya taşımak gerekir.

Senaryo 4: Database I/O Problemi

Veritabanları yoğun I/O üretebilir. MySQL/PostgreSQL için ek analiz:

# MySQL için I/O istatistikleri
mysql -u root -p -e "SHOW ENGINE INNODB STATUSG" | grep -A 30 "FILE I/O"

# PostgreSQL buffer cache hit oranı
psql -U postgres -c "
SELECT 
    sum(heap_blks_read) as disk_reads,
    sum(heap_blks_hit) as cache_hits,
    round(sum(heap_blks_hit) * 100.0 / nullif(sum(heap_blks_hit) + sum(heap_blks_read), 0), 2) as cache_hit_ratio
FROM pg_statio_user_tables;"

Cache hit oranı yüzde 90’ın altındaysa veritabanı çok fazla diskten okuma yapıyor demektir. shared_buffers (PostgreSQL) veya innodb_buffer_pool_size (MySQL) değerlerini artırmak çözüm olabilir.

Kalıcı İzleme: Sorun Tekrarlarsa Hazırlıklı Olmak

Sorun anlık olarak çözüldükten sonra tekrarlamasına karşı izleme kurmanız gerekir.

#!/bin/bash
# /usr/local/bin/iowait_monitor.sh
# crontab'a ekle: * * * * * /usr/local/bin/iowait_monitor.sh

THRESHOLD=30
LOGFILE="/var/log/iowait_alert.log"
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

IOWAIT=$(iostat -c 1 2 | tail -1 | awk '{print $4}' | cut -d. -f1)

if [ "$IOWAIT" -gt "$THRESHOLD" ]; then
    echo "[$TIMESTAMP] UYARI: iowait %$IOWAIT" >> $LOGFILE
    
    # Anlık D state process'leri kaydet
    echo "--- D State Processes ---" >> $LOGFILE
    ps -eo pid,stat,user,comm,wchan | grep "^[0-9]* *D" >> $LOGFILE
    
    # Top I/O process'leri kaydet
    echo "--- Top IO Processes ---" >> $LOGFILE
    iotop -b -o -n 1 -P >> $LOGFILE 2>/dev/null
    
    echo "---" >> $LOGFILE
fi

Bu script dakikada bir çalışır, iowait eşiği aşıldığında detaylı bilgiyi loglar. Böylece bir sonraki problem anında elinizde tarihsel veri olur.

Hızlı Triage Özeti

Bir sorunu hızlıca çözmek istediğinizde şu sıralamayla ilerleyin:

  • İlk olarak uptime ile load average’a bakın, CPU sayısıyla kıyaslayın
  • vmstat 1 5 ile b sütunu ve wa değerini kontrol edin
  • ps aux | awk '$8 ~ /^D/' ile D state process’leri listeleyin
  • iostat -xz 1 5 ile hangi diskin yüzde 80+ kullanımda olduğunu bulun
  • iotop -o -P ile hangi process’in buna neden olduğunu tespit edin
  • lsof -p PID ile o process’in hangi dosyalara eriştiğini görün
  • ionice ile gerekirse önceliği düşürün veya process’i restart edin

Sonuç

Yüksek load average ile düşük CPU kombinasyonu, Linux sistem yöneticiliğinde en yanıltıcı durumlardan biridir. Sistemin performans sorunu yaşadığı açıktır ama sebebi CPU’ya bakmakla bulamazsınız. D state process’ler, iowait yüzdesi ve disk kuyruk uzunluğu üçlüsü asıl hikayeyi anlatır.

vmstat, iostat ve iotop üçlüsünü yetkin biçimde kullandığınızda büyük çoğunlukta sorunu 5-10 dakika içinde tespit edebilirsiniz. Yedekleme scriptleri, log fırtınaları, bellek dolması ve veritabanı I/O’su en yaygın nedenlerdir ve her birinin kendine özgü çözüm yöntemi vardır.

En önemli öğreti şudur: Load average tek başına bir şey söylemez. Her zaman bileşenlere ayırarak bakın, r mı yüksek yoksa b mi, bu soruyu sormak sizi doğru yöne götürür.

Benzer Konular

Bir yanıt yazın

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