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

17

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

Eu executei uma configuração padrão do Apache no prefork com mod_php e alguns scripts PHP. Como você pode ver no gráfico abaixo, acontece algo 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 reinicializada imediatamente.

Gráfico de memória do Munin

O que eu estou querendo saber é como melhor depurar isso. Curta de configurar o PHP com FastCGI e executá-lo em seus próprios processos, o que é 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 passos você tomaria para rastrear esse problema?

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

Depois de encontrar um processo apache2 que estava crescendo gradual e continuamente na memória, adicionei mais algumas chamadas error_log () ao meu script PHP que imprimia a quantidade total de RSS usada em vários pontos em sua execução (usando a saída de ps). Isso, no entanto, acabou por ser enganador - embora parecesse que o RSS saltou apenas depois que meu script foi executado, mais tarde, a depuração revelou que esse não era o caso. Tenha cuidado!

Felizmente, todas essas chamadas error_log () acabaram sendo úteis no final. Quando ativei 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 de sistema 'brk' e subtraia o parâmetro da primeira chamada da última chamada - alguns geralmente vem em um após o outro). Em seguida, procurei pela chamada de sistema 'write' mais recente que continha minha mensagem error_log (), que me informava em que ponto do script a memória estava sendo alocada. Com mais algumas chamadas error_log () estrategicamente colocadas para identificar a localização com mais precisão, finalmente encontrei o culpado.

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

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

    
por ondrej 28.11.2009 / 12:04

4 respostas

5

Rastrear o que está causando o problema pode ser uma dor no rabo. A primeira coisa que eu faria se tivesse um problema como esse seria reduzir MaxRequestsPerChild para um número agressivamente baixo (~ 100-200) e ver se isso faz diferença. Em caso afirmativo, você provavelmente tem código que está vazando memória em um loop em algum lugar e você vai querer executar uma auditoria de código.

Outra coisa a se ver é o fullstatus do Apache, veja se você pode descobrir qual solicitação em particular está causando o vazamento de memória. Obtenha os PIDs nos seus processos suspeitos e corra um risco neles.

    
por 28.11.2009 / 17:43
2

sexta-feira @ exatamente às 23h? Isso corresponde a um tempo de backup? Seu sistema tem a E / S disponível para atender processos e backups nesse momento? O seu software de tendências também tem tendência # procs ou mesmo apache scoreboard, e quanto a E / S de disco?

A coisa primeiro que eu faria seria calcular a quantidade de mem que cada proc leva, então configure um limite razoável para MaxRequests no apache para que $ procmem * $ procs não possa exceder o RAM disponível. Eu suspeito que sua instância precise ser reiniciada porque a OOM inicia uma caça às bruxas que provavelmente não é muito proveitosa. Você precisa para garantir que sua caixa possa lidar com esses tempos difíceis mantendo-se dentro de seus limites e não indo para swap e certamente não OOM. Isso é mais difícil se você tiver cron cronicando, e é extremamente difícil se cronjobs executados unilatterally sem ter certeza que é seguro rodar (isto é, o script a cada 5 minutos falha em checar se os últimos 5min ainda estão rodando).

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

O método de Matt pode valer a pena ser testado, mas deve ser usado apenas como uma ferramenta para solução de problemas. Não é recomendável mantê-lo dessa forma, pois isso tornará o problema geral muito mais difícil de encontrar na próxima vez que for procurá-lo. Dito isso, isso só vai realmente provocar problemas com o apache / modules e nada em seu código. Eu acho que você vai concordar que as chances são boas, não é algum tipo de vazamento de memória no módulo do Apache (supondo que você esteja usando uma distro respeitável).

    
por 28.11.2009 / 18:44
0

A primeira pergunta a ser feita é qual é o aplicativo executado pelo Apache?

É um que você escreveu ou um aplicativo de terceiros?

Quais outros componentes / pacotes ele faz referência?

Você está atualizado sobre seus pacotes?

Algo específico em seus arquivos httpd.conf relacionados ao desempenho?

    
por 28.11.2009 / 13:12
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, PHP Quick Profiler . Se você tem muitas transações de banco de dados em andamento, um software como, por exemplo, Kontrollbase pode ajudá-lo a encontrar o problema.

    
por 28.11.2009 / 18:30