1. Alarm Tengah Malam: Saat Layanan Produksi Mulai Tumbang
Pukul 02.14 dini hari, ponsel saya bergetar liar. Bukan sekadar notifikasi biasa, melainkan alarm PagerDuty bernada darurat yang menandakan latensi API gateway melampaui ambang batas kritis 3000ms. Di balik layar monitor Grafana, grafik penggunaan memori pada kluster PHP-FPM melonjak vertikal membentuk garis lurus ke atas, lalu mendadak flat karena sistem operasi mulai menelan proses-proses tersebut satu per satu.
[!WARNING] Ketika Linux OOM (Out-of-Memory) Killer mulai beraksi di tengah trafik tinggi, dia tidak akan meminta izin untuk mematikan proses. Proses PHP-FPM yang melayani ribuan request aktif akan dibunuh tanpa ampun, memicu gelombang 502 Bad Gateway massal di hadapan Nginx.
Situasi di ruang perang digital malam itu terasa mencekam. Tim SRE (Site Reliability Engineering) langsung berkumpul di kanal Discord darurat. Nginx mencatat lonjakan error connect() to upstream (111: Connection refused) secara masif. Jelas, ini bukan sekadar lonjakan trafik organik dari kampanye marketing tengah malam, melainkan sebuah pertanda klasik adanya kebocoran memori (memory leak) sistemik yang perlahan mencekik nyawa backend web kita.
2. Jejak Digital: Menelusuri Anomali Log & Lonjakan Metrik
Tanpa membuang waktu, saya langsung melompat ke terminal dan mengakses salah satu node aplikasi menggunakan SSH untuk memeriksa apa yang sebenarnya terjadi di balik layar. Langkah pertama seorang detektif infrastruktur adalah melihat log sistem kernel untuk memastikan kecurigaan terhadap OOM Killer:
# Memeriksa jejak OOM Killer di kernel ring buffer
dmesg -T | grep -i oom
Benar saja, baris demi baris log menampilkan pesan kelam: Out of memory: Kill process 14202 (php-fpm) score 859 or sacrifice child. Kernel terpaksa mengorbankan worker PHP-FPM karena konsumsi RAM telah menyentuh angka 98% dari total 32GB yang tersedia pada instance c5.2xlarge tersebut.
Namun, mematikan proses hanyalah solusi tambal sulam. Kita harus menemukan akar masalahnya. Saya segera mengarahkan pandangan ke metrik Prometheus yang dikoleksi oleh php-fpm_exporter. Grafik menunjukkan korelasi kuat antara jumlah request yang dilayani oleh satu worker pool dengan peningkatan baseline memori yang tidak pernah turun kembali ke titik awal (retained memory).
# Konfigurasi upstream Nginx yang sempat kewalahan menangani disconnect worker
upstream php_backend {
server unix:/run/php/php8.2-fpm.sock weight=100 max_fails=3 fail_timeout=5s;
keepalive 32;
}
Pola konsumsi memori ini sangat khas: setiap kali sebuah skrip PHP selesai dieksekusi, seharusnya garbage collector membersihkan semua variabel, objek, dan koneksi yang dialokasikan. Namun, pada kasus ini, ada memori yang tertahan di dalam siklus hidup worker PHP-FPM, menumpuk lapis demi lapis seiring bertambahnya request yang masuk, hingga akhirnya membawa kita pada investigasi mendalam terhadap kode aplikasi dan konfigurasi proses manager.
3. Membongkar Biang Kerok: Akar Masalah yang Sebenarnya
Setelah menarik sampel dump memori dan menganalisis kode sumber menggunakan alat profiling, terungkaplah dua biang kerok utama yang bersekongkol menjatuhkan sistem kita.
Pertama, para developer menggunakan pustaka ORM pihak ketiga yang melakukan eager loading relasi database secara berlebihan di dalam perulangan (loop), ditambah adanya circular reference pada objek layanan singleton yang di-cache secara manual di dalam memori global selama siklus hidup proses.
Kedua, konfigurasi pm.max_requests pada file pool.d/www.conf diatur bernilai 0 (infinity). Artinya, worker PHP-FPM tidak akan pernah daur ulang secara otomatis kecuali jika proses tersebut crash atau dimatikan manual oleh administrator.
; Konfigurasi bawaan yang berbahaya di lingkungan produksi berbeban tinggi
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 0 ; <-- INI ADALAH BOM WAKTU
[!TIP] Menyetel
pm.max_requests = 0mungkin terlihat efisien karena menghindari overhead pembuatan proses baru. Namun, pada ekosistem aplikasi PHP skala besar yang hampir mustahil 100% bebas dari micro-leak pada pustaka pihak ketiga, pengaturan ini adalah bom waktu yang menunggu meledak.
4. Tindakan Darurat: Menghidupkan Kembali Layanan dalam Hitungan Menit
Sembari tim backend mulai merombak kode untuk memutuskan referensi melingkar dan memperbaiki manajemen memori objek, layanan produksi harus segera diselamatkan detik itu juga agar tidak semakin merugi.
Solusi paling cepat dan aman di tingkat infrastruktur adalah membatasi umur setiap worker PHP-FPM dengan memaksa daur ulang otomatis setelah melayani sejumlah request tertentu. Berikut adalah one-liner terminal darurat yang saya eksekusi untuk memperbarui konfigurasi dan me-reload layanan tanpa memutus koneksi aktif secara brutal:
# Langkah darurat: Mengubah pm.max_requests dan mereload PHP-FPM secara graceful
sudo sed -i 's/^pm.max_requests = 0/pm.max_requests = 500/' /etc/php/8.2/fpm/pool.d/www.conf && sudo systemctl reload php8.2-fpm
Perintah di atas langsung menyuntikkan batas 500 request per worker. Artinya, begitu sebuah worker PHP-FPM telah melayani 500 request, proses tersebut akan dimatikan secara bersih oleh master process dan digantikan oleh worker baru yang masih 'segar' dengan alokasi memori bersih. Grafik RAM di Grafana langsung melandai, dan latensi kembali normal dalam kurun waktu kurang dari 60 detik.
5. Kebijakan Retrospektif: Menambal Celah agar Tak Pernah Terulang
Insiden pukul dua pagi tersebut memberikan pelajaran mahal bagi seluruh tim. Kami tidak boleh lagi mengandalkan keberuntungan atau asumsi bahwa aplikasi kita bersih dari kebocoran memori. Sebuah arsitektur defensif harus segera ditegakkan.
Kami merumuskan tiga pilar kebijakan baru:
- Mandatory
pm.max_requests: Setiap pool PHP-FPM di seluruh lingkungan staging dan produksi wajib memiliki batas maksimum request antara500hingga1000tergantung kompleksitas aplikasi. - Otomasi Profiling pada CI/CD: Mengintegrasikan alat pemindai memori seperti Xdebug Profiler dan unit test berbasis alokasi memori ke dalam pipeline GitLab CI/CD sebelum kode di-merge ke branch utama.
- Pemisahan Resource Heavy Worker: Memisahkan pool PHP-FPM untuk proses latar belakang (seperti pemrosesan antrean Redis/Supervisor) dengan pool untuk request HTTP reguler.
# Konfigurasi Nginx yang optimal setelah penyesuaian pool FPM
server {
listen 80;
server_name api.domain.com;
root /var/www/html/public;
index index.php;
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_read_timeout 300s;
fastcgi_send_timeout 300s;
}
}
6. Pasang Alarm: Otomasi Deteksi Dini untuk Tim On-Call
Agar tidak ada lagi kepanikan di tengah malam, kami melengkapi sistem pemantauan dengan aturan peringatan (alerting rules) yang proaktif di Prometheus dan Alertmanager. Jika rasio penggunaan memori node mencapai 80% selama lebih dari 3 menit, sistem akan otomatis mengirimkan peringatan awal ke kanal komunikasi tim.
Berikut adalah contoh konfigurasi Prometheus Alerting Rule yang kami terapkan untuk mendeteksi potensi anomali memori sebelum berubah menjadi bencana OOM Killer:
groups:
- name: php-fpm-memory-alerts
rules:
- alert: HighPHPFPMNodeMemory
expr: (node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes > 0.80
for: 3m
labels:
severity: warning
annotations:
summary: "Penggunaan memori node tinggi (di atas 80%)"
description: "Instance {{ $labels.instance }} mengalami tekanan memori tinggi, potensi memory leak pada PHP-FPM terdeteksi."
- alert: PHPFPMPoolRestartSpike
expr: rate(php_fpm_slow_requests[5m]) > 0
for: 2m
labels:
severity: critical
annotations:
summary: "Lonjakan slow request pada PHP-FPM"
description: "Worker pool mengalami antrean panjang akibat kekurangan sumber daya."
- alert: OOMKillerTriggered
expr: increase(node_vmstat_oom_kill[1m]) > 0
labels:
severity: page
annotations:
summary: "DARURAT: OOM Killer aktif!"
description: "Kernel Linux telah mematikan proses akibat kehabisan RAM. Tindakan segera diperlukan."
Dengan aturan peringatan berlapis ini, tim SRE kini memiliki waktu bernapas dan bertindak preventif jauh sebelum sistem benar-benar tumbang. Pengalaman pahit malam itu mengubah cara pandang kami terhadap pentingnya manajemen siklus hidup proses di balik ekosistem web modern.