PHP-FPM'de pm.max_children değerini tahminle değil ölçerek belirlemek. Süreç belleğini ölçme, hesap formülü, pm modları, status sayfası ve slowlog.
PHP-FPM’in ayar dosyasında bir satır var ki sitenin kaderini belirler ve çoğu sunucuda varsayılan değerinde bırakılmıştır: pm.max_children. Debian ve Ubuntu’da bu değer kutudan 5 olarak çıkar. Yani aynı anda en fazla 5 PHP isteği işlenir, altıncı ziyaretçi sıraya girer. Küçük bir blog için yeter. Kampanya günündeki bir e-ticaret sitesi için trafik sıkışmasıdır.
Tersi de sorun: değeri “ne kadar çok o kadar iyi” diyerek 200 yaparsan, trafik geldiğinde PHP süreçleri belleği bitirir ve Linux veritabanını öldürür. Doğru değer tahminle değil, ölçümle bulunur. Nasıl yapıldığını anlatayım.
Kısa cevap: pm.max_children ≈ PHP’ye ayırabileceğin bellek ÷ ortalama bir PHP-FPM sürecinin bellek kullanımı. Sonucu biraz aşağı yuvarla ve status sayfasıyla gözlemle.
Önce bir PHP isteğinin nasıl işlendiğini hatırlayalım
Nginx bir .php isteği aldığında onu PHP-FPM’e iletir. PHP-FPM’in elinde bir havuz dolusu işçi süreç (child) vardır; her işçi aynı anda tek bir istek işler. Bütün işçiler meşgulse yeni istek sırada bekler. pm.max_children, bu havuzun olabilecek en büyük boyutu.
Yani bu ayar aslında şu sorunun cevabı: aynı anda kaç PHP isteğini belleğim taşır?
1. Bir süreç ne kadar bellek kullanıyor?
Site normal trafikteyken şu komutla ortalamayı ölç (sürüm numarasını kendi PHP sürümünle değiştir):
ps --no-headers -o rss -C php-fpm8.3 | awk '{s+=$1; n++} END {printf "%.0f MB\n", s/n/1024}'
Bu değer sitenin ne yaptığına göre çok değişir. Sade bir PHP sitesinde 30-40 MB görürsün, eklentisi bol bir WordPress, WHMCS ya da büyük bir Laravel uygulamasında 80-150 MB’ı bulabilir. İnternetteki hazır tablolara değil, kendi ölçümüne güven.
Küçük bir not: rss değeri süreçlerin ortak kullandığı belleği (örneğin OPcache) her süreçte ayrı sayar. Yani ölçüm gerçek kullanımdan biraz fazla çıkar. Bu, hesabı güvenli tarafta tutan iyi bir hata.
2. PHP’ye ne kadar bellek kalıyor?
Sunucudaki toplam bellekten diğer her şeyi çıkar:
- İşletim sistemi ve Nginx: 300-500 MB
- MySQL veya MariaDB:
innodb_buffer_pool_sizedeğeri artı biraz pay - Redis, Elasticsearch, cron işleri gibi diğer servisler
3. Hesap
Örnek bir 4 GB sunucu düşünelim:
| Kalem | Bellek |
|---|---|
| Toplam | 4096 MB |
| İşletim sistemi ve Nginx | −500 MB |
| MySQL | −1200 MB |
| PHP’ye kalan | 2396 MB |
| Ortalama PHP süreci | 60 MB |
| pm.max_children | 2396 ÷ 60 ≈ 39 |
Buradan 35 gibi yuvarlak ve biraz aşağıda bir değer seçerim. Payı, arada bir çok bellek isteyen rapor sayfası ya da büyük bir dosya yüklemesi yer.
4. Hangi pm modu?
Ayar dosyası Debian ve Ubuntu’da /etc/php/8.3/fpm/pool.d/www.conf yolunda. Üç mod var:
dynamic: varsayılan. Belli sayıda işçi hazırda bekler, trafik artıncapm.max_children’a kadar çoğalır. Çoğu site için doğru seçim.static: her zamanpm.max_childrenkadar işçi çalışır. Yalnızca bu siteye ayrılmış, trafiği yüksek sunucularda en hızlı yanıtı verir ama belleği sürekli tutar.ondemand: istek gelmedikçe işçi açılmaz. Aynı sunucuda çok sayıda az ziyaret edilen site varsa belleği korur, ilk istekte biraz gecikme ekler.
Örnek dynamic ayarı:
pm = dynamic
pm.max_children = 35
pm.start_servers = 8
pm.min_spare_servers = 4
pm.max_spare_servers = 12
pm.max_requests = 500
pm.max_requests ayrıca önemli: her işçi 500 istekten sonra kapanıp yenisiyle değişir. Böylece bir eklentideki küçük bir bellek sızıntısı günler içinde büyüyüp sunucuyu yiyemez.
Değişiklikten sonra ayarı test et ve servisi yeniden yükle:
sudo php-fpm8.3 -t
sudo systemctl reload php8.3-fpm
5. Gözlemle: status sayfası
Hesap bir başlangıç noktası, gerçek cevap trafikte ortaya çıkar. PHP-FPM’in kendi durum sayfasını aç:
pm.status_path = /fpm-status
Nginx’te bu adresi yalnızca sunucunun kendisine aç, dışarıya değil:
location = /fpm-status {
allow 127.0.0.1;
deny all;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
curl http://127.0.0.1/fpm-status çıktısında üç satıra bak:
max children reached: sıfırdan büyükse havuz en az bir kez doldu ve istekler bekledi.listen queue: şu anda sırada bekleyen istek sayısı. Sürekli sıfırın üstündeyse işçi yetmiyor.active processes: o anda çalışan işçi sayısı. Hiçbir zaman sınıra yaklaşmıyorsa değeri gereğinden yüksek tutuyorsun.
Havuz doluyor ama bellek de yetmiyorsa, pm.max_children’ı artırmak çözüm değil. O durumda ya süreçleri hafifletmen (OPcache açık mı, gereksiz eklenti var mı?) ya da sunucuyu büyütmen gerekir.
6. Yavaş istekleri yakala: slowlog
Bazen sorun işçi sayısı değil, işçileri uzun süre meşgul eden birkaç yavaş sayfadır. PHP-FPM, belli bir süreyi aşan isteklerin o anki çağrı yığınını kaydedebilir:
request_slowlog_timeout = 5s
slowlog = /var/log/php8.3-fpm-slow.log
Log dosyası, isteğin hangi fonksiyonda takılı kaldığını satır numarasıyla gösterir. Çoğu zaman karşına ya indekssiz bir sorgu ya da cevap vermeyen bir dış servis çağrısı çıkar. Dış servis çağrılarına mutlaka zaman aşımı ekle. Karşı taraf yanıt vermediğinde senin işçin 60 saniye boyunca rehin kalmasın.
Özet
- Ortalama süreç belleğini
psile ölç. - Diğer servislerin payını çıkar, kalan belleği süreç boyutuna böl.
- Sonucu biraz aşağı yuvarla,
pm.max_requestsekle. - Status sayfasında
max children reachedvelisten queuedeğerlerini izle. - Yavaş istekleri slowlog ile yakala.
Sunucu yavaşladığında sorunun PHP-FPM’de olduğunu nasıl anlayacağını bakılacak 10 komut yazısında anlattım. PHP tarafını güvenli tutmak için de PHP güvenlik önlemleri yazısına göz atabilirsin.