Come posso determinare la causa di una perdita di memoria apparente nella mia app Web basata su Apache / PHP?


18

Circa una volta alla settimana, ma a volte anche un paio di volte al giorno dopo aver funzionato bene per giorni, le mie istanze EC2 non rispondono. I grafici della memoria di Munin raccontano una storia piuttosto semplice: la memoria allocata alle "app" inizia a crescere e non si ferma fino a quando lo swap non viene completamente utilizzato e l'istanza viene effettivamente messa in ginocchio. Un altro grafico personalizzato mostra che il processo in costante crescita è apache2.

Eseguo un'impostazione Apache prefork standard con mod_php e alcuni script PHP. Come puoi vedere nel grafico qui sotto, succede qualcosa che innesca i processi apache2 per iniziare a consumare sempre più memoria. Il primo picco verde che ho colto in tempo e riavviato Apache prima che le cose sfuggissero di mano. Il secondo picco è diventato un po 'più lontano e l'istanza ha dovuto essere riavviata completamente.

Grafico della memoria di Munin

Quello che mi chiedo è come eseguire il debug al meglio. A meno di configurare PHP con FastCGI e farlo funzionare nei propri processi, qual è un buon modo per scoprire se è Apache o una combinazione di PHP e del mio codice che sta causando un uso eccessivo della memoria? Quali passi fareste ragazzi per rintracciare questo problema?


AGGIORNAMENTO: Sono stato in grado di rintracciare la perdita dopo essere stato coinvolto nella strass, come ha suggerito Matt di seguito.

