Comment puis-je déterminer la cause d'une fuite de mémoire apparente dans mon application Web basée sur Apache / PHP?

18

Environ une fois par semaine, mais parfois même quelques fois par jour après un bon fonctionnement pendant des jours, mes instances EC2 ne répondent plus. Les graphiques de la mémoire de Munin racontent une histoire assez simple: la mémoire allouée aux "applications" commence à croître et ne s'arrête pas jusqu'à ce que le swap soit pleinement utilisé et que l'instance soit effectivement mise à genoux. Un autre graphique personnalisé montre que le processus en croissance constante est apache2.

J'exécute une configuration Apache standard préfork avec mod_php et quelques scripts PHP. Comme vous pouvez le voir dans le graphique ci-dessous, quelque chose se produit qui déclenche les processus apache2 pour commencer à consommer de plus en plus de mémoire. Le premier pic vert que j'ai attrapé à temps et a redémarré Apache avant que les choses ne deviennent incontrôlables. Le deuxième pic est allé un peu plus loin et l'instance a dû être redémarrée.

Munin Memory Graph

Ce que je me demande, c'est comment déboguer au mieux. À moins de configurer PHP avec FastCGI et de le faire fonctionner dans ses propres processus, quelle est une bonne façon de savoir si c'est Apache ou une combinaison de PHP et de mon code qui cause l'utilisation excessive de la mémoire? Quelles mesures prendriez-vous pour dépister ce problème?


MISE À JOUR: J'ai pu suivre la fuite après avoir impliqué Strace, comme Matt l'a suggéré ci-dessous.

Après avoir trouvé un processus apache2 qui augmentait progressivement et continuellement en mémoire, j'ai ajouté quelques appels error_log () à mon script PHP qui imprimait la quantité totale de RSS utilisée à divers moments de son exécution (en utilisant la sortie de ps). Cela s'est toutefois révélé trompeur - alors qu'il semblait que RSS n'a sauté qu'après l'exécution de mon script, un débogage ultérieur a révélé que ce n'était pas le cas. Faites attention!

Heureusement, tous ces appels error_log () se sont avérés utiles à la fin. Lorsque j'ai lancé strace ( strace -p <pid> -tt -o trace.log -s 256), j'ai vu que pour chaque demande, le processus allouait environ 400 Ko de mémoire (recherchez l'appel système 'brk' et soustrayez le paramètre du premier appel du dernier appel - quelques-uns viennent généralement en un après un autre). J'ai ensuite recherché le dernier appel système «écriture» qui contenait mon message error_log (), qui m'a dit à quel moment du script la mémoire était allouée. Avec quelques appels error_log () placés stratégiquement pour localiser plus précisément l'emplacement, j'ai finalement trouvé le coupable.

La mémoire fuyait lorsque nous avons appelé curl_exec () à partir de notre script PHP. Certains codes curl liés à la gestion d'une connexion SSL font quelque chose de mal - la fuite a disparu lorsque je suis passé à HTTP. Le journal des modifications de Curl fait référence à quelques fuites de mémoire SSL qui ont été corrigées dans 7.19.5 (nous étions sur 7.18.2), donc je vais essayer cela ensuite.

En attendant, je cours avec un MaxRequestsPerChild très bas qui maintient Apache dans des limites raisonnables. Merci tout le monde!

ondrej
la source
Comment le nombre de processus enfants apache varie-t-il au cours de la même période?
SimonJ
@SimonJ Simon, grande question, le nombre reste à peu près le même, plus quelques processus en moins. Il oscille autour de 60 lorsque les serveurs ont des problèmes ainsi qu'au repos. Je vais configurer un graphique Munin pour être sûr à 100%.
ondrej
Pas une solution, mais si l'une des applications est connue pour manger de la RAM comme un fou, alors il vaut mieux garder le swap off: lorsque le noyau détecte un manque de RAM, il tuera les plus gros porcs de la mémoire (apache). Avec le swap activé, le noyau tuera certains processus beaucoup plus tard, car le swap est beaucoup plus lent que la RAM. Pas d'échange - récupération plus rapide, temps d'arrêt plus courts. (J'ai seulement essayé de désactiver le swap dans un cas similaire sur une machine avec 8 Go de RAM, donc YMMW.)
chronos

