Httpd 进程消耗大量内存

Httpd 进程消耗大量内存

我有一台让我头疼的服务器。它托管了几个网站:这些网站要么基于 php,要么基于 java。我的设置是使用 apache2 + suPhp 来管理 php 网站,使用 apache2 + mod_proxy + apache tomcat 来管理 java 应用程序。

过去几周,我发现了一些奇怪的行为。有时,我发现一个 httpd 进程的 CPU 占用率会达到 30-40%,内存占用率会超过 70%。我没有看到任何 php 或 java 进程占用额外的资源,所以我(天真地)认为问题与 php 或 java 代码无关。这些峰值似乎发生在随机时间,并且间隔随机;有时一天发生多次,有时一整周都没有发生任何情况。

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

所以,我实际上有几个问题:

  1. 有人能告诉我如何追踪这种行为的原因吗?最终,我想看看是哪种请求导致了这些问题。我查看了 httpd 访问和错误日​​志,但并没有发现任何异常。我对研究这种类型的问题并不熟悉,所以即使是对你来说可能非常明显的事情也可能有所帮助。
  2. 有没有办法限制单个 httpd 子进程可能消耗的资源量?或者当它超过一定内存量时直接将其终止?
  3. 与上一个问题相关,我是否可以将 oom-daemon 配置为对 httpd 进程更敏感,而对其他进程的敏感度更低?我之所以问这个问题,是因为在找到合适的解决方案之前,我想确保当 httpd 进程再次开始出现故障时,它不会终止我的 java 进程。

更新

lsof最近我发现导致此问题出现的请求来自 googlebot。以下是消耗所有可用内存和 CPU 的进程的输出摘录:

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

我设置了 mod_security 来记录来自 googlebots 似乎使用此规则的 IP 范围的所有请求<VirtualHost>

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

我让服务器保持这种状态一段时间。在此期间,httpd 进程多次出现峰值,甚至 OOM 守护进程开始终止进程​​(httpd、java,现在它甚至不时关闭 mysql)。然后我提取了所有被 googlebot 命中的 URL,并创建了一个小脚本来卷曲所有这些 URL,希望我可以让 httpd 进程出现峰值,从而找到导致这些问题的请求。

不幸的是,这并没有发生——所有的请求都很快返回,并且 CPU 和内存使用率远不及 googlebot 访问服务器时的水平。

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

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

  2. 导致问题的请求没有被记录。据我所知,情况不应该如此,因为我告诉 mod_security 在第 1 阶段记录请求,即在 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 +++

提前致谢。

答案1

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

您认为这完全值得检查,这表明您要么对 CGI 漏洞了解很多,要么对 Web 服务/流程管理了解甚少。

最后,我想看看是哪种请求导致了这些问题

这是一个明智的起点。最简单的解决方案是安装 mod_security 并将其配置为记录传入请求(Apache 仅在响应分派时记录)。还有其他方法,例如嗅探流量(pastMon、Wireshark)或在反向代理上记录。

有没有办法限制单个 httpd 子进程可能消耗的资源量

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

我是否可以将 oom-daemon 配置为对 httpd 进程更具触发性

搞乱 OOM Killer 几乎总是一个坏主意。即使您认为自己知道自己在做什么。在没有网络服务器的情况下,您的 Java 能做任何有用的事情吗?如果是,那么也许您应该考虑在单独的机器上运行它们。

答案2

首先:您必须限制可以启动的 Apache 进程的最大数量。这可以通过降低来实现MaxSpareServers。从低开始(3-10,然后稍微增加一点,直到性能开始下降)。MinSpareServers应该是 2。

PHP 通常作为 apache 模块 (mod_php) 运行,这意味着它与 apache 运行在相同的地址空间中,我的意思是您将只看到 apache 进程,并且内部也会运行 php。您可以gdb在这些进程上运行并backtrace在内部运行gdb以查看它们在做什么。您也可以使用pstack它。如果您注意到哪个 URL 发生了这种情况(检查 access.log 以找出 URL),那么您可以开始调试 php 代码。搜索 php 调试器或/和 php 分析器。

答案3

终于找到答案了。原来我们托管的一个网站有一个可疑的 .htaccess 隐藏在某个地方,里面有一个 RewriteRule,在某些情况下会触发无限循环。

相关内容