Apache ServerLimit ve MaxRequestWorkers Değerlerinin Optimizasyonu

Apache web sunucunuzu kurdunuz, siteniz yayında ve her şey güzel gidiyor. Sonra bir gün trafik artıyor, sunucu kasılmaya başlıyor ve loglarınızda şu korkutucu mesajı görüyorsunuz: server reached MaxRequestWorkers setting, consider raising the MaxRequestWorkers setting. İşte tam bu noktada Apache’nin bellek ve process yönetimini anlamanız gerekiyor. Bu yazıda ServerLimit ve MaxRequestWorkers direktiflerini derinlemesine inceleyeceğiz, yanlış yapılandırmanın nasıl felaket yarattığını göreceğiz ve gerçek dünya senaryolarıyla optimum değerleri nasıl hesaplayacağınızı öğreneceğiz.

Apache MPM Nedir ve Neden Önemli?

ServerLimit ve MaxRequestWorkers değerlerini ayarlamadan önce Apache’nin hangi modda çalıştığını anlamanız şart. Apache, farklı Multi-Processing Module (MPM) modlarını destekler ve her modun davranışı birbirinden farklıdır.

prefork MPM: Her istek için ayrı bir process oluşturur. PHP’nin mod_php ile kullanıldığı klasik yapılarda tercih edilir. Thread-safe olmayan kütüphanelerle güvenli çalışır ama bellek tüketimi yüksektir.

worker MPM: Her process içinde birden fazla thread çalıştırır. Daha az bellek kullanır, daha fazla eş zamanlı bağlantı yönetebilir.

event MPM: Worker MPM’e benzer ama keep-alive bağlantılarını çok daha verimli yönetir. Modern Apache kurulumlarında varsayılan ve önerilen moddur.

Hangi MPM’i kullandığınızı öğrenmek için:

apache2ctl -V | grep MPM
# veya
httpd -V | grep MPM
# Çıktı: Server MPM: event

Mevcut yüklü MPM modüllerini görmek için:

apache2ctl -M | grep mpm
# Çıktı: mpm_event_module (shared)

ServerLimit ve MaxRequestWorkers Farkı

Bu iki direktif birbiriyle karıştırılıyor ve yanlış anlaşılıyor. Net bir şekilde tanımlayalım:

MaxRequestWorkers: Aynı anda kaç tane isteğin karşılanacağını belirler. Eski Apache sürümlerinde MaxClients olarak geçiyordu. Bu değere ulaşıldığında yeni gelen bağlantılar kuyrukta bekler (kernel’in ListenBacklog ayarına kadar) ya da reddedilir.

ServerLimit: MaxRequestWorkers değerinin üst sınırını belirler. Prefork MPM’de bu değer aynı zamanda maksimum process sayısını ifade eder. Eğer MaxRequestWorkers değerini 256’nın üzerine çıkarmak istiyorsanız ServerLimit’i de artırmanız zorunludur.

Arasındaki ilişkiyi şöyle özetleyebiliriz:

  • ServerLimit: Fiziksel üst sınır, Apache’nin hiçbir zaman aşamayacağı değer
  • MaxRequestWorkers: Aktif olarak kullanılacak maksimum worker sayısı
  • MaxRequestWorkers her zaman ServerLimit’e eşit ya da küçük olmak zorundadır
  • ServerLimit değişikliği Apache’nin tamamen yeniden başlatılmasını gerektirir, sadece reload yetmez

Mevcut Durumunuzu Analiz Edin

Optimizasyona başlamadan önce mevcut durumu anlamak gerekiyor. Apache’nin o anki durumunu görmek için mod_status modülünü aktif edin:

# Debian/Ubuntu
sudo a2enmod status

# mod_status yapılandırması
sudo nano /etc/apache2/conf-available/server-status.conf
<Location "/server-status">
    SetHandler server-status
    Require local
    # Ya da belirli IP'den erişim için:
    # Require ip 192.168.1.0/24
</Location>

ExtendedStatus On

Komut satırından anlık durumu görüntülemek için:

