İçeriğe geç
Muhammet Şafak
en
Soran: Melis Cevaplandı:

Container'larım sürekli OOMKilled oluyor, memory limit'lerini PHP-FPM ve Go için nasıl doğru ayarlarım?


Soru

Kubernetes'te hem PHP-FPM (Laravel) hem de Go servislerimiz var. Lokalde limit koymadan çalıştırınca her şey yolunda görünüyor ama production'da yük altında pod'lar sürekli OOMKilled olup yeniden başlıyor. Memory request/limit değerlerini PHP-FPM ve Go container'ları için nasıl doğru ayarlarım? Sürekli limiti yükseltmek dışında hangi uygulama seviyesi ayarlara bakmalıyım?

Cevap

Kısa cevap: OOMKilled bir uygulama hatası değil, container’ın cgroup memory limitini aştığının sinyalidir.

Kısa cevap

Lokalde limit yok, kernel büyümeye izin veriyor; production’da limit + yük altında tavana çarpıp öldürülüyor. Çözüm limiti körlemesine yükseltmek değil, gerçek peak’i ölçüp uygulama knob’larını ona göre ayarlamak.

Neden

  1. Önce ölçün: working set ne kadar? container_memory_working_set_bytes metriğine bakın. Limiti gözlenen peak’in üzerine, makul bir pay bırakarak koyun. “Lokalde sorun yok” yanıltıcıdır çünkü orada limit yoktur; gerçek soru yük altındaki tepe kullanımdır.

  2. PHP-FPM’de katil pm.max_children’dır. Toplam bellek ≈ max_children × istek başına ortalama bellek. Tutorial’dan kopyalanan devasa bir max_children yük altında tüm worker’ları açar ve limiti patlatır. Formül: max_children = (container limit − pay) / worker peak. Worker peak’i tek bir process’in gerçek RSS’inden ölçün.

  3. PHP memory_limit container’ı korumaz. O ayar per-process’tir; container geneline dair hiçbir garanti vermez, hatta tek bir worker’ın memory_limit’e kadar büyümesine izin verir. Container’a karşı asıl kalkanınız max_children’dır; memory_limit’i makul tutun ama container koruması sanmayın.

  4. Go runtime cgroup limitini varsayılan olarak bilmez. GC’yi heap büyümesine (GOGC) göre tempolar, container limitine göre değil; limitli bir container’da bu, fazla allocate edip OOM olmak demektir. Çözüm: GOMEMLIMIT’i (Go 1.19+) limitin biraz altına ayarlayın — bu soft limit, belleğe yaklaştıkça GC’yi agresifleştirir. Ayrıca GOMAXPROCS’u CPU limitine eşitleyin (automaxprocs); Go 1.25 öncesinde bunu yapmazsanız scheduler node’un tüm çekirdeklerine göre çalışırdı, Go 1.25’ten (Ağustos 2025) itibaren runtime bunu cgroup CPU limitine göre otomatik ayarlıyor.

    # Go container
    resources:
      requests: { memory: "256Mi" }
      limits:   { memory: "512Mi" }
    env:
      - { name: GOMEMLIMIT, value: "450MiB" } # limit − pay (soft limit)
      - { name: GOMAXPROCS, value: "1" }      # CPU limitine eşitle

Ne yapmalı

  1. K8s’te requests = tipik, limits = peak + pay. Heap dışı kullanım (stack, buffer, off-heap, PHP extension’ları) için mutlaka pay bırakın; limiti çok sıkarsanız normal bir burst bile pod’u öldürür. Sürprizsiz davranış istiyorsanız request = limit ile Guaranteed QoS düşünün.

  2. Leak mi, yüksek peak mi ayırın. working_set yük altında yüksek bir seviyede plato yapıyorsa bu bir boyutlandırma problemidir; limiti ve knob’ları ona göre ayarlayın. Monoton tırmanıp hiç plato yapmıyorsa bu bir memory leak’tir; limiti yükseltmek onu yalnızca geciktirir, kodu düzeltmeniz gerekir.

Sonuç: Ben olsam önce working set’i ölçer, limiti gözlenen peak + pay olarak koyardım. PHP-FPM’de max_children’ı limite karşı formülle sabitler, Go’da GOMEMLIMIT + GOMAXPROCS ayarlardım. Heap dışı için her zaman pay bırakır ve grafiği izleyerek bunun bir leak mı yoksa yüksek peak mi olduğunu ayırt ederdim. Limiti sonsuza dek yükseltmek bir çözüm değil, gerçek problemi erteleme biçimidir.

Yorumlar

Yorum yapmak için GitHub hesabınızla giriş yapmanız yeterli. Yorumlar GitHub Discussions üzerinde saklanır.

Diğer Sorular

Tüm sorular

Sitede Ara

Yazı, proje ve sayfalarda arama yapmak için yazmaya başlayın.

Esc ile kapat Pagefind ile güçlendirildi