Réponses:

5

Retrouver ce qui cause le problème peut être une douleur dans le cul. La première chose que je ferais si j'avais un problème comme celui-ci est de réduire MaxRequestsPerChildà un nombre agressivement bas (~ 100-200) et de voir si cela fait une différence. Si c'est le cas, vous avez probablement du code qui fuit de la mémoire dans une boucle quelque part et vous voudrez exécuter un audit de code.

Une autre chose à regarder est le statut complet d'Apache, voyez si vous pouvez découvrir quelle demande particulière est à l'origine de la fuite de mémoire. Obtenez les PID de vos processus suspects et exécutez-les.

mat
la source
Merci Matt. 'ps aux | grep apache2 'me dit que sur une soixantaine de processus actifs, une douzaine environ utilise beaucoup plus de mémoire qu'ils ne le devraient (> 100 Mo en RSS). J'ai regardé la sortie de / proc / <pid> / smaps et trouvé que chacun a exactement un mappage anonyme qui occupe 95% + de l'espace. J'essaie maintenant de comprendre quoi et quand allouer cet énorme morceau de mémoire. Je vais regarder dans strace - merci pour l'astuce.
ondrej
2

Vendredi @ exactement 23h? Cela correspond-il à un temps de sauvegarde? Votre système dispose-t-il des E / S pour servir les processus et les sauvegardes à ce moment-là? Est-ce que les logiciels de tendance ont également tendance # procs ou même le tableau de bord apache, qu'en est-il des E / S disque?

La première chose que je ferais serait de calculer la quantité de mem que chaque proc prend, puis de fixer une limite raisonnable pour MaxRequests dans apache afin que $ procmem * $ procs ne puisse pas dépasser le ram disponible. Je soupçonne que votre instance doit être redémarrée car OOM lance une chasse aux sorcières qui est probablement (souvent) peu fructueuse. Vous devez vous assurer que votre boîte peut gérer ces moments difficiles en restant dans ses limites et ne pas échanger et certainement pas MOO. C'est plus difficile si vous avez des cronjobs en cours, et extrêmement difficile si ces cronjobs s'exécutent unilatéralement sans vous assurer qu'il est sûr à exécuter (c'est-à-dire que le script toutes les 5 minutes ne vérifie pas si les 5 dernières minutes sont toujours en cours d'exécution).

Maintenant que vous vous êtes assuré que même si les choses tournent mal, vous n'aurez pas besoin de redémarrer votre box, les choses commenceront à aller beaucoup mieux pour vous. Vous pourrez vous connecter pendant ces périodes difficiles et avoir une bonne idée de ce qui se passe en utilisant top, dstat, free -m, iostat, etc.

La méthode de Matt peut valoir la peine d'être essayée, mais ne doit être utilisée que comme un outil de dépannage, je ne recommande pas de la conserver de cette façon car cela rendra le problème global beaucoup plus difficile à trouver la prochaine fois que vous le chercherez. Cela dit, cela ne résoudra que les problèmes avec apache / modules et rien dans votre code. Je pense que vous conviendrez que les chances sont bonnes, ce n'est pas une sorte de fuite de mémoire dans le module apache (en supposant que vous utilisez une distribution de bonne réputation).

fimbulvetr
la source
0

La première question à se poser est quelle est l'application exécutée via Apache?

Est-ce celle que vous avez écrite ou une application tierce?

À quels autres composants / packages fait-il référence?

Êtes-vous à jour sur vos packages?

Quelque chose de spécifique dans vos httpd.conffichiers concernant les performances?

garenne
la source
0

Si votre problème est dû à l'application PHP et si vous avez écrit le logiciel vous-même, je vous recommande d'utiliser un profileur comme par exemple PHP Quick Profiler . Si vous avez beaucoup de transactions de base de données en cours, un logiciel comme par exemple Kontrollbase pourrait vous aider à trouver le problème.

Raffael Luthiger
la source
Raffael, merci. Oui, l'application PHP est à moi et ne touche aucune base de données SQL. Je vais essayer PHP Quick Profiler et faire un rapport.
ondrej