# Curl ile server-status çıktısını al
curl -s http://localhost/server-status?auto | grep -E "BusyWorkers|IdleWorkers|Scoreboard"

# Daha detaylı görünüm
watch -n 2 'curl -s http://localhost/server-status?auto'

Çıktıda göreceğiniz Scoreboard satırındaki karakterlerin anlamları:

  • _: Boşta bekleyen worker (idle)
  • S: Başlatılıyor (starting up)
  • R: İstek okuma
  • W: İsteğe yanıt yazıyor
  • K: Keep-alive bağlantısı bekliyor
  • D: DNS sorgusu
  • C: Bağlantı kapanıyor
  • .: Açık olmayan slot

Eğer Scoreboard’da sürekli W ve R harfleri dolup taşıyorsa ve _ karakterleri azalıyorsa, worker sayısını artırmayı düşünme zamanı gelmiş demektir.

Bellek Hesaplama: En Kritik Adım

Burada sysadminlerin en büyük hatası, mantıklı bir hesap yapmadan keyfi değerler girmek. MaxRequestWorkers’ı 1000 yapayım, ne olur ki? diyenler sunucuyu OOM (Out of Memory) killer’ın eline teslim eder.

Doğru hesaplama şu şekilde yapılır:

# Mevcut Apache process'lerinin bellek kullanımını öğren
ps aux | grep apache2 | grep -v grep | awk '{print $6}' | sort -n

# Ortalama bellek kullanımı (KB cinsinden)
ps aux | grep apache2 | grep -v grep | awk '{sum += $6; count++} END {print "Ortalama:", sum/count, "KB =", sum/count/1024, "MB"}'

# En büyük process'i bul
ps aux | grep apache2 | grep -v grep | sort -k6 -rn | head -5

Gerçek dünya örneği verelim. Diyelim ki yukarıdaki komut şu çıktıyı verdi:

Ortalama: 45231 KB = 44.17 MB

Şimdi hesaplama:

# Sistemde kullanılabilir toplam RAM'i öğren
free -m

# Çıktı örneği:
#               total        used        free      shared  buff/cache   available
# Mem:           7962        1823         412         234        5726        5652

# Hesaplama mantığı:
# Kullanılabilir RAM: ~5500 MB (available)
# Sistem ve diğer servisler için ayır: 1000 MB
# Apache için kalan: 4500 MB
# Her process ~45 MB alıyorsa: 4500 / 45 = 100 worker

Bu hesabı script haline getirin:

#!/bin/bash
# apache_memory_calc.sh

AVAILABLE_MEM=$(free -m | awk '/^Mem:/{print $7}')
SYSTEM_RESERVE=1024  # MB olarak sistem rezervi
APACHE_MEM=$((AVAILABLE_MEM - SYSTEM_RESERVE))

# Apache process başına ortalama bellek (MB)
AVG_PROCESS_MEM=$(ps aux | grep apache2 | grep -v grep | 
    awk '{sum += $6; count++} END {printf "%.0f", sum/count/1024}')

if [ -z "$AVG_PROCESS_MEM" ] || [ "$AVG_PROCESS_MEM" -eq 0 ]; then
    echo "Apache process bulunamadi veya henuz calismıyor."
    AVG_PROCESS_MEM=50  # Varsayılan tahmin
fi

MAX_WORKERS=$((APACHE_MEM / AVG_PROCESS_MEM))

echo "Kullanilabilir RAM: ${AVAILABLE_MEM} MB"
echo "Sistem rezervi: ${SYSTEM_RESERVE} MB"
echo "Apache için RAM: ${APACHE_MEM} MB"
echo "Process başına ortalama RAM: ${AVG_PROCESS_MEM} MB"
echo "Önerilen MaxRequestWorkers: ${MAX_WORKERS}"
echo "Önerilen ServerLimit: ${MAX_WORKERS}"

Prefork MPM Yapılandırması

PHP uygulamaları mod_php ile çalışıyorsa büyük ihtimalle prefork MPM kullanıyorsunuzdur. Yapılandırma dosyasını açalım:

