如何确定基于Apache / PHP的Web应用程序中明显的内存泄漏的原因?


18

我的EC2实例大约每周一次,但有时甚至一天运行几次,几天后,我的EC2实例变得无响应。Munin的内存图讲述了一个非常简单的故事:分配给“应用程序”的内存开始增长,并且直到交换功能被完全使用并且实例被有效地压倒才停止。另一个自定义图形显示不断增长的过程是apache2。

我使用mod_php和一些PHP脚本运行标准的prefork Apache安装程序。正如您在下图中所看到的,发生了一些事情,这些事件触发apache2进程开始消耗越来越多的内存。我遇到了第一个绿色高峰,在事情失控之前重新启动了Apache。第二个峰值进一步扩大了,该实例必须完全重新启动。

穆宁记忆图

我想知道的是如何最好地调试它。缺少使用FastCGI设置PHP并使其在自己的进程中运行的方法,找出它是Apache还是PHP和我的代码的组合导致过多内存使用的好方法是什么?你们将采取什么步骤来跟踪此问题?


更新:在涉及到strace之后,我能够跟踪泄漏,如Matt所建议的那样。

找到内存中逐渐且持续增长的apache2进程后,我在PHP脚本中添加了更多error_log()调用,以打印出在执行过程中各个点使用的RSS总量(使用ps的输出)。但是事实证明这是令人误解的-尽管似乎RSS仅在执行完脚本后才跳起来,但后来的调试表明事实并非如此。小心!

幸运的是,所有这些error_log()调用最终都非常有用。当我启动strace(strace -p <pid> -tt -o trace.log -s 256)时,我发现对于每个请求,该进程都在分配约400k的内存(查找“ brk”系统调用,并从上次调用中减去第一个调用的参数-通常有几个传入)一个接一个)。然后,我搜索了包含我的error_log()消息的最新“写入”系统调用,该消息告诉我在脚本中的哪个位置分配了内存。通过再战略性地放置几个error_log()调用以更精确地定位位置,我终于找到了罪魁祸首。

当我们从PHP脚本中调用curl_exec()时,内存正在泄漏。一些与处理SSL连接有关的curl代码做错了-当我切换到HTTP时,泄漏消失了。Curl的changelog引用了7.19.5(我们在7.18.2中已修复)中的一些SSL内存泄漏,因此我将在下一个尝试。

同时,我正在以非常低的MaxRequestsPerChild运行,这使Apache处于合理范围之内。感谢大家!


apache子进程的数量在同一时期如何变化?
SimonJ

@SimonJ Simon,一个很好的问题,这个数字几乎保持不变,外加一些过程。当服务器出现故障以及处于静止状态时,它会徘徊在60左右。我将建立一个Munin图以确保100%正确。
ondrej

这不是解决方案,但是如果已知其中一个应用程序疯狂地吞噬RAM,那么最好不要进行交换:当内核检测到RAM不足时,它将杀死最大的内存消耗(apache)。启用交换后,内核将在更晚的时间内终止某些进程,因为交换比RAM慢得多。无需交换-恢复速度更快,停机时间更短。(我只是尝试在具有8GiB RAM的计算机上禁用交换功能,所以使用YMMW。)
chronos

Answers:


5

找出导致问题的原因可能会让人感到痛苦。如果遇到这样的问题,我要做的第一件事就是减少MaxRequestsPerChild到比较低的数字(〜100-200),看看是否有所作为。如果是这样,那么您可能有代码正在某个地方的某个循环中泄漏内存,并且您将需要运行代码审核。

要查看的另一件事是Apache的fullstatus,看看是否可以找出导致内存泄漏的特定请求。获取可疑进程的PID并对其进行跟踪。


谢谢马特。'ps aux | grep apache2'告诉我,在60个活动的进程中,大约有12个正在使用的内存要多于应有的内存(RSS中大于100MB)。我查看了/ proc / <pid> / smaps的输出,发现每一个都只有一个匿名映射,占用了95%以上的空间。我现在正在尝试找出什么以及何时分配此巨大的内存。我将研究strace -感谢您的提示。
ondrej

2

星期五@晚上11点?那对应于备份时间吗?您的系统当时是否有可用于服务进程和备份的I / O?您的趋势软件还会趋势化proc甚至apache记分牌吗,磁盘I / O呢?

我要做的第一件事是计算每个proc需要多少内存,然后为apache中的MaxRequests设置一个合理的限制,以便$ procmem * $ procs不能超过可用内存。我怀疑您的实例需要重新启动,因为OOM可能会(通常)不太富有成效地开展一场女巫狩猎。您需要确保您的设备箱处于限制范围之内,而不是进行交换,当然也不是OOM,以应对这些繁重的时刻。如果您要执行cronjobs,这会比较困难,如果说cronjobs在不确保运行安全的情况下不间断地运行(即,每5分钟的脚本无法检查最后5分钟是否仍在运行),则将非常困难。

现在,您已确保即使万一出错,也无需重新启动设备,一切对您来说都会变得更好。您可以在这段繁忙的时间内登录,并了解使用top,dstat,free -m,iostat等的情况。

Matt的方法可能值得尝试,但仅应用作故障排除的工具,我不建议您采用这种方法,因为这样会使您下次查找时很难发现整个问题。也就是说,它只会真正解决apache /模块的问题,而不会解决代码中的任何问题。我想您会同意,这是很好的机会,这不是apache模块中的某种内存泄漏(假设您使用的是著名的发行版)。


0

要问的第一个问题是通过Apache运行的应用程序是什么?

是您编写的,还是第三方应用程序?

它还引用其他哪些组件/软件包?

您是否正在更新包裹?

您的httpd.conf文件中是否有与性能相关的特定内容?


0

如果您的问题是由PHP应用程序引起的,并且您自己编写了该软件,那么我建议您使用探查器,例如PHP Quick Profiler。如果您正在进行许多数据库事务,那么诸如Kontrollbase之类的软件可以帮助您在那找到问题。


拉斐尔,谢谢。是的,PHP应用程序是我的,它不影响任何SQL数据库。我将给PHP Quick Profiler一炮而红并进行报告。
ondrej
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.