如何阻止 Apache 导致我的整个服务器崩溃?

如何阻止 Apache 导致我的整个服务器崩溃?

我维护着一台 Gentoo 服务器,上面有一些服务,包括 Apache。它相当低端(2GB RAM 和 2 核的低端 CPU)。我的问题是,尽管我尽了最大努力,但超载的 Apache 还是导致整个服务器崩溃。事实上,此时我几乎确信 Linux 是一个糟糕的操作系统,不值得任何人花时间在负载下寻找稳定性。

我尝试过的事情:

  1. 调整根 Apache 进程(以及其所有子进程)的 oom_adj。这几乎没有效果。当 Apache 过载时,它会使系统陷入困境,因为系统在杀死任何进程之前会分页出所有其他进程。
  2. 关闭交换。没有帮助,它会卸载分页到进程的二进制文件和 / 上的其他文件的内存,从而导致相同的效果。
  3. 将其放入内存受限的 cgroup(限制为 512 MB RAM,占总内存的 1/4)。这“有效”,至少在我自己的压力测试中是如此 - 除了服务器在负载下不断崩溃(基本上停滞了所有其他进程,无法通过 SSH 访问等)。
  4. 以空闲 I/O 优先级运行它。这最终不是一个好主意,因为它只会导致系统负载无限增加(达到数千),几乎没有任何明显的效果 - 直到您尝试访问磁盘的未缓冲部分。这导致任务冻结。(良好的 I/O 调度就是这样,是吗?)
  5. 限制与 Apache 的并发连接数。设置的数字太低会导致网站无响应,因为大多数插槽都被长请求(文件下载)占用。
  6. 我尝试了各种 Apache MPM,但没有取得太大成功(prefork、event、itk)。
  7. 从 prefork/event+php-cgi+suphp 切换到 itk+mod_php。这提高了性能,但并没有解决实际问题。
  8. 切换 I/O 调度程序(cfq 到 deadline)。

强调一下:我不在乎 Apache 本身是否因负载过大而崩溃,我只希望系统的其余部分保持稳定。当然,如果 Apache 在短暂的密集负载后能够快速恢复,那就太好了,但要一步一步来。

现在我最困惑的是,在当今这个时代,人类怎么能设计出这样一种操作系统,而这样一个看似简单的任务(不允许一个系统组件导致整个系统崩溃)却似乎几乎不可能完成 - 或者至少很难做到。

请不要建议诸如虚拟机或“购买更多 RAM”之类的事情。


在朋友的帮助下收集到更多信息:调用 cgroup oom killer 时,进程会挂起。以下是调用跟踪:

[<ffffffff8104b94b>]?准备等待+0x70/0x7b
[<ffffffff810a9c73>] mem_cgroup_handle_oom+0xdf/0x180
memcg_oom_wake_function+0x0/0x6d 复制代码
[<ffffffff810aa041>] __mem_cgroup_try_charge+0x32d/0x478
[<ffffffff810aac67>] mem_cgroup_charge_common+0x48/0x73
__lru_cache_add+0x60/0x62 复制代码
[<ffffffff810aadc3>] mem_cgroup_newpage_charge+0x3b/0x4a
[<ffffffff8108ec38>] handle_mm_fault+0x305/0x8cf
[<ffffffff813c6276>]?计划+0x6ae/0x6fb
[<ffffffff8101f568>] do_page_fault+0x214/0x22b
[<ffffffff813c7e1f>] 页面错误+0x1f/0x30

此时,apache 内存 cgroup 实际上已死锁,并在系统调用中消耗 CPU(所有调用跟踪均显示上述情况)。这似乎是 cgroup 实现中的问题...

答案1

我不想这么说,但你似乎问错了问题。

这并不是要阻止 Apache 搞垮你的服务器,而是要让你的 Web 服务器每秒处理更多的查询 - 足够多,这样你就不会遇到问题。重新定义问题的部分答案是限制 Apache,这样它就不会在高负载下崩溃。

对于第二部分,Apache 有一些你可以设置的限制 -最大客户数是一个重要的配置。这限制了允许运行的子进程数。如果您可以为长时间运行的进程(例如下载大型文件)减轻 Apache 的负担,那么 Apache 中就有另一个可以为 PHP 提供服务的插槽。如果文件下载必须由 PHP 层验证,它们仍然可以这样做,并将静态内容传递回更优化的 Web 服务器,例如NginX 发送文件

同时,在每次单独请求时都分叉 Apache,以最慢的方式运行 PHP - 作为 CGI(无论您使用什么 Apache MPM) - 也会让机器花费大量时间不运行您的代码。mod_php 的优化程度明显更高。

