¿Cómo puedo determinar la causa de una aparente pérdida de memoria en mi aplicación web basada en Apache / PHP?

18

Aproximadamente una vez a la semana, pero a veces incluso un par de veces al día después de funcionar bien durante días, mis instancias de EC2 dejan de responder. Los gráficos de memoria de Munin cuentan una historia bastante sencilla: la memoria asignada a las "aplicaciones" comienza a crecer y no se detiene hasta que el intercambio se utiliza por completo y la instancia se pone de rodillas. Otro gráfico personalizado muestra que el proceso en constante crecimiento es apache2.

Ejecuto una configuración estándar de Apache prefork con mod_php y algunos scripts PHP. Como puede ver en el gráfico a continuación, sucede algo que activa los procesos apache2 para comenzar a consumir más y más memoria. El primer pico verde lo atrapé a tiempo y reinicié Apache antes de que las cosas se salieran de control. El segundo pico llegó un poco más lejos y la instancia tuvo que reiniciarse por completo.

Munin Memory Graph

Lo que me pregunto es cómo depurar esto mejor. A menos que configure PHP con FastCGI y lo ejecute en sus propios procesos, ¿cuál es una buena manera de averiguar si es Apache o una combinación de PHP y mi código lo que está causando el uso excesivo de memoria? ¿Qué pasos tomarían para rastrear este problema?


ACTUALIZACIÓN: pude rastrear la fuga después de involucrar strace, como Matt sugirió a continuación.

Después de encontrar un proceso apache2 que crecía gradual y continuamente en la memoria, agregué algunas llamadas más de error_log () a mi script PHP que imprimieron la cantidad total de RSS utilizada en varios puntos de su ejecución (usando la salida de ps). Sin embargo, eso resultó ser engañoso: si bien parecía que RSS saltó solo después de que mi script terminó de ejecutarse, la depuración posterior reveló que ese no era realmente el caso. ¡Ten cuidado!

Afortunadamente, todas esas llamadas error_log () resultaron ser útiles al final. Cuando encendí strace ( strace -p <pid> -tt -o trace.log -s 256), vi que para cada solicitud, el proceso estaba asignando alrededor de 400k de memoria (busque la llamada al sistema 'brk' y reste el parámetro de la primera llamada de la última llamada; algunas generalmente vienen en una después de otro). Luego busqué la llamada al sistema 'write' más reciente que contenía mi mensaje error_log (), que me decía en qué punto del script se estaba asignando la memoria. Con unas pocas llamadas error_log () estratégicamente ubicadas para identificar la ubicación con mayor precisión, finalmente encontré al culpable.

La memoria estaba perdiendo cuando llamamos curl_exec () desde nuestro script PHP. Algunos códigos curl relacionados con el manejo de una conexión SSL están haciendo algo mal: la fuga desapareció cuando cambié a HTTP. El registro de cambios de Curl hace referencia a algunas pérdidas de memoria SSL que se solucionaron en 7.19.5 (estábamos en 7.18.2), así que lo intentaré a continuación.

Mientras tanto, estoy ejecutando con un MaxRequestsPerChild muy bajo que mantiene a Apache dentro de límites razonables. ¡Gracias a todos!

ondrej
fuente
¿Cómo varía la cantidad de procesos secundarios apache durante el mismo período?
SimonJ
@SimonJ Simon, gran pregunta, el número se mantiene prácticamente igual, más menos algunos procesos. Se mueve alrededor de 60 cuando los servidores están teniendo problemas, así como cuando están en reposo. Sin embargo, configuraré un gráfico de Munin para estar 100% seguro.
ondrej
No es una solución, pero si se sabe que una de las aplicaciones come RAM como un loco, entonces es mejor no cambiar: cuando el kernel detecta la falta de RAM, matará a los cerdos de memoria más grandes (apache). Con el intercambio habilitado, el núcleo matará algunos procesos mucho más tarde, porque el intercambio es mucho más lento que la RAM. Sin intercambio: recuperación más rápida, menor tiempo de inactividad. (Solo he intentado deshabilitar el intercambio en un caso similar en una máquina con 8GiB RAM, así que YMMW.)
cronos

