Bagaimana saya bisa menentukan penyebab kebocoran memori yang jelas di aplikasi web berbasis Apache / PHP saya?

18

Sekitar sekali seminggu, tetapi kadang-kadang bahkan beberapa kali sehari setelah berjalan baik selama berhari-hari, mesin virtual EC2 saya menjadi tidak responsif. Grafik memori Munin menceritakan kisah yang sangat mudah: memori yang dialokasikan untuk "aplikasi" mulai tumbuh dan tidak berhenti sampai swap sepenuhnya digunakan dan instance secara efektif diturunkan hingga ke lutut. Grafik kustom lain menunjukkan bahwa proses yang terus berkembang adalah apache2.

Saya menjalankan setup Apache prefork standar dengan mod_php dan beberapa skrip PHP. Seperti yang dapat Anda lihat pada grafik di bawah ini, terjadi sesuatu yang memicu proses apache2 untuk mulai menggunakan lebih banyak memori. Lonjakan hijau pertama yang saya ingat waktu dan restart Apache sebelum semuanya keluar dari tangan. Lonjakan kedua menjadi sedikit lebih jauh dan instans harus reboot langsung.

Grafik Memori Munin

Yang saya ingin tahu adalah bagaimana cara terbaik men-debug ini. Kurang menyiapkan PHP dengan FastCGI dan menjalankannya dalam prosesnya sendiri, apa cara yang baik untuk mengetahui apakah itu Apache atau kombinasi PHP dan kode saya yang menyebabkan penggunaan memori yang berlebihan? Langkah apa yang akan kalian lakukan untuk melacak masalah ini?


UPDATE: Saya bisa melacak kebocoran setelah terlibat, seperti yang disarankan Matt di bawah ini.

Setelah menemukan proses apache2 yang secara bertahap dan terus tumbuh dalam memori, saya menambahkan beberapa error_log () panggilan ke skrip PHP saya yang mencetak jumlah total RSS yang digunakan di berbagai titik dalam pelaksanaannya (menggunakan output ps). Namun itu ternyata menyesatkan - sementara tampaknya RSS melonjak hanya setelah skrip saya selesai dieksekusi, kemudian debugging mengungkapkan bahwa bukan itu masalahnya. Hati-hati!

Untungnya, semua panggilan error_log () ini ternyata bermanfaat pada akhirnya. Ketika saya menjalankan strace ( strace -p <pid> -tt -o trace.log -s 256), saya melihat bahwa untuk setiap permintaan, proses mengalokasikan sekitar 400k memori (mencari panggilan sistem 'brk' dan mengurangi parameter panggilan pertama dari panggilan terakhir - beberapa biasanya datang dalam satu setelah yang lainnya). Saya kemudian mencari panggilan sistem 'tulis' terbaru yang berisi pesan error_log () saya, yang memberi tahu saya pada titik mana dalam skrip memori yang dialokasikan. Dengan beberapa panggilan error_log () yang ditempatkan lebih strategis untuk menentukan lokasi dengan lebih akurat, akhirnya saya menemukan pelakunya.

Memori bocor ketika kami memanggil curl_exec () dari skrip PHP kami. Beberapa kode ikal yang terkait dengan penanganan koneksi SSL melakukan kesalahan - kebocoran hilang ketika saya beralih ke HTTP. Curel's changelog mereferensikan beberapa kebocoran memori SSL yang diperbaiki pada 7.19.5 (kami menggunakan 7.18.2) jadi saya akan mencobanya nanti.

Sementara itu, saya menjalankan dengan MaxRequestsPerChild yang sangat rendah yang menjaga Apache dalam batas yang wajar. Terimakasih semuanya!

ondrej
sumber
Bagaimana jumlah proses anak apache bervariasi pada periode yang sama?
SimonJ
@ Simon Simon, pertanyaan besar, jumlahnya tetap hampir sama, plus minus beberapa proses. Itu melayang di sekitar 60 ketika server mengalami masalah serta ketika mereka sedang beristirahat. Saya akan mengatur grafik Munin menjadi 100% yakin.
ondrej
Bukan solusi, tetapi jika salah satu aplikasi diketahui memakan RAM seperti orang gila, maka lebih baik untuk tetap menukar: ketika kernel mendeteksi kekurangan RAM, itu akan membunuh babi memori terbesar (apache). Dengan swap diaktifkan, kernel akan membunuh beberapa proses lebih lama, karena swap jauh lebih lambat daripada RAM. Tanpa swap - pemulihan lebih cepat, downtime yang lebih kecil. (Saya hanya mencoba menonaktifkan swap kasus serupa pada mesin dengan 8GiB RAM, sehingga YMMW.)
chronos

Jawaban:

5