# Ubuntu/Debian
sudo nano /etc/apache2/mods-available/mpm_prefork.conf

# CentOS/RHEL
sudo nano /etc/httpd/conf.modules.d/00-mpm.conf
<IfModule mpm_prefork_module>
    # Başlangıçta oluşturulacak child process sayısı
    StartServers             5

    # Her zaman hazırda bekleyen minimum idle process
    MinSpareServers          5

    # Maksimum idle process sayısı (bunun üzeri öldürülür)
    MaxSpareServers         10

    # Mutlak maksimum - bellek hesabına göre belirle
    MaxRequestWorkers      150

    # MaxRequestWorkers 256'yı geçiyorsa bu değeri de artır
    ServerLimit            150

    # Her child process kaç istek karşıladıktan sonra yeniden başlar
    # Memory leak olan PHP uygulamaları için önemli
    MaxConnectionsPerChild  10000
</IfModule>

Burada MaxConnectionsPerChild direktifine dikkat edin. PHP uygulamalarında memory leak riski varsa bu değeri düşük tutmak (örneğin 1000-5000) process’lerin periyodik olarak yeniden başlamasını sağlar ve bellek sızıntısının birikmesini önler. Elbette bu trade-off içerir, process başlatma maliyeti artar.

Worker ve Event MPM Yapılandırması

Modern PHP-FPM + Nginx/Apache kurulumlarında ya da statik/PHP dışı içerik sunan sunucularda worker veya event MPM çok daha verimlidir:

sudo nano /etc/apache2/mods-available/mpm_event.conf
<IfModule mpm_event_module>
    # Başlangıç process sayısı
    StartServers             3

    # Minimum idle thread sayısı
    MinSpareThreads         25

    # Maksimum idle thread sayısı
    MaxSpareThreads         75

    # Her process içindeki thread sayısı
    ThreadsPerChild         25

    # Maksimum eş zamanlı istek (MaxRequestWorkers)
    MaxRequestWorkers      200

    # Maksimum process sayısı
    # MaxRequestWorkers / ThreadsPerChild = gereken process sayısı
    # 200 / 25 = 8, biraz üstünde tutalım
    ServerLimit             16

    # Keep-alive bağlantıları için ayrılan thread sayısı
    AsyncRequestWorkerFactor 2

    MaxConnectionsPerChild   0
</IfModule>

Event MPM’de ServerLimit hesabı farklıdır. Process başına thread çalıştığı için:

  • MaxRequestWorkers: 200
  • ThreadsPerChild: 25
  • Gereken minimum ServerLimit: 200 / 25 = 8
  • Güvenli ServerLimit: 16 (biraz marj bırakıyoruz)

Gerçek Dünya Senaryo 1: Orta Ölçekli E-Ticaret Sitesi

4 GB RAM’li bir VPS üzerinde WooCommerce çalıştıran bir senaryo düşünelim. Prefork MPM kullanılıyor, PHP mod_php ile entegre.

Sorun: Yoğun saatlerde sayfa yüklemeleri 30-40 saniyeye çıkıyor, hatta zaman zaman 503 hatası alınıyor.

Analiz aşaması:

# Error log'a bak
sudo tail -f /var/log/apache2/error.log | grep -i "maxrequestworkers|maxclients|reached"

# Process başına bellek kullanımını ölç
# WooCommerce + WordPress ortalaması genelde 80-120 MB arasında
ps aux | grep apache2 | grep -v grep | awk '{sum+=$6; n++} END {print sum/n/1024 " MB"}'
# Çıktı: 95.3 MB

Hesaplama:

  • Toplam RAM: 4096 MB
  • İşletim sistemi: ~400 MB
  • MySQL: ~600 MB
  • Diğer servisler: ~200 MB
  • Apache için kalan: ~2900 MB
  • Process başına: ~95 MB
  • MaxRequestWorkers: 2900 / 95 = 30 (yaklaşık)

Yapılandırma:

<IfModule mpm_prefork_module>
    StartServers             3
    MinSpareServers          3
    MaxSpareServers          8
    MaxRequestWorkers       30
    ServerLimit             30
    MaxConnectionsPerChild  5000