Dopo aver trovato un processo apache2 che stava gradualmente e continuamente crescendo in memoria, ho aggiunto alcune altre chiamate error_log () al mio script PHP che stampavano la quantità totale di RSS usata in vari punti della sua esecuzione (usando l'output di ps). Ciò tuttavia si è rivelato fuorviante - mentre sembrava che l'RSS saltava solo dopo che la mia sceneggiatura era stata eseguita, in seguito il debug ha rivelato che non era proprio il caso. Stai attento!

Fortunatamente, tutte quelle chiamate error_log () si sono rivelate utili alla fine. Quando ho avviato strace ( strace -p <pid> -tt -o trace.log -s 256), ho visto che per ogni richiesta, il processo stava allocando circa 400k di memoria (cercare la chiamata di sistema "brk" e sottrarre il parametro della prima chiamata dall'ultima chiamata - alcuni di solito arrivano in uno dopo un'altro). Ho quindi cercato la più recente chiamata di sistema "write" che conteneva il mio messaggio error_log (), che mi diceva in quale punto dello script era allocata la memoria. Con alcune chiamate error_log () più strategicamente posizionate per individuare la posizione in modo più preciso, ho finalmente trovato il colpevole.

La memoria stava perdendo quando abbiamo chiamato curl_exec () dal nostro script PHP. Alcuni codici arricciati relativi alla gestione di una connessione SSL stanno facendo qualcosa di sbagliato: la perdita è andata via quando sono passato a HTTP. Il log delle modifiche di Curl fa riferimento ad alcune perdite di memoria SSL che sono state risolte in 7.19.5 (eravamo su 7.18.2), quindi ci proverò dopo.

Nel frattempo, sto correndo con un MaxRequestsPerChild molto basso che mantiene Apache entro limiti ragionevoli. Grazie a tutti!


In che modo il numero di processi figlio di Apache varia nello stesso periodo?
SimonJ,

@SimonJ Simon, ottima domanda, il numero rimane più o meno lo stesso, più meno alcuni processi. Si aggira intorno ai 60 anni quando i server hanno problemi e quando sono a riposo. Preparerò un grafico Munin per essere sicuro al 100%.
ondrej,

Non è una soluzione, ma se è noto che una delle applicazioni consuma la RAM come un matto, allora è meglio mantenere lo swap off: quando il kernel rileva la mancanza di RAM, ucciderà i maiali di memoria più grandi (apache). Con lo swap abilitato, il kernel ucciderà alcuni processi molto più tardi, perché lo swap è molto più lento della RAM. Nessuno scambio: recupero più rapido, tempi di inattività ridotti. (Ho provato a disabilitare lo swap in un caso simile su una macchina con 8GiB di RAM, quindi YMMW.)
chronos,

Risposte:


5

Rintracciare ciò che sta causando il problema può essere un dolore nel culo. La prima cosa che farei se avessi un problema del genere è ridurre MaxRequestsPerChilda un numero aggressivamente basso (~ 100-200) e vedere se questo fa la differenza. In tal caso, probabilmente hai del codice che perde memoria in un loop da qualche parte e ti consigliamo di eseguire un controllo del codice.

Un'altra cosa da guardare è il fullstatus di Apache, vedere se riesci a scoprire quale particolare richiesta sta causando la perdita di memoria. Ottieni i PID sui tuoi processi sospetti ed esegui una sequenza su di essi.


Grazie Matt. 'ps aux | grep apache2 'mi dice che dei circa 60 processi attivi, circa una dozzina sta usando molta più memoria di quanto dovrebbero (> 100 MB in RSS). Ho esaminato l'output di / proc / <pid> / smaps e ho scoperto che ognuno ha esattamente una mappatura anonima che occupa il 95% + dello spazio. Ora sto cercando di capire cosa e quando allocato questo enorme pezzo di memoria. Esaminerò la situazione - grazie per la punta.
ondrej,

2

Venerdì @ esattamente alle 23:00? Ciò corrisponde a un tempo di backup? Il sistema ha l'I / O disponibile per servire processi e backup in quel momento? Il software di tendenza tende anche a # procs o persino ad scoreboard apache, che ne dici di I / O su disco?

La prima cosa che farei sarebbe calcolare la quantità di mem necessaria per ogni proc, quindi impostare un limite ragionevole per MaxRequests in apache in modo che $ procmem * $ procs non possa superare la RAM disponibile. Sospetto che la tua istanza debba essere riavviata perché OOM dà il via a una caccia alle streghe che è probabilmente (spesso) non molto fruttuosa. È necessario al fine di garantire la vostra casella in grado di gestire questi tempi pesanti, rimanendo entro i suoi limiti e non andare a swap e certamente non OOM. Questo è più difficile se hai cronjobs in corso, ed estremamente difficile se detti cronjobs funzionano unilatteralmente senza assicurarsi che sia sicuro (cioè lo script ogni 5 minuti non controlla se l'ultimo 5min è ancora in esecuzione).

Ora che ti sei assicurato che anche se le cose andranno male, non avrai bisogno di riavviare il box, le cose inizieranno molto meglio per te. Sarai in grado di accedere in questi momenti difficili e avere una buona idea di cosa sta succedendo usando top, dstat, free -m, iostat, ecc.

Vale la pena provare il metodo di Matt, ma dovrebbe essere usato solo come strumento per la risoluzione dei problemi, non consiglio di mantenerlo in questo modo perché renderà il problema generale molto più difficile da trovare la prossima volta che lo stai cercando. Detto questo, risolverà davvero solo i problemi con apache / module e non con nulla nel tuo codice. Penso che sarai d'accordo sul fatto che le possibilità sono buone, non è una sorta di perdita di memoria nel modulo apache (supponendo che stai usando una distribuzione affidabile).


0

La prima domanda da porsi è qual è l'applicazione che esegue Apache?

È quello che hai scritto o un'app di terze parti?

A quali altri componenti / pacchetti fa riferimento?

Sei aggiornato sui tuoi pacchetti?

Qualcosa di specifico nei tuoi httpd.conffile relativo alle prestazioni?


0

Se il tuo problema è causato dall'applicazione PHP e se hai scritto tu stesso il software, ti consiglio di usare un profiler come ad esempio PHP Quick Profiler . Se hai molte transazioni di database in corso, un software come ad esempio Kontrollbase potrebbe aiutarti a trovare il problema lì.


Raffael, grazie. Sì, l'app PHP è mia e non colpisce nessun database SQL. Darò un colpo a PHP Quick Profiler e riferirò indietro.
ondrej,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.