我有一台运行 Apache2 2.4.38 的 Debian 10 服务器。最近,我替换了所有已配置的 HTTPS vhosts 使用的 SSL 证书文件并运行了systemctl reload apache2.service
,它/usr/sbin/apachectl graceful
通过 systemd 单元文件运行。
根据 Apache 2 文档,
USR1 或优雅信号会导致父进程建议子进程在当前请求之后退出(如果子进程没有提供任何服务,则立即退出)。父进程重新读取其配置文件并重新打开其日志文件。当每个子进程死亡时,父进程会用新一代配置中的子进程替换它,该子进程会立即开始处理新请求。
在繁忙的服务器上,替换子进程的延迟是可以理解的。但是,今天我注意到,服务器很少会使用替换前的旧 SSL 证书进行响应。我去查看了这些进程:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 13559 0.0 0.2 15640 10356 ? Ss 2022 18:53 /usr/sbin/apache2 -k start
www-data 16834 0.7 0.6 1232452 27780 ? Sl 06:00 3:47 /usr/sbin/apache2 -k start
www-data 17415 0.9 0.6 1231844 26532 ? Sl 10:22 2:32 /usr/sbin/apache2 -k start
www-data 17552 0.7 0.6 1231736 26376 ? Sl 10:53 1:47 /usr/sbin/apache2 -k start
www-data 17612 0.6 0.6 1232000 26840 ? Sl 10:54 1:34 /usr/sbin/apache2 -k start
www-data 17641 0.6 0.5 1231980 22732 ? Sl 10:54 1:36 /usr/sbin/apache2 -k start
www-data 17642 0.8 0.6 1231848 24728 ? Sl 10:54 1:59 /usr/sbin/apache2 -k start
www-data 26704 0.5 0.6 1232216 24748 ? Sl Jan18 89:53 /usr/sbin/apache2 -k start
START
列表中的最后一个进程在和列上显得格格不入TIME
。我于 1 月 24 日运行了重新加载命令,但现在,六天过去了,这个进程仍在运行。不过,服务器对请求的响应很好——尚不清楚这一个工作进程是否真的在处理新请求,但其他工作进程都在处理。
服务器配置非常简单,并且默认设置足够,因此不在此处列出(目前 - 如果您需要任何特定信息,请询问)。它唯一有趣的功能是它正在运行 mod_jk,即各种VirtualHost
指令JkMount /* workername
(workername
在 /etc/libapache2-mod-jk/workers.properties 中定义)。mod_jk 用于ajp13
连接到运行 Tomcat 的两个负载平衡应用程序服务器之一。
这不是我们第一次遇到 Apache2 工作进程卡住的情况,但我一直无法确定它最终卡住的原因。这可能与 mod_jk 和(非常)传统的 Java 应用程序有关,可能存在一些请求,导致 Java/mod_jk 级别出现罕见的边缘情况错误,并阻止工作进程通过 USR1 信号退出。Java 应用程序日志不在我的控制范围内;它们非常冗长,包含无意义的信息,通常缺少时间戳,并且对于故障排除目的几乎毫无用处,除非您恰好在错误发生的确切时刻查看它们。
我必须进行非正常重启,尽管这会导致轻微的生产中断,但我对将来调试此问题的进一步方法很感兴趣,以便我们可以让工作程序在需要时始终可靠地执行正常重启。我们如何才能更好地分析是什么导致工作程序卡住?
结果apachectl -V
:
Server version: Apache/2.4.38 (Debian)
Server built: 2021-12-21T16:50:43
Server's Module Magic Number: 20120211:84
Server loaded: APR 1.6.5, APR-UTIL 1.6.1
Compiled using: APR 1.6.5, APR-UTIL 1.6.1
Architecture: 64-bit
Server MPM: event
threaded: yes (fixed thread count)
forked: yes (variable process count)
Server compiled with....
-D APR_HAS_SENDFILE
-D APR_HAS_MMAP
-D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
-D APR_USE_SYSVSEM_SERIALIZE
-D APR_USE_PTHREAD_SERIALIZE
-D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
-D APR_HAS_OTHER_CHILD
-D AP_HAVE_RELIABLE_PIPED_LOGS
-D DYNAMIC_MODULE_LIMIT=256
-D HTTPD_ROOT="/etc/apache2"
-D SUEXEC_BIN="/usr/lib/apache2/suexec"
-D DEFAULT_PIDLOG="/var/run/apache2.pid"
-D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
-D DEFAULT_ERRORLOG="logs/error_log"
-D AP_TYPES_CONFIG_FILE="mime.types"
-D SERVER_CONFIG_FILE="apache2.conf"
apachectl -S
删除生产子域名:
VirtualHost configuration:
*:443 is a NameVirtualHost
default server vhost1.domain.example (/etc/apache2/sites-enabled/00_vhost1.domain.example-ssl.conf:2)
port 443 namevhost vhost1.domain.example (/etc/apache2/sites-enabled/00_vhost1.domain.example-ssl.conf:2)
port 443 namevhost vhost2.domain.example (/etc/apache2/sites-enabled/01_vhost2.domain.example-ssl.conf:2)
port 443 namevhost vhost3.domain.example (/etc/apache2/sites-enabled/02_vhost3.domain.example-ssl.conf:2)
port 443 namevhost vhost4.domain.example (/etc/apache2/sites-enabled/03_vhost4.domain.example-ssl.conf:2)
alias alias1.domain.example
*:80 is a NameVirtualHost
default server vhost1.domain.example (/etc/apache2/sites-enabled/00_vhost1.domain.example.conf:1)
port 80 namevhost vhost1.domain.example (/etc/apache2/sites-enabled/00_vhost1.domain.example.conf:1)
port 80 namevhost vhost2.domain.example (/etc/apache2/sites-enabled/01_vhost2.domain.example.conf:1)
alias 172.16.33.63
port 80 namevhost vhost3.domain.example (/etc/apache2/sites-enabled/02_vhost3.domain.example.conf:1)
port 80 namevhost vhost4.domain.example (/etc/apache2/sites-enabled/03_vhost4.domain.example.conf:1)
alias alias1.domain.example
ServerRoot: "/etc/apache2"
Main DocumentRoot: "/var/www/html"
Main ErrorLog: "/var/log/apache2/error.log"
Mutex default: dir="/var/run/apache2/" mechanism=default
Mutex watchdog-callback: using_defaults
Mutex rewrite-map: using_defaults
Mutex ssl-stapling-refresh: using_defaults
Mutex ssl-stapling: using_defaults
Mutex proxy: using_defaults
Mutex ssl-cache: using_defaults
PidFile: "/var/run/apache2/apache2.pid"
Define: DUMP_VHOSTS
Define: DUMP_RUN_CFG
User: name="www-data" id=33
Group: name="www-data" id=33
答案1
第三方模块存在一些问题,不允许 httpd 正常重启子进程,甚至导致服务器无响应。
我在使用 mod_weblogic (mod_wl_24.so) 时也遇到了类似的问题,其他人也报告了 mod_security 也遇到了类似的问题。Apache HTTPD 开发人员曾尝试在第三方模块中解决这些所谓的错误行为。
例如 2.4.4X 对此有几处修复。我会尝试升级到至少 2.4.49 及以上版本,然后再试一次。检查https://downloads.apache.org/httpd/CHANGES_2.4并寻找“mpm_event”。
In 2.4.49
*) mpm_event: Fix children processes possibly not stopped on graceful
restart. PR 63169. [Joel Self <joelself gmail.com>]
*) mpm_event: Fix graceful stop/restart of children processes if connections
are in lingering close for too long. [Yann Ylavic]
In 2.4.47
*) mpm_event: Don't reset connections after lingering close, restoring prior
to 2.4.28 behaviour. [Yann Ylavic]
如果您不能立即升级,使用 mod_proxy_ajp 也可能会获得更好的结果。
简而言之,答案是:如果您使用第三方模块,请尝试将版本升级到至少 2.4.49。
编辑:
之前我忘了提到一种解决方法,即“坚持下去”,直到您可以升级或更改模块,即强制 httpd 永远不要优雅地重启任何子进程。怎么做?
使 MaxSpareThreads 具有与 MaxRequestWorkers 相同的值。并且 MaxConnectionsPerChild 为 0 以允许无限连接,这样 httpd 就不会尝试正常关闭它们。
例子:
MaxSpareThreads 500
MaxRequestWorkers 500
MaxConnectionsPerChild 0
并且永远不要发出平稳重启(某些发行版称之为重新加载),只需重新启动或完全停止/启动。