</IfModule>

Bu örnek küçük gözükebilir ama 4 GB RAM’li bir sunucuda WordPress+WooCommerce için 30 eş zamanlı istek gayet makul bir değerdir. Gerçek şu ki çoğu orta ölçekli e-ticaret sitesi aynı anda 30’dan fazla PHP isteği görmez. Üzerine bir CDN ve sayfa önbellekleme (Varnish veya LiteSpeed Cache) eklendiğinde bu değer çok yeterli olur.

Gerçek Dünya Senaryo 2: Yüksek Trafikli API Sunucusu

8 GB RAM, PHP-FPM kullanan bir REST API sunucusu. Event MPM ile Apache ters proxy olarak çalışıyor, PHP-FPM arka planda işlemleri karşılıyor.

Bu senaryoda Apache process’leri hafiftir çünkü PHP işlemlerini kendi üstlenmez:

# Apache worker'larının bellek kullanımı (PHP-FPM kurulumunda çok düşük olur)
ps aux | grep apache2 | grep -v grep | awk '{sum+=$6; n++} END {print sum/n/1024 " MB"}'
# Çıktı: 8.7 MB (evet, bu kadar düşük olabilir)
<IfModule mpm_event_module>
    StartServers              4
    MinSpareThreads          50
    MaxSpareThreads         150
    ThreadsPerChild          50
    MaxRequestWorkers       500
    ServerLimit              20
    AsyncRequestWorkerFactor  4
    MaxConnectionsPerChild    0
</IfModule>

Bu yapılandırmada Apache tek başına 500 eş zamanlı isteği yönetebilir ve sadece ~70-80 MB bellek kullanır. PHP-FPM tarafındaki pool ayarlarını da buna göre yapılandırmanız gerektiğini unutmayın.

Yapılandırmayı Test Edin

Değişiklik yapmadan önce syntax kontrolü şart:

# Syntax kontrolü
sudo apache2ctl configtest
# ya da
sudo apachectl -t

# Sorun yoksa yeniden başlat (ServerLimit değişikliği reload ile uygulanmaz!)
sudo systemctl restart apache2

# Yalnızca MaxRequestWorkers değiştirdiyseniz reload yeterli
sudo systemctl reload apache2

Yük testi için Apache Bench kullanarak yapılandırmanızı doğrulayın:

# 1000 istek, 50 eş zamanlı bağlantı
ab -n 1000 -c 50 http://localhost/

# Daha gerçekçi test: keep-alive ile
ab -n 5000 -c 100 -k http://localhost/

# Sonuçlarda dikkat edilecek değerler:
# - Requests per second: ne kadar istek/saniye karşılıyorsunuz
# - Failed requests: başarısız istek var mı
# - Time per request: ortalama yanıt süresi

Yük testi sırasında başka bir terminal açıp sunucuyu izleyin:

# Anlık worker durumu
watch -n 1 'curl -s http://localhost/server-status?auto | grep -E "BusyWorkers|IdleWorkers"'

# Bellek kullanımı
watch -n 1 'free -m'

# Apache process sayısı
watch -n 1 'ps aux | grep apache2 | grep -v grep | wc -l'

Sık Yapılan Hatalar ve Çözümleri

Hata 1: ServerLimit’i değiştirip sadece reload yapmak. ServerLimit değişikliği Apache’nin belleğe yükleme biçimini etkiler ve sadece tam yeniden başlatma ile aktif olur. Reload yaparsanız değişiklik görmezden gelinir ve log’da uyarı bile göremezsiniz.

Hata 2: MaxRequestWorkers’ı çok yüksek tutmak. Bellek dolunca Linux kernel’in OOM killer’ı devreye girer ve rastgele process’leri öldürür. Çoğunlukla Apache process’lerini hedef alır ama bazen MySQL’i de vurabilir. Bunun yerine düşük bir MaxRequestWorkers ile sağlıklı bir sistem, yüksek değerle çöken bir sistem arasında tercih yapmanız gerekirse düşük değeri seçin.