当 Apache 和 PHP 层得到适当优化时,PHP 可以承载大量流量。例如,昨天,即 2010 年 12 月 11 日,我运行的一对 PHP 服务器在 24 小时内的点击量接近 1900 万次,其中大部分发生在上午 7 点至晚上 8 点的时间段。

这里还有很多其他问题,其他地方还有关于优化 Apache 和 PHP 的文章,我认为您需要先阅读它们,然后再指责 Linux/Apache 和 PHP。

答案2

当你处理生产 Apache 服务器时,你必须具有平均进程大小,尤其是 php,我建议您:

  • 检查您的进程平均内存消耗
  • 调整MaxClients为 AVERAGE_MEMORY / RAM_DEDICATED_TO_APACHE

其中 RAM_DEDICATED_TO_APACHE 必须是 TOTAL_RAM 的另一个估计值减去机器其余部分需要的 RAM(如果您在同一台机器上运行数据库,请慷慨地提供数据库)。

我真的建议你使用,您可以轻松地在保存机器的不同端口上运行 2 个服务器,并将静态文件路由到专用文件(媒体)服务器(lighthttpd、nginx)或带有 worker 且没有额外模块的 apache 实例。当然,使用 varnish 捕获静态内容。

分割负载非常重要,因为如果不这样做,您将使用相同数量的 RAM 来传送任何静态文件(需要少于 1MB)。

如果您确实需要确保永远不会耗尽所有内存,您可以安装一个每 2 分钟运行一次的新 cronjob(您可以考虑更多或更少),使用以下行,将 50最低内存调整为任意数量,并将该数字保持在 30 以上至少;您需要一些内存来停止服务器。

vmstat -S M | tail -n 1 | awk 'BEGIN{ "date" | getline date }{if($4 + $6 < 50){ system("/etc/init.d/httpd stop"); system("/etc/init.d/httpd start"); print "Rebooting apache  on " date >> "/var/log/apache-reboots.log"}}'

这是一种限制内存的非常肮脏的方法,但当你不太确定每个 apache 进程的平均内存时,这种方法会非常有用,如果你在日志文件(“/var/log/apache-reboots.log”)中看到几次重新启动,那么你应该调整你的 apache MaxClientsMaxRequestsPerChildThreadsPerChild 避免将来硬重启,随着时间和调整,你将获得服务器的精确配置。

答案3

您可以尝试以下几种常规方法:

  • 从您的描述很难判断 Apache/Linux 是真的崩溃了还是只是严重超载了。我怀疑您的服务器负载过高,唯一有效的措施就是关闭电源。除非有实际崩溃的具体证据,否则我会将问题视为服务器超载。如果您优化了服务器的性能,但它仍然崩溃,那么您可以努力找到并解决该问题。
  • 通常,您不会希望服务器处于经常使用交换区的状态,尤其是任何 Apache 实例。您可能会很快陷入负载失控的情况,即服务器运行良好,但当流量增加几个百分点时,它就会开始使用交换区,负载猛增,导致网站变慢或无法访问。为了防止 Apache 使用交换区,请减少最大客户端/连接数和/或通过禁用任何不需要的模块来减少内存使用。另请参阅下一点。
  • 您提到 Apache 中的连接正在被文件下载等长请求占用。为了帮助减少此问题,您可以使用第二个 Web 服务器(如 lighttp)设置来仅提供静态内容,Apache 将请求转发/重定向到该服务器。这可以释放 Apache 中的连接来执行繁重的工作,并让您减少最大客户端/连接数。
  • 如果您需要防止 DoS 攻击(无论是故意还是意外),您可以安装和设置各种 Apache 模块。例如,我使用了 mod_evasive 和 mod_limitipconn,它们足以防止恶意程度较低的 DoS 攻击。
  • 不要忽视对 Apache 或操作系统或应用程序的其他部分的优化。计算机非常擅长按照您的指令运行,因此如果您的 Apache 设置显示“使用超过此服务器的资源”,那么它就会这样做。与许多软件一样,Apache 旨在与大量硬件和应用程序配合使用,但需要为两者正确设置。默认配置仅适用于简单、低流量的网站。
  • 只需进行一些调整,您就应该能够找到一个平衡点,让服务器负载高但仍然足够灵敏,可以登录并检查。此时,您的选择要么是分析和优化应用程序,包括添加缓存层,要么是获得更好的硬件。此步骤应在正确设置 Apache 之后进行。

答案4

我发现问题了...

为整个内存受限的 cgroup设置oom_adj为 15 是非常愚蠢的做法。cgroup 中所有进程的调整分数最终都为 1000 - 因此当 cgroup 内存不足时,系统会终止随机进程并通常出现异常行为。

只需删除设置的行即可,我的系统并未崩溃oom_adj

相关内容