Como posso determinar a causa de um aparente vazamento de memória no meu aplicativo Web baseado em Apache / PHP?

18

Cerca de uma vez por semana, mas às vezes até duas vezes por dia após uma boa execução por dias, minhas instâncias do EC2 ficam sem resposta. Os gráficos de memória de Munin contam uma história bastante direta: a memória alocada para "aplicativos" começa a crescer e não para até que a troca seja totalmente usada e a instância seja efetivamente derrubada. Outro gráfico personalizado mostra que o processo em constante crescimento é o apache2.

Eu executo uma configuração padrão do Apache pré-fork com mod_php e alguns scripts PHP. Como você pode ver no gráfico abaixo, algo acontece que aciona os processos do apache2 para começar a consumir mais e mais memória. O primeiro pico verde que peguei a tempo e reiniciei o Apache antes que as coisas saíssem do controle. O segundo pico foi um pouco mais longe e a instância teve que ser reiniciada imediatamente.

Munin Memory Graph

O que eu quero saber é como melhor depurar isso. Além de configurar o PHP com FastCGI e executá-lo em seus próprios processos, qual é uma boa maneira de descobrir se é o Apache ou uma combinação de PHP e meu código que está causando o uso excessivo de memória? Que medidas vocês tomariam para rastrear esse problema?


ATUALIZAÇÃO: Consegui rastrear o vazamento depois de me envolver, como sugeriu Matt abaixo.

Depois de encontrar um processo apache2 que crescia gradualmente e continuamente na memória, adicionei mais algumas chamadas error_log () ao meu script PHP que imprimia a quantidade total de RSS usado em vários pontos de sua execução (usando a saída de ps). No entanto, isso acabou sendo enganoso - embora parecesse que o RSS pulou apenas após a execução do meu script, a depuração posterior revelou que esse não era realmente o caso. Seja cuidadoso!

Felizmente, todas essas chamadas error_log () acabaram sendo úteis no final. Quando iniciei strace ( strace -p <pid> -tt -o trace.log -s 256), vi que para cada solicitação, o processo estava alocando cerca de 400k de memória (procure a chamada do sistema 'brk' e subtraia o parâmetro da primeira chamada da última chamada - alguns geralmente vêm em um depois de outro). Em seguida, procurei a chamada mais recente do sistema 'write' que continha minha mensagem error_log (), que dizia em que ponto do script a memória estava sendo alocada. Com mais algumas chamadas error_log () estrategicamente colocadas para identificar o local com mais precisão, finalmente encontrei o culpado.

A memória estava vazando quando chamamos curl_exec () do nosso script PHP. Algum código de ondulação relacionado ao manuseio de uma conexão SSL está fazendo algo errado - o vazamento desapareceu quando mudei para o HTTP. O changelog de Curl faz referência a alguns vazamentos de memória SSL que foram corrigidos na 7.19.5 (estávamos na 7.18.2), então tentarei isso a seguir.

Enquanto isso, estou executando um MaxRequestsPerChild muito baixo que mantém o Apache dentro de limites razoáveis. Obrigado a todos!

ondrej
fonte
Como o número de processos filhos do apache varia no mesmo período?
28909 SimonJ
@ SimonJ Simon, ótima pergunta, o número permanece praticamente o mesmo, além de alguns processos. Ele fica em torno de 60 quando os servidores estão tendo problemas e quando estão em repouso. Vou configurar um gráfico Munin para ter 100% de certeza.
ondrej
Não é uma solução, mas se um dos aplicativos é conhecido por comer RAM como um louco, é melhor manter a troca: quando o kernel detecta falta de RAM, ele mata os maiores porcos da memória (apache). Com a troca ativada, o kernel matará alguns processos muito mais tarde, porque a troca é muito mais lenta que a RAM. Sem troca - recuperação mais rápida, menor tempo de inatividade. (Eu só tentei desativando troca em um caso semelhante em uma máquina com 8GiB RAM, de modo YMMW.)
chronos

