apache2.4 debian 9 上的随机 CONNECTION_RESET

apache2.4 debian 9 上的随机 CONNECTION_RESET

我的服务器出现了一些奇怪的行为,我就是找不到原因。我到处找了。

我将向能够解决这个问题的人支付价值 200 美元的比特币。

问题:

当从 apache 请求任何资源(页面、图像、css、js)时,有时需要很长时间才能响应。大约一半的时间,连接会被重置。(在 Chrome 上:net::ERR_CONNECTION_RESET)这种情况很少发生,随机发生,并且绝对不可预测。更令人困惑的是,虽然一个请求似乎挂起了,但我可以发出其他完美运行的请求。

关于服务器:

我在 Debian 9 上运行 apache2.4 mpm-prefork 和 php7.0。apache 模块使用 mod_rewrite 和来自 certbot 的 ssl 证书。在某些情况下,php 会调用 inkscape 将 svgs 渲染为 png。

服务器负载非常低 (0.02) 并且只运行 apache。

检查内容:

  • 检查了所有服务器日志。(系统日志、apache 日志)- 没有
  • 增加了 apache mpm-prefork 限制 - 不行
  • 检查了可能的 DNS 问题 - 没有结果
  • 我甚至搬到了一个全新的根服务器(在不同的提供商上)——情况仍然一样

我继续用Wireshark分析tcp流量,发现一些可疑行为。当连接冻结时,会出现一些TCP无序、重传和ACKed未见分段数据包...但我没有必要的底层知识来判断发生了什么。

任何提示都是大大感谢!

编辑:

这是 mpm_prefork 配置:

<IfModule mpm_prefork_module>
    StartServers            10
    MinSpareServers         10
    MaxSpareServers         50
    MaxRequestWorkers       300
    MaxConnectionsPerChild  0
</IfModule>

编辑编辑:

当这种情况再次发生时,我很幸运,在服务器和客户端上都运行了 tcp 嗅探器。以下是 pcap 文件,截取到最后约 30 秒。

服务器端pcap

客户端.pcap

如果有知识的人可以快速看一眼并告诉我发生了什么,我会很高兴。

编辑编辑编辑:

我设法使错误可重现,至少在 KeepAlive 开启的情况下。当请求完成且内容已提供时,tcp 连接将在 5 秒后以 FIN-ACK 关闭。在 FIN-ACK 之后 5-12 秒的时间窗口内发出另一个请求时,连接将冻结。

然而,关闭 KeepAlive 后,这种情况不再发生,尽管同时加载多个资源时错误发生得更频繁。但之后就无法重现了。

答案1

我会检查服务器和客户端之间传输的 TCP 数据包的大小。如果数据包大小接近 1500,则有可能因为多种原因被丢弃:

  1. 如果数据包上设置了 DNF 位,并且数据包在某处被分割,则这可能是导致数据包被丢弃的问题

  2. 如果 MTU 设置为 1500,并且数据包正在通过隧道、加密等,这会导致数据包中添加额外的标头,那么这也会导致数据包丢失。尝试将您正在使用的接口两端的 mtu 设置为低于 1500 的值,可能是 1420 甚至更低。

答案2

非常确定我发现了这个问题:-)因为我刚刚遇到了同样的事情。

1. 原因

我认为你有或更多服务于端口 80(或 443,如果是 SSL 连接)的进程。您可以按如下方式检查,此处使用端口 80 的命令以及我出现问题的系统的输出:

# netstat -tupan | grep ":80.*LISTEN"

Proto Recv-Q Send-Q Local    Foreign  State   PID/Program name
                    Address  Address
tcp6       0      0 :::80    :::*     LISTEN  22718/apache2
tcp6       0      0 :::80    :::*     LISTEN  1794/apache2

两个进程通过同一个端口为同一个 IP 地址提供服务确实是有可能的,可以使用端口选项SO_REUSEADDRSO_REUSEPORT请参见这里这里(关于“Linux >= 3.9”的部分)。