Respuestas:

5

Rastrear lo que está causando el problema puede ser un dolor de cabeza. Lo primero que haría si tuviera un problema como ese es reducirlo MaxRequestsPerChilda un número agresivamente bajo (~ 100-200) y ver si eso marca la diferencia. Si es así, entonces es probable que tenga código que está perdiendo memoria en un bucle en algún lugar y querrá ejecutar una auditoría de código.

Otra cosa a tener en cuenta es el estado completo de Apache, vea si puede averiguar qué solicitud en particular está causando la pérdida de memoria. Obtenga los PID en sus procesos sospechosos y ejecute una secuencia sobre ellos.

mate
fuente
Gracias Matt 'ps aux | grep apache2 'me dice que de los aproximadamente 60 procesos activos, alrededor de una docena están usando mucha más memoria de la que deberían (> 100 MB en RSS). Observé el resultado de / proc / <pid> / smaps y descubrí que cada uno tiene exactamente un mapeo anónimo que ocupa más del 95% del espacio. Ahora estoy tratando de averiguar qué y cuándo se le asignó esta gran porción de memoria. Voy a mirar en strace, gracias por la sugerencia.
ondrej
2

Viernes a las 11pm exactamente? ¿Corresponde eso a un tiempo de respaldo? ¿Tiene su sistema la E / S disponible para servir procesos y copias de seguridad en ese momento? ¿Su software de tendencias también tiene tendencia # procs o incluso el marcador de apache, ¿qué tal la E / S de disco?

Lo primero que haría sería calcular cuánto mem toma cada proceso, luego establecer un límite razonable para MaxRequests en apache para que $ procmem * $ procs no pueda exceder el RAM disponible. Sospecho que su instancia debe reiniciarse porque OOM inicia una cacería de brujas que probablemente (a menudo) no sea muy fructífera. Usted necesita asegurarse de que su cuadro puede manejar estos tiempos pesados al permanecer dentro de sus límites y no ir al swap y ciertamente no OOM. Esto es más difícil si tiene cronjobs funcionando, y extremadamente difícil si dichos cronjobs se ejecutan unilateralmente sin asegurarse de que sea seguro ejecutarlo (es decir, el script de cada 5 minutos no comprueba si los últimos 5 minutos todavía se están ejecutando).

Ahora que se ha asegurado de que, incluso si las cosas van mal, no tendrá que reiniciar su caja, las cosas comenzarán a ir mucho mejor para usted. Podrá iniciar sesión durante estos tiempos difíciles y tener una buena idea de lo que está sucediendo con top, dstat, free -m, iostat, etc.

Puede valer la pena probar el método de Matt, pero solo debe usarse como una herramienta para la resolución de problemas, no recomiendo mantenerlo así porque hará que el problema general sea mucho más difícil de encontrar la próxima vez que lo esté buscando. Dicho esto, solo resolverá problemas con apache / modules y no con nada en su código. Creo que estará de acuerdo en que las posibilidades son buenas, no es una especie de pérdida de memoria en el módulo Apache (suponiendo que esté usando una distribución de buena reputación).

fimbulvetr
fuente
0

La primera pregunta es ¿cuál es la aplicación que se ejecuta en Apache?

¿Es uno que escribiste o una aplicación de terceros?

¿A qué otros componentes / paquetes hace referencia?

¿Estás actualizado en tus paquetes?

¿Algo específico en sus httpd.confarchivos relacionado con el rendimiento?

madriguera
fuente
0

Si su problema es causado por la aplicación PHP y usted mismo escribió el software, le recomiendo que use un generador de perfiles como, por ejemplo, PHP Quick Profiler . Si tiene muchas transacciones de bases de datos, entonces un software como, por ejemplo, Kontrollbase podría ayudarlo a encontrar el problema allí.

Raffael Luthiger
fuente
Rafael, gracias. Sí, la aplicación PHP es mía y no afecta a ninguna base de datos SQL. Le daré una oportunidad a PHP Quick Profiler e informaré.
ondrej