Httpd进程消耗大量的内存

我有一个服务器,让我头痛。 它拥有几个网站:这些都是基于PHP或基于Java。 我有一个设置,使用Apache2 + suPhp的PHP网站,和apache2 +的mod_proxy + Apache的Tomcat的Java应用程序。

在过去的几个星期里我看到一些奇怪的行为。 有时,我会得到一个单一的httpd进程,达到30-40%cpu,超过70%的内存。 我没有看到任何PHP或Java进程占用额外的资源,所以我(天真地)认为这个问题是不相关的PHP或Java代码。 这些高峰似乎随机发生在随机的时间和随机的时间间隔; 有时每天多次,其他时间整整一周都没有发生。

我注意到的另一个奇怪的事情是,当我手动kill -9正在执行的httpd进程时,另一个httpd进程会在几秒钟内popup,并开始占用大量的内存和CPU。 我可以重复几次,直到它自己停止发生:/

所以,我其实有几个问题:

  1. 有没有人有任何提示,我应该如何追查这种行为的原因? 最终,我想看看是什么样的请求导致这些问题。 我已经看了httpd访问和错误日​​志,我真的找不到任何不寻常的东西。 我对这类问题的研究并不熟悉,所以即使对您来说看起来非常明显的事情也可以提供帮助。
  2. 有没有办法限制一个单一的httpdsubprocess可能消耗的资源量? 或只是简单地杀了它,当它超过了一定的内存量?
  3. 与上一个问题相关,我可以configurationoom-daemon在httpd进程上触发更多,而在其他进程上更less? 我问,因为,直到我find一个体面的解决scheme,我想确保它停止杀死我的java进程时,httpd进程再次开始行动。

UPDATE

最近我发现,造成这个烂摊子的请求来自googlebot。 这是消耗所有可用的mem和cpu的进程的lsof输出摘录:

 httpd 18588 nobody 37u IPv6 96675092 TCP myhost.com:http->crawl-66-249-76-96.googlebot.com:56730 (ESTABLISHED) 

我设置了mod_security来logging来自我的<VirtualHost>中googlebots似乎使用的IP范围的所有来自这个规则的请求:

 SecRule REMOTE_ADDR "@ipMatch 66.249.76.0/24" phase:1,id:1,auditlog,allow 

我这样离开了服务器一段时间。 在这段时间内,httpd进程被刺激了好几次,到了OOM守护进程开始查杀进程(httpd,java,甚至现在不时地closuresmysql)的程度。 然后,我提取了所有被googlebot击中的url,并创build了一个curl所有这些url的脚本,希望能够使httpd进程飙升,从而find导致这些问题的请求。

不幸的是,这并没有发生 – 所有的请求都很快返回,而cpu和内存使用情况远不及Googlebot访问服务器时的情况。

所以我认为有两种可能性:

  1. 问题是由于特定的HTTP标头。 我的脚本不复制这些,它只是使用普通的curl,没有额外的标题。

  2. 导致问题的请求不会被logging。 据我所知,这不应该是这种情况,因为我告诉mod_securitylogging第一阶段,这是在Apache实际上处理请求之前的请求。

有没有人有任何其他的想法?


更新2:

strace输出的过程:

 brk(0x3568c000) = 0x3568c000 brk(0x356ca000) = 0x356ca000 brk(0x35708000) = 0x35708000 brk(0x35746000) = 0x35746000 brk(0x35784000) = 0x35784000 brk(0x357c2000) = 0x357c2000 brk(0x35800000) = 0x35800000 brk(0x3583e000) = 0x3583e000 ... brk(0x3587c000) = 0x3587c000 brk(0x358ba000) = 0x358ba000 brk(0x358f8000) = 0x358f8000 brk(0x35936000) = 0x35936000 brk(0x35974000) = 0x35974000 brk(0x359b2000) = 0x359b2000 brk(0x359f0000) = 0x359f0000 brk(0x35a2e000) = 0x35a2e000 brk(0x35a6c000) = 0x35a6c000 brk(0x35aaa000) = 0x35aaa000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f2028000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f2005000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1fe2000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1fbf000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1f9c000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1f79000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1f56000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1f33000 ... mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1f10000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1eed000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1eca000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1ea7000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1e84000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1e61000 mmap(NULL, 143360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f72f1e3e000 +++ killed by SIGKILL +++ 

提前致谢。

我没有看到任何PHP或Java进程占用额外的资源

你认为这是值得检查的,build议你要么知道很多CGI漏洞,要么对Web服务/进程pipe理知之甚less。

最终,我想看看是什么样的请求导致这些问题

这是一个明智的开始。 最简单的解决scheme是安装mod_security并将其configuration为logging传入的请求(Apache只在发送响应的位置logging日志)。 还有其他一些方法,例如嗅探stream量(pastMon,Wireshark)或login反向代理。

有没有办法限制单个httpdsubprocess可能消耗的资源量

不是直接的,但是您应该将LimitInternalRecursion,LimitRequestBody,LimitRequestFields,LimitRequestFieldSize,LimitRequestLine,MaxKeepAliveRequests,MaxRequestsPerChild和Timeout设置为合理的值。

我可以configurationoom-daemon在httpd进程上更好地触发

把OOM Killer搞砸几乎总是一个坏主意。 即使你认为你知道你在做什么。 你是否在没有web服务器的情况下使用anythiong? 如果是这样,那么也许你应该考虑在单独的机器上运行它们。

首先,你必须限制可以启动的apache进程的最大数目。 这可以通过降低MaxSpareServers来完成。 从低位开始(3-10),直到表演开始下降为止。 MinSpareServers应该是2。

PHP通常作为一个Apache模块(mod_php)运行,这意味着它运行在与Apache相同的地址空间,我的意思是你会看到只有Apache进程,里面也会运行PHP。 你可以在这些进程上运行gdb并在gdb里面运行backtrace ,看看他们在做什么。 你也可以使用pstack 。 如果你注意到了这个URL(请查看access.log查找URL),那么你可以开始debuggingphp代码。 search一个phpdebugging器或/和一个php分析器。

终于find了答案。 结果我们托pipe的网站中有一个隐藏的.htaccess隐藏在其中的一个RewriteRule,在某些情况下触发无限循环。