我在 Django 中编写了一个 web 应用程序,并使用 mod_wsgi 在 Apache 2.4 下托管。
乍一看,它运行正常。但是,当 Apache 进程运行一段时间后,Web 应用程序开始响应 503 错误。有时,多次按 F5 后,您可以顺利通过。
恼人的事情是我的 Apache error.log 中根本没有与此相关的任何内容,所以我不知道从哪里开始调试。我唯一的线索是浏览器中显示的错误:503 Service Unavailable
。
其他网站(PHP)不受影响。重新启动 Apache 服务器可以立即可靠地解决问题。
该 Web 应用程序的 Apache 配置如下:
WSGIDaemonProcess app python-path=/opt/app home=/opt/app
WSGIProcessGroup app
WSGIScriptAlias /app /opt/app/wsgi.py
我已经发现这但我不认为这是问题所在,因为在我的情况下套接字已经放置在其中/var/run/apache2/
(而不是“apache 日志目录”)。此目录可供其他人读取。套接字本身具有模式700
,但具有正确的所有者(www-data
)。
有任何想法吗?
编辑:
我注意到,当问题发生并重新启动 Apache 来修复它时,Apache 需要很长时间才能退出。在错误日志中,我发现了这些消息:
[Sun Jun 25 14:14:18.935566 2017] [core:warn] [pid 374:tid 140587055682752] AH00045: child process 21761 still did not exit, sending a SIGTERM
[Sun Jun 25 14:14:18.935637 2017] [core:warn] [pid 374:tid 140587055682752] AH00045: child process 21812 still did not exit, sending a SIGTERM
[Sun Jun 25 14:14:20.937578 2017] [core:warn] [pid 374:tid 140587055682752] AH00045: child process 21761 still did not exit, sending a SIGTERM
[Sun Jun 25 14:14:20.937640 2017] [core:warn] [pid 374:tid 140587055682752] AH00045: child process 21812 still did not exit, sending a SIGTERM
[Sun Jun 25 14:14:22.939827 2017] [core:warn] [pid 374:tid 140587055682752] AH00045: child process 21761 still did not exit, sending a SIGTERM
[Sun Jun 25 14:14:22.939893 2017] [core:warn] [pid 374:tid 140587055682752] AH00045: child process 21812 still did not exit, sending a SIGTERM
[Sun Jun 25 14:14:24.942034 2017] [core:error] [pid 374:tid 140587055682752] AH00046: child process 21761 still did not exit, sending a SIGKILL
[Sun Jun 25 14:14:24.942176 2017] [core:error] [pid 374:tid 140587055682752] AH00046: child process 21812 still did not exit, sending a SIGKILL
因此,从表面上看,问题可能是由挂起的 (WSGI?) 线程引起的?我该如何继续调试这个问题?
答案1
这已经晚了 5 年了,但我可能有一些见解可以分享:
当前的问题是 Apache 似乎没有记录 503。我发现 Apache 正在记录 503,但试图将其记录到已删除的文件。当 Apache 服务器进程启动时,它会获取要写入日志的文件的文件句柄。重要的是,它不会更新该文件句柄;预期是,如果文件被移动/删除,服务器将重新启动(如文档)。
一种常见的设置是使用 Linux 的 logrotate 命令每晚自动轮换 Apache 访问 + 错误日志。作为此设置的一部分,配置脚本会正常重启 Apache 服务器,以便新服务器进程写入新日志文件。但是,如果出于某种原因,正常重启无法终止并重新启动某些服务器进程,则这些进程仍将写入旧日志文件,而旧日志文件通常会在一天后自动删除。因此,如果您在发生这种情况后查看此问题,则不会看到 503!它们被写入旧的、可能不存在的日志文件。
为什么正常重启无法终止某些服务器进程?我不确定,但我猜测这是因为 Apache 2.4 中存在此错误:https://bz.apache.org/bugzilla/show_bug.cgi?id=63169
除了日志记录问题之外,据我所知,这是 503 错误背后的真正问题。有一个进程卡在旧一代服务器上,如果负载足够高,则对该进程的任何请求都将出现 503 错误,因为与 mod_wsgi 的某些交互我不理解或不关心。
根据所有这些信息可以采取的一些潜在行动:
- 如果可以的话,请尝试更新到 Apache 2.49。在我的环境中,这不是一个选项,所以我不确定这是否有帮助。
- 考虑使用 Apache 的 Piped Logs,这是写入日志文件的替代方法,不需要重新启动服务器。文档这里。我不认为它会修复 503 错误,但至少日志记录可以正常工作。
- 考虑增加 mpm_event.conf(或 mpm_worker.conf)中的 MinSpareThreads 值。如果我链接到的 Apache 错误确实是问题所在,那么问题在于流量激增会产生过多进程,而 Apache 在正常重启时会忘记它们。据说,仅此解决方案似乎就解决了我的问题,但才过了几天。
以下一些信号表明您可能遇到了与我描述的相同的问题:
- 如果您运行
lsof | grep /var/log/apache2
(或无论您的日志在哪里),将至少有一个进程具有指向(已删除的)日志文件的打开文件句柄。 - Apache 的服务器状态中有一个服务器进程被标记为(旧代),但未标记为停止。
- 如果您对 Apache 进行硬重启,问题应该会得到解决,至少在下一次 logrotate 运行之前。