Hata 3: ServerLimit’i MaxRequestWorkers’dan düşük tutmak.

# Bu yanlış yapılandırma Apache'nin startup log'unda uyarı verir:
# WARNING: MaxRequestWorkers of 300 exceeds ServerLimit value
# of 256 servers, lowering MaxRequestWorkers to 256.
# To increase, please see the ServerLimit directive.

# Doğrusu: ServerLimit >= MaxRequestWorkers olmalı
ServerLimit          300
MaxRequestWorkers    300

Hata 4: Swap alanını hesaba katmak. Swap kullanımına güvenerek MaxRequestWorkers değerini RAM kapasitesinin ötesine taşıyanlar büyük hayal kırıklığı yaşar. Disk tabanlı swap üzerinde Apache worker çalıştırmak, yanıt sürelerini saniyelerle ölçülür hale getirir. Swap’ı acil durum tamponu olarak görün, normal operasyon belleği olarak değil.

Monitoring ve Log Analizi

Yapılandırmanızın ne kadar iyi çalıştığını sürekli izlemeniz gerekiyor:

#!/bin/bash
# apache_monitor.sh - Cron ile her 5 dakikada çalıştırın

LOG_FILE="/var/log/apache_monitor.log"
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

BUSY=$(curl -s http://localhost/server-status?auto | grep BusyWorkers | awk '{print $2}')
IDLE=$(curl -s http://localhost/server-status?auto | grep IdleWorkers | awk '{print $2}')
TOTAL=$((BUSY + IDLE))
MAX=$(grep MaxRequestWorkers /etc/apache2/mods-enabled/mpm_prefork.conf 2>/dev/null | 
      awk '{print $2}' | tr -d ' ')

USAGE_PCT=$((BUSY * 100 / MAX))

echo "${TIMESTAMP} | Busy: ${BUSY} | Idle: ${IDLE} | Total: ${TOTAL} | Kullanım: %${USAGE_PCT}" 
     >> "$LOG_FILE"

# %80 üzerine çıkınca uyar
if [ "$USAGE_PCT" -gt 80 ]; then
    echo "${TIMESTAMP} UYARI: Worker kullanımı %${USAGE_PCT}'e ulasti!" 
         | mail -s "Apache Worker Uyarisi" [email protected]
fi

Error log’unda düzenli olarak arama yapın:

# Son 24 saatte MaxRequestWorkers uyarısı var mı
grep -c "MaxRequestWorkers" /var/log/apache2/error.log

# Hangi saatlerde yoğunluk var
grep "MaxRequestWorkers" /var/log/apache2/error.log | 
    awk '{print $1, $2}' | cut -d: -f1-2 | sort | uniq -c | sort -rn | head -20

Sonuç

ServerLimit ve MaxRequestWorkers tuning’i, Apache optimizasyonunun belki de en kritik adımıdır. Yanlış yapılandırılmış bir sunucu ya kaynak israfeder ya da trafik yükü altında çöker. Doğru yaklaşım her zaman ölçümle başlar: önce process başına gerçek bellek kullanımını öğrenin, sonra kullanılabilir RAM’i hesaba katarak mantıklı bir üst sınır belirleyin.

MPM seçiminiz de kritik öneme sahip. PHP-FPM kullanıyorsanız event MPM ile çok daha hafif ve verimli bir yapı kurabilirsiniz. Mod_php ile devam etmek zorundaysanız prefork MPM ile çalışın ama MaxConnectionsPerChild ile bellek sızıntılarının önüne geçmeyi unutmayın.

Son olarak şunu vurgulayayım: bu değerler bir kez ayarlanıp unutulan parametreler değil. Uygulamanız büyüdükçe, trafik arttıkça, sunucunuza RAM ekledikçe bu değerleri gözden geçirin. Monitoring kurulmadan yapılan her optimizasyon körler tarafından yapılan bir çalışmadır. mod_status aktif olsun, loglarınızı düzenli inceleyin ve Apache Bench gibi araçlarla periyodik yük testleri yapın.

Yorum yapın