reklam
Linux

iostat ve top Yalan Söylediğinde: eBPF (bcc ve bpftrace) ile Canlı Üretim Ortamında Mikrosaniyelik Disk ve Ağ Analizi

4 dk okuma
reklam

Kurumsal üretim ortamlarında en tehlikeli performans sorunları, izleme araçlarınızda (Zabbix, Prometheus/Grafana) her şey yemyeşil görünürken uygulama katmanından gelen “Veritabanı sorguları arada bir 500ms takılıyor” veya “API yanıt sürelerinde anlık sıçramalar (tail latency) var” şikayetleridir. Sunucuya bağlanıp iostat -xz 1 veya top çalıştırdığınızda ortalama disk gecikmesinin (r_await / w_await) 0.8 ms, CPU kullanımının ise %25 olduğunu görürsünüz.
Peki sorun nerede? Geleneksel araçlar /proc dosya sistemindeki sayaçları belirli aralıklarla (örneğin 1 saniyede bir) okuyup aritmetik ortalama alır. Saniyede 10.000 I/O yapılan bir NVMe diskte 9.995 işlem 100 mikrosaniyede (µs) tamamlanırken, 5 işlem 200 milisaniye (ms) takılırsa iostat size ortalamayı “mükemmel” olarak raporlar. İşte bu aykırı değerleri (outliers) ve çekirdek içi gizli darboğazları canlı üretim ortamında sıfıra yakın yükle yakalamanın tek yolu eBPF (Extended Berkeley Packet Filter) teknolojisidir.
1. Hazırlık: Kurumsal Linux Dağıtımlarında BCC ve bpftrace Kurulumu
Modern Linux çekirdeklerinde (Kernel 5.4+ / Ubuntu 22.04-24.04 / RHEL 8-9) BTF (BPF Type Format) desteği yerleşik geldiği için çoğu araç artık devasa kernel-headers paketlerine bile ihtiyaç duymadan (CO-RE – Compile Once, Run Everywhere) çalışır.

# Ubuntu / Debian tabanlı sistemler için
sudo apt update && sudo apt install -y bpfcc-tools linux-headers-$(uname -r) bpftrace

# RHEL / Rocky / AlmaLinux tabanlı sistemler için
sudo dnf install -y bcc-tools bpftrace

Not: Ubuntu’da BCC araçları /usr/sbin/ altında -bpfcc sonekiyle (biolatency-bpfcc gibi), RHEL tabanlı sistemlerde ise /usr/share/bcc/tools/ altında doğrudan kendi adlarıyla yer alır.
2. Disk Katmanı: Ortalamaları Bırakıp Dağılıma (Histogram) Bakmak
A. biolatency: Blok Katmanı Gecikme Histogramı
iostat üzerindeki tek bir ortalama rakam yerine, blok katmanına (blk-mq) inen tüm I/O isteklerinin kaç mikrosaniye sürdüğünü logaritmik bir dağılımla görmek için biolatency kullanılır:

# Disk I/O gecikmelerini mikrosaniye (-u) cinsinden, disk bazlı (-D) olarak 10 saniye boyunca topla
sudo biolatency-bpfcc -u -D 10 1

Örnek Çıktı:

reklam
disk = 'nvme0n1'
usecs : count distribution
0 -> 1 : 0 | |
2 -> 3 : 0 | |
4 -> 7 : 1420 |**** |
8 -> 15 : 12850 |****************************************|
16 -> 31 : 4120 |************ |
32 -> 63 : 310 | |
...
16384 -> 32767 : 14 | |
32768 -> 65535 : 4 | |

Bu çıktı bize ne söylüyor? İsteklerin ezici çoğunluğu 8-31 mikrosaniye arasında tamamlanırken (All-Flash NVMe performansı), 18 adet istek 16ms ile 65ms arasına takılmış! Yani uygulama ekibinin şikayet ettiği anlık takılmalar gerçek ve tamamen blok katmanındaki kuyruk/donanım gecikmesinden kaynaklanıyor (bimodal distribution).
B. biosnoop ve ext4slower / xfsslower: Suçlu Süreci (PID) ve Dosyayı Yakalamak
Histogramda gecikmeyi gördük; peki bu 30ms üzeri gecikmeye hangi süreç (PostgreSQL mi, log-shipper mı, backup ajanı mı) ve hangi dosya sebep oluyor?
Yukarıdaki gecikmeyi dosya sistemi katmanında (VFS -> XFS/EXT4) filtreleyerek sadece 10 milisaniyeyi aşan işlemleri canlı ekrana düşürelim:

# XFS dosya sisteminde 10ms'den (varsayılan) daha uzun süren tüm okuma/yazma işlemlerini yakala
sudo xfsslower-bpfcc 10

Örnek Çıktı:

TIME     COMM           PID    T BYTES   OFF_KB   LAT(ms) FILENAME
14:12:01 mariadbd 4182 S 0 0 28.44 ib_logfile0
14:12:04 filebeat 1902 R 65536 104820 14.12 app-access.log

Burada T sütunundaki S (Sync – fsync/fdatasync) harfi, mariadbd sürecinin ib_logfile0 dosyasını diske flush ederken 28.44 ms beklediğini doğrudan kanıtlar.
3. Ağ Katmanı: Kuyrukları, Yeniden İletimleri ve Gecikmeleri İzlemek
netstat -s veya ss komutları size sistemde toplam kaç adet TCP Retransmission (yeniden iletim) olduğunu söyler, ancak hangi IP’ye, hangi porta ve hangi TCP durumunda (state) paket kaybı yaşandığını söylemez.
A. tcpretrans: Paket Kaybı Yaşayan Bağlantıları Canlı Yakalamak
Arista/Cisco switch portlarında veya WAN/Firewall geçişlerinde mikro-patlamalar (microburst) yüzünden düşen paketleri anında yakalamak için:

# Çekirdek seviyesinde gerçekleşen her TCP Retransmit olayını anlık olarak listele (-l ile TLP dahil)
sudo tcpretrans-bpfcc -l

Çıktıda kaynak IP:Port, hedef IP:Port ve bağlantının o anki durumu (ESTABLISHED veya SYN_SENT) doğrudan listelenir. Eğer SYN_SENT durumunda yoğun retransmit görüyorsanız karşı tarafın somaxconn / syn_backlog kuyruğu taşmış veya arada bir Firewall SYN paketi düşürüyor demektir.
B. tcplife: Kısa Ömürlü Bağlantıların Gerçek Bant Genişliği ve Süresi
top veya iftop gibi araçlar milisaniyeler içinde açılıp kapanan (short-lived) TCP bağlantılarını yakalayamaz. tcplife, TCP el sıkışmasından (SYN) kapanışına (FIN/RST) kadar geçen süreyi, hangi süreç (PID/COMM) tarafından açıldığını ve aktarılan toplam TX_KB/RX_KB miktarını sıfır paket yakalama (pcap) yüküyle raporlar:

# Sadece belirli bir portu (Örn: 5432 PostgreSQL veya 9200 OpenSearch) hedefleyen oturumları izle
sudo tcplife-bpfcc -D 5432

4. bpftrace ile Kendi Tek Satırlık (One-Liner) Cerrahi Neşterinizi Yazın
BCC araçları hazır betikler sunarken, bpftrace çekirdek içindeki herhangi bir tracepoint veya kprobe noktasına anında kanca (hook) atmanızı sağlar.
Senaryo 1: Hangi Süreç Disk Kuyruğunu Blokluyor? (Block I/O Size Histogram)
Sistemde hangi sürecin küçük rastgele (4K) I/O, hangisinin devasa sıralı (1MB+) I/O göndererek SAN/NVMe kuyruğunu doldurduğunu bulmak için:

sudo bpftrace -e 'tracepoint:block:block_rq_issue { @[comm] = hist(args->bytes); }'

Ctrl+C yaptığınızda her süreç (comm) için ayrı ayrı byte cinsinden I/O boyutu histogramı ekrana basılır.
Senaryo 2: fsync() Fırtınası Yapan Süreçleri Yakalamak
Ceph, ZFS veya SAN üzerinde yazma IOPS’unu tüketen en büyük düşman aşırı fsync() / fdatasync() çağrılarıdır. Hangi uygulamanın saniyede kaç kez diski senkronize etmeye zorladığını 5 saniye içinde sayalım:

sudo bpftrace -e 'tracepoint:syscalls:sys_enter_fsync, tracepoint:syscalls:sys_enter_fdatasync { @[comm, probe] = count(); } interval:s:5 { exit(); }'
reklam