内核所做的SO_REUSEPORT就是以非确定性的方式将传入的 TCP 连接分发给为该端口提供服务的进程。一个进程是正确处理请求的 Apache,另一个进程是“其他”进程,它永远不会回答任何问题。在我的例子中,它是另一个 Apache2 进程。

2.解决方案

  1. 如果您有两个 Apache 进程,首先找出其中哪个是“僵尸”。为此,请停止常规 Apache 服务器 ( service apache2 stop) 并检查哪个进程仍存在 ( netstat -tupan | grep ":80.*LISTEN")。这就是“僵尸”。记下它的 PID。

  2. 要了解有关谁或什么启动了这个“僵尸”进程的更多信息:

    • cat /proc/<pid>/loginuid使用该“僵尸”进程的 PID执行。如果显示4294967295,则表示是由系统而不是用户启动的(原因)。否则,它是您可以查找的用户的 UID。

    • 执行ps auxf并确定“僵尸”进程的进程启动时间。如果它与系统启动时间相匹配,则意味着该进程在启动时以某种方式启动。

  3. 为了(也许)了解有关此“僵尸”进程内部发生的情况的更多信息,您可以使用 附加到它strace。这将创建大量难以阅读的日志,但由于重现此“僵尸”进程的问题可能并不容易,因此在我们终止该进程之前至少收集其中一些日志(尤其是发送到该进程的 HTTP 请求)似乎不错。您将使用进程的 PID 执行,而不是$PID

    strace -o strace.log -f -p $PID
    
  4. 为了暂时解决该问题,请终止“僵尸”进程,并为 提供其 PID $PIDkill $PID或者如果需要kill -9 $PID

  5. 检查该“僵尸”进程在重新启动后是否再次启动并运行,如果是,则必须调查并修复导致

3. 重现原因

手动创建一个 Apache2 “僵尸”进程是可能的(但并非易事),它将与常规 Apache 服务器并行运行,并且“不回答任何问题”。以下是几乎但不完全完整的说明:

  1. 创建相关配置文件的副本:

    cp /etc/apache2/envvars /etc/apache2/envvars-zombie
    cp /etc/apache2/apache2.conf /etc/apache2/apache2-zombie.conf
    
  2. 编辑/etc/apache2/envvars-zombie脚本开头的并静态设置SUFFIX="-zombie",覆盖其中的条件赋值。

  3. 编辑/etc/apache2/apache2-zombie.conf并阻止包含任何 VirtualHost 配置文件。就我而言,我会将相应的行修改为:

    # IncludeOptional sites-enabled/
    
  4. 确保apache2-zombie.conf文件中包含默认监听端口。在我的例子中,这已通过 实现Include ports.conf

  5. 创建 Apache2 新实例所需的锁文件和日志目录,并使新 Apache2 运行时用户可以访问它们:

    mkdir /var/log/apache2-zombie
    chown www-data /var/log/apache2-zombie/
    
    mkdir /var/lock/apache2-zombie
    chown www-data /var/lock/apache2-zombie/
    
  6. 现在您应该能够按如下方式启动“僵尸”Apache 进程:

    cd /etc/apache2/
    source envvars-zombie
    /usr/sbin/apache2 -f apache2-zombie.conf -k start
    
  7. 确认现在确实有第二个进程在 Apache2 标准端口上运行:netstat -tupan | grep ":80.*LISTEN"

  8. 第二台 Apache2 服务器还不是“僵尸”,因为它仍然会回答“404 Not Found”或(因为我们没有设置 SSL)在端口 443 上发出请求时导致 SSL 错误。但您已经可以观察到以下效果:一些请求以不确定的方式发送到这个新服务器并导致这些错误。(我在实践中已经达到了这一点……)

  9. 要创建“合适的”僵尸 Apache,请设置一个简单的脚本,该脚本将接受 HTTP 请求,然后sleep()在几分钟内不执行任何操作( ),以让浏览器放弃,让 TCP 连接超时。为 Apache 默认主机安装它。这样,它将用于对端口的所有 HTTP 请求,因为我们禁用了所有 VirtualHost 配置,因此 Apache 无法为任何请求找到更合适的主机,并将选择默认主机。

相关内容