Respostas:

5

Rastrear o que está causando o problema pode ser um pé no saco. A primeira coisa que eu faria se tivesse um problema como esse é reduzir MaxRequestsPerChildpara um número extremamente baixo (~ 100-200) e ver se isso faz diferença. Se isso acontecer, você provavelmente terá um código que está vazando memória em um loop em algum lugar e desejará executar uma auditoria de código.

Outra coisa a considerar é o status completo do Apache, veja se você consegue descobrir qual solicitação específica está causando o vazamento de memória. Obtenha os PIDs nos seus processos suspeitos e execute um rastreio neles.

mate
fonte
Obrigado Matt. 'ps aux | O grep apache2 'me diz que dos cerca de 60 processos ativos, cerca de uma dúzia está usando muito mais memória do que deveria (> 100 MB em RSS). Analisei a saída de / proc / <pid> / smaps e descobri que cada um tem exatamente um mapeamento anônimo que ocupa 95% do espaço. Agora estou tentando descobrir o que e quando alocado esse enorme pedaço de memória. Vou dar uma olhada - obrigado pela dica.
28610 ondrej
2

Sexta-feira @ exatamente 23:00? Isso corresponde a um tempo de backup? Seu sistema possui a E / S disponível para atender processos e backups naquele momento? Seu software de tendência também tende a # procs ou até o apache scoreboard, que tal E / S de disco?

A primeira coisa que eu faria seria calcular a quantidade de mem que cada proc leva e definir um limite razoável para MaxRequests no apache, para que $ procmem * $ procs não possam exceder a RAM disponível. Eu suspeito que sua instância precisa ser reiniciada porque o OOM inicia uma caça às bruxas que provavelmente (muitas vezes) não é muito proveitosa. Você precisa garantir que sua caixa possa lidar com esses tempos pesados, permanecendo dentro de seus limites e não trocando e, certamente, não OOM. Isso é mais difícil se você tiver o cronjobs em funcionamento e extremamente difícil se o cronjobs for executado unilateralmente, sem ter certeza de que é seguro executar (ou seja, o script a cada 5 minutos não verifica se o último 5min ainda está em execução).

Agora que você garantiu que, mesmo que as coisas dêem errado, não será necessário reiniciar sua caixa, as coisas começarão a ficar muito melhores para você. Você poderá fazer login durante esses tempos difíceis e ter uma boa idéia do que está acontecendo usando top, dstat, free -m, iostat etc.

Vale a pena tentar o método de Matt, mas só deve ser usado como uma ferramenta para solução de problemas. Não recomendo mantê-lo dessa maneira, pois tornará o problema geral muito mais difícil de encontrar na próxima vez que você o procurar. Dito isto, ele realmente provocará problemas com o apache / modules e não nada no seu código. Acho que você concorda que as chances são boas de que não seja algum tipo de vazamento de memória no módulo apache (supondo que você esteja usando uma distribuição respeitável).

fimbulvetr
fonte
0

A primeira pergunta a fazer é o que o aplicativo está executando no Apache?

Você escreveu ou é um aplicativo de terceiros?

Quais outros componentes / pacotes ele faz referência?

Você está atualizado em seus pacotes?

Alguma coisa específica em seus httpd.confarquivos está relacionada ao desempenho?

Warren
fonte
0

Se o seu problema é causado pelo aplicativo PHP e se você mesmo escreveu o software, recomendo que você use um criador de perfil como, por exemplo, o PHP Quick Profiler . Se você tiver muitas transações de banco de dados, um software como o Kontrollbase pode ajudá-lo a encontrar o problema.

Raffael Luthiger
fonte
Raffael, obrigado. Sim, o aplicativo PHP é meu e não atinge nenhum banco de dados SQL. Vou dar uma chance ao PHP Quick Profiler e relatar.
ondrej