Site açılıyor, birkaç dakika çalışıyor, sonra aniden isteklerin yarısı 502 Bad Gateway ile geri dönüyor ve nginx logunda connect() to unix:/run/php/php8.2-fpm.sock failed (11: Resource temporarily unavailable) satırı tekrarlanıyor. Bunu gördüyseniz, neredeyse emin olabilirsiniz ki sorun kodda değil. Sorun, PHP-FPM havuzunun tüm worker'larını tüketmiş olması ve istek kuyruğunun dolmasıdır.
PHP-FPM bir süreç yöneticisidir: bir dizi worker önceden oluşturulur veya anlık olarak oluşturulur, her worker aynı anda yalnızca bir PHP isteğini çalıştırır ve diğer istekler kuyrukta bekler. Kuyruk dolduğunda, nginx'in artık bağlanacak yeri kalmaz ve aynı 502'yi verir. Doğru yapılandırmanın tamamı tek bir soruya cevap vermektir: Sunucu belleği tükenmeden aynı anda kaç worker'a sahip olabilirim?
pm.max_children'ı hisse göre değil, belleğe göre hesaplayın
pm.max_children sayısı, eşzamanlı worker'ların üst sınırıdır. Bunu sunucunun gerçek kapasitesinden daha büyük ayarlarsanız, 502 yerine daha kötü bir hata alırsınız: Linux OOM Killer süreçlerden birini öldürür ve MySQL'i kaybedebilirsiniz.
Önce bir worker'ın gerçek boyutunu ölçün. Sadece sleep(30) yapan basit bir betik oluşturun, ona birkaç eşzamanlı istek gönderin ve sonra:
ps -o rss= -C php-fpm8.2 | awk '{s+=$1; n++} END {print s/n/1024 " MB avg, " n " workers"}'
Birkaç ağır eklentiye sahip bir WordPress sitesi için tipik çıktı, her worker için 60 ila 120 megabayt arasındadır. Şimdi hesap basit. 4 gigabayt RAM'e sahip ve bunun 1.5 gigabaytı MySQL, önbellek ve işletim sistemine giden bir sunucuda, PHP için yaklaşık 2.5 gigabayt kalır. 80 megabaytlık bir worker ile mantıklı üst sınır 100 değil, yaklaşık 30 worker'dır.
Çoğu kişinin gözden kaçırdığı bir nokta: Bu sayıyı boş bir sunucuda hesaplamayın. Worker'lar opcache önbelleği büyüdükçe ve eklentiler yüklendikçe büyür. Bir haftalık çalışma süresinden sonra her worker'ın tüketimi yüzde 30 arttıysa, üst sınırınız gerçek değildir.
dynamic mi ondemand mı: Hangisini seçmeliyim?
dynamic modu bir dizi hazır worker tutar ve pm.start_servers ile pm.max_spare_servers arasında dalgalanır. ondemand modu hiç hazır worker tutmaz ve yalnızca istek geldiğinde süreç oluşturur.
| Kriter | dynamic | ondemand |
|---|---|---|
| İlk istek gecikmesi | Daha az, worker hazır | Daha fazla, süreç oluşturma 20 ila 60 milisaniye |
| Boşta bellek | start_servers kadar tüketilir | Neredeyse sıfır |
| Pik trafik | Uygun | pm.max_children yeterliyse uygun |
| Dengeli ve yüksek trafik | Uygun | Sürekli süreç oluşturma, CPU yükü |
Yüksek trafikli siteler için tercihim dynamic. Nedeni basit: Her saniye birkaç isteğin geldiği bir sitede, süreç oluşturma maliyeti birkaç hazır worker tutma maliyetinden daha yüksektir. ondemand'ı, sunucu birkaç düşük trafikli siteyi barındırdığında ve belleği veritabanı için serbest bırakmak istediğimde seçiyorum. Paylaşımlı bir Linux hosting veya küçük bir VPS üzerinde çalışıyorsanız, bu karar RAM tüketiminde belirgin bir fark yaratır.
4 gigabaytlık bir sunucuda yüksek trafikli bir site için dengeli bir yapılandırma:
pm = dynamic
pm.max_children = 30
pm.start_servers = 8
pm.min_spare_servers = 6
pm.max_spare_servers = 12
pm.max_requests = 500
request_terminate_timeout = 60s
pm.max_requests = 500, worker'ı 500 istekten sonra emekliye ayırır. Bu, huysuz eklentilerin bellek sızıntılarını dizginler ve maliyeti biraz süreç yeniden oluşturma yüküdür. Eklentileriniz temizse, bunu 2000'e veya 0'a çekebilirsiniz.
Worker eksikliğinin belirtilerini nereden anlarız
502 yalnızca belirtilerden biridir. Ağır yük altında sitenizin TTFB'si 200 milisaniyeden 3 saniyeye çıkıyorsa ama sunucu CPU'su yüzde 40'ın üzerinde değilse, muhtemelen istekler PHP-FPM kuyruğunda bekliyor, işlemci meşgul değil. Havuzun gerçek durumunu görmek için:
curl -s http://127.0.0.1/status?full | grep -E "active|idle|listen queue|max children"
listen queue sıfırdan farklı bir sayı gösteriyorsa, istekler kuyruğun arkasında kalmış demektir. max children reached sıfırdan büyükse, worker üst sınırına dokunmuşsunuzdur. Bu iki sayıyı sabahın 3'ünde değil, pik aralıklarında ölçmelisiniz.
Burada hata yapıyorlar: Çoğu kişi kuyruğu görür ve hemen pm.max_children'ı ikiye katlar. Sonuç, birkaç saat sonra sunucunun OOM ile karşılaşması, MySQL'in yeniden başlaması ve sitenin tamamen çökmesidir. Dolu kuyruk, worker eksikliğinin belirtisi değildir; her worker'ın aşırı uzun sürdüğünün belirtisidir. Önce bir isteğin neden 2 saniye sürdüğünü anlamalısınız. Yavaş bir sorgu veya harici bir API'ye HTTP isteği ise, worker eklemek yalnızca bekleyen süreç sayısını artırır.
Yavaş istekleri bulmak için slowlog'u etkinleştirin:
request_slowlog_timeout = 5s
slowlog = /var/log/php8.2-fpm-slow.log
Birkaç saat sonra dosyayı açın ve çağrı yığınında hangi fonksiyonun takıldığını görün. Çoğu durumda harici bir servise yapılan bir file_get_contents veya indekssiz bir sorgu bulunur. Dağınık kesintileri kesin olarak kökünden çözmek için aralıklı kesintileri kökünden çözme yöntemine de bakın; log deseni genellikle FPM'de gizli kalan şeyi gösterir.
Kararlılığı etkileyen yan ayarlar
opcache'i ciddiye alın
opcache olmadan, her worker her istekte PHP dosyalarını diskten okur ve derler. opcache.memory_consumption = 256 ve opcache.max_accelerated_files = 20000 ile her isteğin yürütme süresi belirgin şekilde azalır ve sonuç olarak worker'lar daha hızlı serbest kalır. Bu, aynı 30 worker üst sınırının daha fazla yükü karşıladığı anlamına gelir.
Socket mi TCP mi?
Unix socket aynı sunucuda biraz daha hızlıdır ve dosya sistemini meşgul etmez. nginx ve PHP aynı makinedeyse, socket'i seçin. PHP başka bir sunucudaysa, TCP'den başka çare yoktur ve listen = 0.0.0.0:9000'i güvenlik duvarıyla sınırlamalısınız; aksi halde 9000 portu internete açık kalır ve bu yaygın bir güvenlik hatasıdır.
timeout'ları uyumlu hale getirin
FPM'de request_terminate_timeout 60 saniye ama nginx'te fastcgi_read_timeout 30 ise, kullanıcı net bir hata yerine 504 alır. Bu üç değeri hizalayın: PHP'nin kendi timeout'u, FPM değeri ve nginx değeri. 504 aldığınızda ve hangi katmanın suçlu olduğundan emin olmadığınızda, 504 hatası giderme rehberi kontrol sırasını gösterir.
Değişiklikten önce bir temel değer alın
Bu dosyadaki her değişiklik, ölçüm olmadan bir tahmindir. php-fpm.conf'a dokunmadan önce, RAM tüketimini, aktif worker sayısını ve TTFB'yi üç aralıkta (sabah, pik, akşam) kaydedin. Sonra bir parametreyi değiştirin ve 24 saat bekleyin. Aynı anda beş parametreyi değiştirmek, hangisinin etkili olduğunu asla anlayamayacağınız anlamına gelir.
Mevcut sunucunuz bu ayarlardan sonra bile pikte yetersiz kalıyorsa, sorun artık yapılandırma değil; kapasitedir. O aşamada özel sunucu veya daha fazla RAM'e sahip bir plan, körü körüne pm.max_children'ı yükseltmekten daha mantıklı bir seçimdir. Sitenin yanıt verme hızını hızlıca izlemek için de ücretsiz web yöneticisi araçlarını kullanarak değişikliklerin gerçekte ne etki yarattığını görebilirsiniz.
Sık sorulan sorular
pm.max_children'ı hangi sayıya ayarlamalıyım?
Sabit bir sayı yoktur. Bir worker'ın gerçek tüketimini ps -o rss= ile ölçün, veritabanına ve işletim sistemine giden belleği çıkarın ve kalanı her worker'ın tüketimine bölün. Emin olmak için yüzde 10 ila 20 güvenlik payı bırakın.
Sitem boş RAM olmasına rağmen neden 502 veriyor?
Muhtemelen RAM tükenmemiş, PHP-FPM istek kuyruğu dolmuştur. Status page'i etkinleştirerek listen queue değerini kontrol edin. Sıfırdan farklıysa, sorun worker eksikliği değil, her isteğin yavaşlığıdır.
dynamic ve ondemand arasındaki fark pratikte ne kadar önemli?
Dengeli trafiğe sahip yüksek trafikli bir sitede dynamic daha az gecikme verir çünkü her zaman hazır bir worker vardır. Birkaç düşük trafikli siteyi barındıran bir sunucuda ondemand daha fazla bellek serbest bırakır ve buna değer. Pik trafikteki fark TTFB'de birkaç yüz milisaniye olabilir.
Ayarları değiştirdikten sonra ne yapmalıyım?
systemctl reload php8.2-fpm ile ayarları aktif bağlantıları kesmeden yükleyin. Ardından 24 saat boyunca slow logunu ve max children reached değerini izleyin. Sayı sıfır kalırsa ve TTFB düşerse, yapılandırma doğru olmuştur.
Yorumlar 0
Henüz yorum yok — ilk siz olun!