Melacak APA yang menyebabkan masalah bisa jadi menyebalkan. Hal pertama yang akan saya lakukan jika saya memiliki masalah seperti itu adalah mengurangi MaxRequestsPerChildke angka yang sangat rendah (~ 100-200) dan melihat apakah itu membuat perbedaan. Jika ya, maka Anda mungkin memiliki kode yang membocorkan memori dalam satu lingkaran di suatu tempat dan Anda ingin menjalankan audit kode.

Hal lain yang perlu dilihat adalah status penuh Apache, lihat apakah Anda dapat mengetahui permintaan khusus apa yang menyebabkan kebocoran memori. Dapatkan PID pada proses yang Anda duga dan jalankan strace pada mereka.

matt
sumber
Terima kasih, Matt. 'ps aux | grep apache2 'memberi tahu saya bahwa dari 60 atau lebih proses yang aktif, sekitar selusin menggunakan lebih banyak memori daripada yang seharusnya (> 100MB dalam RSS). Saya telah melihat output dari / proc / <pid> / smaps dan menemukan bahwa masing-masing memiliki tepat satu pemetaan anonim yang mengambil ruang + 95%. Saya sekarang mencoba mencari tahu apa dan kapan mengalokasikan memori yang sangat besar ini. Saya akan melihat ke strace - terima kasih atas tipnya.
ondrej
2

Jumat @ tepat 11 malam? Apakah itu sesuai dengan waktu cadangan? Apakah sistem Anda memiliki I / O yang tersedia untuk melayani proses dan cadangan pada waktu itu? Apakah Anda trending software juga tren # procs atau bahkan apache papan skor, bagaimana dengan disk I / O?

Hal pertama yang akan saya lakukan adalah menghitung berapa banyak mem yang diambil setiap proc, kemudian menetapkan batas yang wajar untuk MaxRequests di apache sehingga $ procmem * $ procs tidak dapat melebihi ram yang tersedia. Saya curiga instance Anda perlu di-boot ulang karena OOM memulai perburuan penyihir yang kemungkinan (sering) tidak terlalu membuahkan hasil. Anda perlu memastikan bahwa kotak Anda dapat menangani masa-masa sulit ini dengan tetap berada dalam batasnya dan tidak pergi untuk bertukar dan tentu saja bukan OOM. Ini lebih sulit jika Anda memiliki cronjobs, dan sangat sulit jika kata cronjobs berjalan tanpa bantuan tanpa memastikan itu aman untuk dijalankan (yaitu setiap skrip 5 menit gagal memeriksa apakah 5 menit terakhir masih berjalan).

Sekarang setelah Anda memastikan bahwa meskipun ada kesalahan besar, Anda tidak perlu me-reboot kotak Anda, semuanya akan mulai jauh lebih baik untuk Anda. Anda akan dapat login selama masa-masa sulit ini dan mendapatkan ide bagus tentang apa yang terjadi menggunakan top, dstat, free -m, iostat, dll.

Metode Matt mungkin layak untuk dicoba, tetapi seharusnya hanya digunakan sebagai alat untuk pemecahan masalah, saya tidak menyarankan menjaganya seperti itu karena itu akan membuat masalah keseluruhan jauh lebih sulit untuk menemukan waktu berikutnya Anda mencarinya. Yang mengatakan, itu hanya akan benar-benar mengatasi masalah dengan apache / modul dan bukan apa pun dalam kode Anda. Saya pikir Anda akan setuju kemungkinannya baik itu bukan semacam kebocoran memori dalam modul apache (dengan asumsi Anda menggunakan distro yang memiliki reputasi baik).

fimbulvetr
sumber
0

Pertanyaan pertama yang harus ditanyakan adalah apakah aplikasi berjalan melalui Apache?

Apakah itu yang Anda tulis, atau aplikasi pihak ketiga?

Apa komponen / paket lain yang dirujuknya?

Apakah Anda mutakhir tentang paket Anda?

Adakah yang spesifik dalam httpd.conffile Anda yang terkait dengan kinerja?

warren
sumber
0

Jika masalah Anda disebabkan oleh aplikasi PHP dan jika Anda menulis sendiri perangkat lunaknya maka saya sarankan Anda untuk menggunakan profiler seperti misalnya PHP Quick Profiler . Jika Anda memiliki banyak transaksi basis data yang sedang berjalan maka sebuah perangkat lunak seperti misalnya Kontrollbase dapat membantu Anda menemukan masalah di sana.

Raffael Luthiger
sumber
Raffael, terima kasih. Ya, aplikasi PHP adalah milik saya dan itu tidak mengenai database SQL apa pun. Saya akan mencoba PHP Quick Profiler dan melaporkannya kembali.
ondrej