Saat gece yarısı, izleme sisteminden gelen uyarı e-postası "CPU 5 dakikadır %98 üzerinde" diyor. SSH ile bağlanıyorsunuz, top yazıyorsunuz ve listenin tepesinde %CPU sütunu 380 gösteren bir satır duruyor. İşte tam bu noktada çoğu rehber sizi yalnız bırakır: süreci bulmayı anlatır, sonrası için "gereksizse sonlandırın" der ve biter. Oysa asıl karar burada başlar.
Çünkü o satırdaki süreç iki şeyden biri olabilir. Ya işini yapan meşru bir servistir — php-fpm, mysqld, node, convert — ve gerçek soru "bu süreç neden bu kadar çalışıyor" sorusudur; onu öldürmek belirtiyi bastırır, sebebi bırakır. Ya da sistemde olmaması gereken bir şeydir: dışarıdan çalıştırılmış bir kripto madencisi, bir bot ajanı, silinmiş bir ikili dosyadan koşan bir süreç. Bu ikisini karıştırmak pahalıya patlar. Birincisinde gereksiz yere servisinizi kesersiniz; ikincisinde süreci öldürüp "çözüldü" dersiniz, oysa sisteme giriş yolu hâlâ açıktır ve süreç on dakika sonra geri gelir.
Bu yazıda süreci bulduktan sonrasını anlatıyorum: hangi sinyallere bakarak meşru/şüpheli ayrımını yapacağınızı, meşru çıktığında süreçten kullanıcıya, kullanıcıdan erişim loguna veya yavaş sorguya inen zinciri, şüpheli çıktığında ise konuyu dağıtmadan nereye devredeceğinizi. Araçların temel kullanımını ayrı ele aldığımız için burada hızlı geçeceğim; ps, top ve htop ile ilk temas için süreç izleme rehberine bakabilirsiniz.
%100 Derken Hangi %100'e Bakıyorsunuz#
top çıktısının üçüncü satırındaki %Cpu(s) alanı tek bir sayı değil, yükün nereye gittiğini ayrıştıran bir dağılımdır. Bu dağılımı okumadan alınan her karar tahmindir. Sunucuya girdiğinizde göreceğiniz satır şuna benzer:
%Cpu(s): 71.2 us, 9.4 sy, 0.0 ni, 14.1 id, 4.9 wa, 0.0 hi, 0.3 si, 0.1 st
Bu alanların anlamı ve size ne söylediği:
| Alan | Adı | Yüksekse ne demek |
|---|---|---|
us | user | Uygulama kodu çalışıyor: PHP, Python, derleme, görüntü işleme, madencilik |
sy | system | Çekirdek işi ağır: aşırı sistem çağrısı, ağ paketi, bağlam değişimi |
ni | nice | Önceliği düşürülmüş işler; genelde toplu iş veya cron görevleri |
id | idle | Boşta kalan pay — düşükse CPU gerçekten dolu |
wa | iowait | CPU disk veya ağ bekliyor. Bu CPU darboğazı değildir, disk darboğazıdır |
si | softirq | Ağ trafiği yoğun; DDoS veya yüksek paket hızının tipik izi |
st | steal | Hipervizör CPU'yu başkasına verdi. Sizin sürecinizin suçu yok |
İki alan özellikle yanlış teşhise yol açar. wa yüksek ve us düşükse CPU'yu büyütmek hiçbir şey değiştirmez; sorun diskte veya ağdadır, CPU sadece bekliyordur. st sürekli %10'un üzerindeyse sanal sunucunuz aşırı paylaşımlı bir fiziksel makinede oturuyor demektir; içeride ne yaparsanız yapın kaybettiğiniz süreyi geri alamazsınız, bu bir sağlayıcı konusudur.
Bir de load average ile CPU doluluğunu karıştırmamak gerekir: load average çalışan süreçlerin yanında disk bekleyenleri de sayar, bu yüzden CPU %20'deyken load 12 olabilir. İkisi arasındaki farkı ve çekirdek sayısına göre normalize etmeyi load average yazısında ayrıntılı ele aldık.
Çekirdek bazlı dağılımı görmek için top açıkken 1 tuşuna basın veya sysstat paketindeki aracı kullanın:
# Her çekirdeğin ayrı ayrı dağılımı, 5 kez 1 saniyelik örnek
mpstat -P ALL 1 5
Tek çekirdeğin dolu, diğerlerinin boş olması çok tipik bir tablodur ve genellikle tek iş parçacıklı bir işi (eski bir cron betiği, tek thread'li bir sıkıştırma) işaret eder. Böyle bir durumda CPU sayısını artırmak toplam kapasiteyi büyütür ama o işi hızlandırmaz. Bütün çekirdeklerin eşit dolu olması ise ya gerçekten paralel bir iş yükünü ya da kapasite yetersizliğini gösterir.
Yükü Üreten Süreci Kesin Olarak Yakalamak#
top canlı bakış için iyidir ama karşılaştırılabilir, kopyalanabilir bir çıktı istiyorsanız ps daha kullanışlıdır. Aşağıdaki komut gerekli tüm sütunları sıralı biçimde tek seferde verir:
ps -eo pid,ppid,user,%cpu,%mem,etimes,stat,args --sort=-%cpu | head -15
Buradaki etimes sütunu altın değerindedir: sürecin kaç saniyedir çalıştığını verir. Sunucu iki aydır ayakta ama tepedeki süreç 900 saniyedir çalışıyorsa, o süreç sunucu açılışında başlamamıştır — biri veya bir şey onu 15 dakika önce başlatmıştır. Bu tek sütun, "bu normal bir servis mi" sorusunun yarısını cevaplar.
Anlık %cpu değeri yanıltıcı olabilir, çünkü ps bunu sürecin tüm ömrü boyunca ortalar. Az önce yükselmiş bir süreç düşük görünür. Gerçek anlık ölçüm için:
# Süreç bazında saniyelik örnekleme (sysstat paketi)
pidstat -u 1 5
# Sadece belirli bir PID'yi izle
pidstat -u -p 12345 1
%CPU sütununda 100'ün üzerinde değer görmek normaldir: 400 demek, dört çekirdeği tam doyuran çok iş parçacıklı bir süreç demektir. top çalışırken I tuşuyla Irix modunu kapatarak bu değeri toplam kapasiteye oranlı görebilirsiniz.
Bu Süreç Meşru mu, Gizlenmiş Bir Madenci mi#
Kripto madencileri artık xmrig adıyla ortalıkta dolaşmıyor. Kendilerini kdevtmpfsi, kinsing, [kworker/0:2], hatta nginx gibi isimlendiriyorlar. İsim tek başına hiçbir şey ifade etmez; ikili dosyanın nerede durduğu ve kime bağlandığı ise neredeyse her zaman kesin cevabı verir.
Şüphelendiğiniz PID için sırayla şu dört kontrolü yapın:
PID=12345
# 1) İkili dosya diskte nerede? Meşru servisler /usr/sbin, /usr/bin, /opt altındadır
ls -l /proc/$PID/exe
# 2) Süreç hangi dizinde çalışıyor?
ls -l /proc/$PID/cwd
# 3) Tam komut satırı (ps kısaltabilir, burası kısaltmaz)
tr '\0' ' ' < /proc/$PID/cmdline; echo
# 4) Nereye bağlanıyor?
ss -tunp | grep "pid=$PID"
Çıktıları şu tabloya göre yorumlayın:
| Sinyal | Meşru servis | Şüpheli süreç |
|---|---|---|
exe yolu | /usr/sbin/php-fpm, /usr/sbin/mysqld | /tmp, /dev/shm, /var/tmp, /home/kullanici/.cache |
exe sonunda (deleted) | Sadece güncelleme sonrası, servis yeniden başlatılmamışsa | Neredeyse her zaman kötü niyet işareti |
| Çalışma dizini | /, /var/www/... | /tmp, rastgele adlı gizli dizin |
| Süreç sahibi | www-data, mysql, nginx | root ama servis dışı, ya da beklenmedik bir site kullanıcısı |
| Ağ bağlantısı | 80, 443, 3306 gibi bilinen portlar | 3333, 4444, 5555, 14444 gibi havuz portları, tanımadığınız IP'ler |
Üst süreç (ppid) | systemd (PID 1) veya ilgili servisin ana süreci | Bir web sunucusu süreci, bir kabuk, ya da systemd birimi olmayan yetim süreç |
/tmp veya /dev/shm altından çalışan bir ikili dosya tek başına en güçlü sinyaldir. Hiçbir paket yöneticisi oraya kurulum yapmaz. Aynı şekilde ls -l /proc/$PID/exe çıktısının sonunda (deleted) etiketi görmek, ikili dosyanın çalıştırıldıktan sonra diskten silindiğini gösterir; bu, adli incelemeyi zorlaştırmak için kullanılan klasik bir tekniktir.
Bir kontrol daha: süreç listesini gizleyen bir araç kurulmuş olabilir. Çekirdeğin gördüğü süreç sayısı ile ps'in gördüğü sayı arasında ciddi bir fark varsa, ps size yalan söylüyor olabilir:
# /proc altındaki gerçek süreç sayısı
ls -d /proc/[0-9]* | wc -l
# ps'in raporladığı sayı
ps -e --no-headers | wc -l
Birkaç fark normaldir (komutlar çalışırken süreç sayısı değişir), on kat fark normal değildir.
Şüpheli Çıktıysa: Burada Durun ve Devredin#
Süreç şüpheli görünüyorsa teşhis bitmiştir ve konu artık performans değil, bir güvenlik olayıdır. Bu noktadaki en yaygın hata, süreci hemen kill ile öldürüp rahatlamaktır. Öldürürsünüz, birkaç dakika sonra geri gelir; çünkü onu geri getiren şey bir cron kaydı, bir systemd birimi ya da hâlâ açık olan giriş yoludur. Üstelik süreci öldürmekle bellekteki kanıtı da yok edersiniz.
Sırasıyla şunu yapın:
- Süreci öldürmeden önce kanıtı toplayın.
ls -l /proc/$PID/exe,cmdlinevess -tunpçıktılarını bir dosyaya kaydedin. Sanallaştırma panelinizden anlık görüntü alabiliyorsanız alın. - Kalıcılık noktalarını çıkarın.
crontab -l,crontab -l -u KULLANICI,/etc/cron.d/,/var/spool/cron/,systemctl list-units --type=service --state=runningve~/.ssh/authorized_keysdosyaları ilk bakılacak yerlerdir. - Ağı kesin, servisi değil. Madencinin havuza ulaşmasını güvenlik duvarıyla engellemek, süreci öldürmekten daha bilgilendiricidir: süreç ayakta kalır, kanıt durur, yük düşer.
- Sonra temizlik prosedürüne geçin. Buradan itibaren yapılacaklar tam bir kurtarma sürecidir ve ayrı bir konudur. Hacklenmiş site kurtarma yazısı sırayı ve şifre/anahtar yenileme adımlarını anlatıyor; dosya sistemindeki kalıntıları taramak içinse malware tarama ve temizleme rehberi işinizi görür.
Kimin ne zaman hangi dosyayı çalıştırdığını geriye dönük görebilmek istiyorsanız, olaydan sonra değil önceden kurulması gereken bir altyapı var: auditd ile sistem denetimi. Bu yazının konusu buradan sonrası değil, ayrımı yapabilmenizdi; ayrım netse yolunuza doğru rehberle devam edin.
Meşru Süreç Çıktıysa: PHP-FPM'den İsteğe İnmek#
Tepedeki süreç php-fpm ise gerçek soru "hangi PHP isteği" sorusudur. Süreç adı bunu söylemez ama iki kaynak söyler: FPM'in yavaş istek günlüğü ve web sunucusunun erişim günlüğü.
Önce yavaş istek günlüğünü açın. Havuz yapılandırma dosyanızda (/etc/php/8.3/fpm/pool.d/www.conf gibi):
; 5 saniyeden uzun süren isteğin PHP yığın izini kaydet
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/www-slow.log
; İsteğin toplam süresini sınırla (sonsuz döngüye karşı sigorta)
request_terminate_timeout = 120s
Servisi yeniden yükledikten sonra günlükte yavaş isteğin hangi dosyanın hangi satırında takıldığını görürsünüz. Bu, "site yavaş" şikâyetini tek bir fonksiyona indiren en hızlı yoldur. Havuzun süreç sayısı ve pm modu ayarları da doğrudan CPU davranışını etkiler; bir sonraki durak olarak PHP-FPM havuz ayarları yazısına bakın.
İkinci kaynak erişim günlüğüdür. Yükün bir bottan mı yoksa gerçek trafikten mi geldiğini şu iki komut çoğu zaman anında gösterir:
# Son 20.000 istekte en çok çağrılan yollar
tail -20000 /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -20
# En çok istek atan IP adresleri
tail -20000 /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20
Tek bir IP'nin toplam isteğin yarısını ürettiğini görmek, konuyu performans ayarından hız sınırlamaya taşır. Aynı şekilde /?s= gibi arama parametreli veya takvim benzeri sonsuz üreten URL'lerin listenin başında olması, önbelleklenemeyen bir uç noktanın taranmakta olduğunu gösterir.
Meşru Süreç Çıktıysa: MySQL'den Sorguya İnmek#
mysqld tepedeyse zincir daha da kısadır, çünkü veritabanı size ne yaptığını doğrudan söyler:
-- O anda çalışan sorgular, en uzun süredir çalışan üstte
SELECT id, user, host, db, command, time, state, LEFT(info, 120) AS sorgu
FROM information_schema.processlist
WHERE command <> 'Sleep'
ORDER BY time DESC
LIMIT 20;
time sütunu 30-40 saniyelerde gezinen ve state alanı Sending data ya da Copying to tmp table gösteren satırlar, indekssiz veya geçici tabloya düşen sorgulardır. Aynı sorgunun birden çok satırda tekrar etmesi ise uygulamanın onu her istekte yeniden çalıştırdığını gösterir.
Anlık görüntü yetmiyorsa yavaş sorgu günlüğünü açın:
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
Bir saat çalıştırıp mysqldumpslow -s t /var/log/mysql/slow.log | head -20 ile en pahalı sorgu kalıplarını sıralayın. Şüpheli sorguyu EXPLAIN ile inceleyip type: ALL (tam tablo taraması) görüyorsanız, eksik indeks büyük olasılıkla tek sebeptir. İndeksleme ve bellek ayarlarının bütünü için MySQL/MariaDB performans optimizasyonu yazısı ayrıntılı bir çerçeve sunuyor.
Bir uyarı: yavaş sorgu günlüğünü açık unutmayın. Yoğun bir sunucuda long_query_time = 1 ile birkaç gün içinde gigabaytlarca günlük birikebilir.
Süreçten Kullanıcıya, Kullanıcıdan Kaynağa#
Aynı sunucuda birden çok site barındırıyorsanız, "hangi süreç" sorusunun ardından "kimin sitesi" sorusu gelir. ps çıktısındaki USER sütunu bunun anahtarıdır; her siteyi ayrı sistem kullanıcısıyla çalıştırmanın en somut faydası da tam olarak budur.
# Kullanıcı bazında toplam CPU payı
ps -eo user,%cpu --no-headers | awk '{c[$1]+=$2} END {for (u in c) printf "%-16s %6.1f\n", u, c[u]}' | sort -k2 -rn
Süreç bir kullanıcıya bağlandıysa, sıradaki adım o kullanıcının zamanlanmış görevlerine bakmaktır. Yükün her saat başı tekrarlayan bir tepe yapması neredeyse her zaman bir cron işaretidir:
# Kullanıcının kendi görevleri
crontab -l -u sitekullanici
# Sistem genelindeki tanımlar
ls -l /etc/cron.d/ /etc/cron.hourly/ /etc/cron.daily/
Zamanlanmış görevlerin çakışması — aynı dakikada başlayan üç yedekleme işi gibi — sık görülen bir CPU tepesi sebebidir ve görevleri dakika bazında dağıtmak çoğu zaman tek satırlık bir düzeltmedir. Cron görevleri yazısında zamanlama sözdizimini ve çakışma önleme yöntemlerini bulabilirsiniz. Süreç bir systemd birimi tarafından başlatılıyorsa systemctl status 12345 gibi PID vererek hangi birime ait olduğunu doğrudan öğrenebilirsiniz.
Yükün ne zaman başladığını hatırlamıyorsanız günlükler size tarihi verir: journalctl --since "2 hours ago" -p warning çıktısı, yükün başladığı ana denk gelen bir servis yeniden başlatmasını veya hatasını çoğu zaman ortaya çıkarır. Günlükleri zaman aralığına göre daraltma yöntemleri için journalctl ile log yönetimi yazısına bakabilirsiniz.
Sunucuyu Hemen Nefes Aldırmak#
Teşhis sürerken sistemin cevap verebilir kalması gerekir. Süreci öldürmek yerine önceliğini düşürmek çoğu durumda daha iyi bir ilk hamledir:
# Çalışan bir sürecin önceliğini düşür (19 = en düşük öncelik)
renice -n 19 -p 12345
# Bir servisin ve alt süreçlerinin CPU payını sınırla
systemctl set-property php8.3-fpm.service CPUQuota=200%
CPUQuota=200% iki çekirdek demektir; systemd bunu anında uygular ve yeniden başlatmaya gerek kalmaz. Bu, kaçak bir servisin tüm makineyi kilitlemesini önleyen en pratik korumadır. Süreç ve dosya tanıtıcı sayısı gibi diğer sınırlar için ulimit ile kaynak limitleri yazısına bakabilirsiniz.
Süreci gerçekten sonlandırmanız gerekiyorsa önce nazik sinyali deneyin; kill -9 veritabanı süreçlerinde bozuk tablo bırakabilir. Sinyaller arasındaki farkı kill sinyalleri yazısında ele aldık.
Son olarak, teşhis "her şey normal, iş yükü gerçekten bu kadar" diyorsa ayar yapacak yer kalmamış demektir. O noktada soru kapasiteye döner ve doğru sorulacak soru çekirdek sayısı ile bellek dengesidir; VDS için kaç CPU ve RAM gerekir yazısı bu kararı somut iş yükü örnekleriyle ele alıyor.
Sıkça Sorulan Sorular#
CPU %100 ama site normal açılıyor, sorun var mı?#
Bir sunucunun CPU'sunu tam kullanması kendi başına arıza değildir; asıl sorun, kuyruğa giren isteklerin beklemeye başlamasıdır. Site normal açılıyorsa yük büyük ihtimalle düşük öncelikli bir arka plan işinden geliyordur. Yine de tampon kalmadığı için trafik biraz arttığında sistem aniden yanıt veremez hâle gelir. Sürekli %100 bir uyarıdır, acil durum değil.
top'ta %CPU 400 yazıyor, bu mümkün mü?#
Evet ve normaldir. top çok çekirdekli sistemlerde her çekirdeği ayrı %100 sayar, dolayısıyla dört çekirdeği tam kullanan bir süreç 400 gösterir. Toplam kapasiteye oranlı görmek isterseniz top çalışırken I tuşuna basarak Irix modunu kapatabilirsiniz; o zaman aynı süreç sekiz çekirdekli bir makinede 50 olarak görünür.
Süreci öldürdüm ama birkaç dakika sonra geri geldi, neden?#
Süreç kendi başına başlamıyor, bir yerden yeniden başlatılıyordur. Sırasıyla cron tanımlarına, systemd birimlerine ve kullanıcıların ~/.bashrc, ~/.profile dosyalarına bakın. Meşru bir serviste bu davranış zaten beklenendir, çünkü servis yöneticisi çöken süreci ayağa kaldırır. Meşru olmayan bir süreçte ise kalıcılık mekanizması bulunup temizlenmeden sorun bitmez.
Steal time nedir, ne zaman sorun sayılır?#
Steal time, hipervizörün CPU zamanını fiziksel makinedeki başka bir sanal sunucuya verdiği süredir. Kısa tepeler normaldir. Ancak dakikalarca %10'un üzerinde seyrediyorsa fiziksel sunucu aşırı paylaştırılmış demektir ve içeride yapacağınız hiçbir optimizasyon bunu düzeltmez. Ölçümü kaydedip sağlayıcınıza iletmek doğru adımdır.
CPU'yu yüksek gösteren süreç kworker ise ne yapmalıyım?#
kworker çekirdeğin kendi iş parçacığıdır ve genellikle asıl yükü üreten değil, onu taşıyan taraftır: yoğun disk yazımı, ağ trafiği veya bir sürücü sorunu bu şekilde görünür. Önce %Cpu(s) satırındaki sy ve si alanlarına bakın, ardından disk ve ağ tarafını inceleyin. Ayrıca köşeli parantezli isimlerin taklit edilebildiğini unutmayın; gerçek bir çekirdek iş parçacığında /proc/PID/exe okunabilir bir hedef göstermez.
Yükü üreten süreç görünmüyor ama CPU dolu, olur mu?#
Olur ve iki tipik sebebi vardır. Birincisi, yük çok kısa ömürlü süreçlerden geliyordur: saniyede onlarca kez doğup ölen bir betik top ekranında yakalanmaz ama pidstat 1 çıktısında görünür. İkincisi, süreç listesini gizleyen bir araç kurulmuştur. /proc altındaki süreç sayısı ile ps çıktısının satır sayısını karşılaştırmak bu şüpheyi hızlıca sınar.