Спустя некоторое время после начала эксплуатации развёрнутого ранее сервера Patchman, мы столкнулись с ошибкой в работе веб-сервера Apache. Было замечено, что через несколько дней после запуска службы веб-сервера, веб-интерфейс сервера Patchman перестал штатным образом отвечать на запросы клиентов. При этом попытки обращения к любым URL веб-сервера в логе /var/log/apache2/error.log стали сопровождаться ошибками "AH03490: scoreboard is full, not at MaxRequestWorkers.Increase ServerLimit". Простой перезапуск юнита службы веб-сервера apache2.service купировал проблему, но примерно через неделю проблема воспроизвелась повторно.
В системе мониторинга на графике потребления оперативной памяти для данного веб-сервера была заметна тенденция постоянного роста потребления оперативной памяти. Однако, не смотря на то, что этот рост потребления не приводил к полной утилизации памяти хоста, ситуация с возникновением ошибки повторялась.
Для получения большей ясности о происходящем с процессами веб-сервера Apache в такой ситуации может оказаться полезен модуль веб-сервера mod_status. В Debian 12 этот модуль активирован в конфигурации по умолчанию, поэтому всё, что потребуется сделать – добавить правило, разрешающее доступ из локальной сети к веб-странице со статусной информацией Apache.
# nano /etc/apache2/mods-enabled/status.conf
...
<Location /server-status>
SetHandler server-status
Require local
Require ip 10.1.5.0/24
</Location>
...
После внесения конфигурационных изменений перезапустим службу веб-сервера:
# systemctl restart apache2.service
Теперь в браузере можно перейти на веб-страницу /server-status и увидеть актуальную информацию о состоянии процессов веб-сервера.
В нашем случае, после перезапуска службы веб-сервера картина выглядела примерно следующим образом:
Далее мы стали наблюдать за изменениями на этой статусной веб-странице, ожидая заменить какую либо аномалию. Например, по тексту возникавшей у нас ошибки веб-сервера, можно найти статью "Apache 2.4 with mpm event freezing up (scoreboard is full) after reload due to bug" с описанием проблемы с накоплением множества соединений в состоянии G (Gracefully finishing), которые можно было наглядно выявить на статусной веб-странице в области Scoreboard. Но время показало, что это не наш случай и у нас подобных соединений со временем не копится. Зато было замечено, что копится количество остановленных процессов веб-сервера (в колонке "Stopping" отображаются как "yes (old gen)").
То есть в нашем случае получается, что при перезапуске службы веб-сервера в системе порождается 2 рабочих процесса apache2 и раз в сутки эти два процесса переходят в остановленное состояние, а к ним прибавляется ещё 2 новых активных процесса с новыми PID. И так остановленные процессы копятся и через 6 суток, наряду с двумя активными процессами, мы имеем дополнительный "чемодан без ручки" в виде 12 остановленных процессов.
С помощью shell-функции мы можем посмотреть то, сколько памяти потребляет каждый из этих процессов. Для этого, например, включим следующий фрагмент кода в конфигурационный файл пользовательской оболочки ~/.profile:
mem()
{
ps -eo rss,pid,euser,args:100 --sort %mem | grep -v grep | grep -i $@ | awk '{printf $1/1024 "MB"; $1=""; print }'
}
Чтобы функция заработала в окружении пользователя, перелогинимся и теперь можем вызывать функцию из командной строки, передав ей в качестве аргумента имя интересующего нас процесса:
# mem apache2
13.7188MB 399866 root /usr/sbin/apache2 -k start
98.7383MB 525516 www-data /usr/sbin/apache2 -k start
99.25MB 476137 www-data /usr/sbin/apache2 -k start
100.191MB 525515 www-data /usr/sbin/apache2 -k start
103.387MB 624604 www-data /usr/sbin/apache2 -k start
107.473MB 426892 www-data /usr/sbin/apache2 -k start
107.73MB 624605 www-data /usr/sbin/apache2 -k start
111.336MB 476136 www-data /usr/sbin/apache2 -k start
111.496MB 673819 www-data /usr/sbin/apache2 -k start
112.18MB 426891 www-data /usr/sbin/apache2 -k start
112.492MB 574835 www-data /usr/sbin/apache2 -k start
116.508MB 574836 www-data /usr/sbin/apache2 -k start
118.242MB 399868 www-data /usr/sbin/apache2 -k start
121.633MB 399867 www-data /usr/sbin/apache2 -k start
122.137MB 673818 www-data /usr/sbin/apache2 -k start
Как видим, в нашем случае каждый из остановленных процессов потребляет в районе 100-120MB ОЗУ и это, в общем-то объясняет ранее показанную на скриншоте "лесенку" потребления памяти на хосте.
В сложившейся ситуации процессы веб-сервера могут копиться до тех пор, пока не будет достигнут лимит на количество процессов, описанный в директиве "Apache HTTP Server Version 2.4 : mpm_common : ServerLimit". В нашем случае, в конфигурации по умолчанию этот лимит составляет 16 процессов. При достижении указанного лимита мы получаем вышеописанную ситуацию с нештатной работой веб-сервера и появлением ошибки "AH03490: scoreboard is full, not at MaxRequestWorkers.Increase ServerLimit".
Но что же приводит к появлению и накоплению остановленных процессов Apache?
Если обратить внимание на лог /var/log/apache2/error.log, то можно заметить, что ошибки появляются в логе в тот момент, когда в полночь происходит ротация логов и в этом моменте имеет место быть факт превышения выше обозначенного лимита в 16 процессов.
[Thu Dec 05 00:00:02.661928 2024] [mpm_event:notice] [pid 472:tid 472] AH00489: Apache/2.4.62 (Debian) mod_auth_gssapi/1.6.4 OpenSSL/3.0.15 mod_wsgi/4.9.4 Python/3.11 configured -- resuming normal operations
[Thu Dec 05 00:00:02.661956 2024] [core:notice] [pid 472:tid 472] AH00094: Command line: '/usr/sbin/apache2'
[Thu Dec 05 00:00:04.664069 2024] [mpm_event:error] [pid 472:tid 472] AH03490: scoreboard is full, not at MaxRequestWorkers.Increase ServerLimit.
[Thu Dec 05 00:00:05.665144 2024] [mpm_event:error] [pid 472:tid 472] AH03490: scoreboard is full, not at MaxRequestWorkers.Increase ServerLimit.
[Thu Dec 05 00:00:06.666239 2024] [mpm_event:error] [pid 472:tid 472] AH03490: scoreboard is full, not at MaxRequestWorkers.Increase ServerLimit.
...
То есть, выполняемая по ночам утилитой logrotate, процедура ротации логов веб-сервера Apache в своей завершающей стадии postrotate (согласно конфигурации /etc/logrotate.d/apache2) инициирует команду "apache2 reload", в результате чего появляются остановленные, но не завершённые корректно процессы apache2, которые впустую расходуют память сервера и приближают веб-сервер к моменту исчерпания лимита ServerLimit.
Можно найти обсуждение "Apache :: scoreboard is full, not at MaxRequestWorkers.Increase Server", где ситуация с некорректным завершением старых процессов apache2 связывается со спецификой работы модуля WSGI. В этом обсуждении в качестве варианта обходного решения проблемы рассматривается отказ от logrotate и использование собственного механизма ротации логов в Apache rotatelogs: "rotatelogs - Piped logging program to rotate Apache logs". В этом случае в конфигурации /etc/logrotate.d/apache2 можно заменить опцию "rotate" на "maxage" и закомментировать в опции "postrotate" строку с командой "invoke-rc.d apache2 reload …". То есть отказаться от перезапуска apache2 при каждой операции ротации логов, чтобы он не плодил старых висящих процессов, оставив при этом только функцию удаления самых старых по времени логов.
Но нам удалось найти ещё один вариант, на котором мы пока и остановились. Упомянутая выше ветка обсуждения навела на мысль о том, что можно посмотреть в сторону конфигурации модуля mod_wsgi. В документации к этому модулю можно найти информацию о том, что при условии использования Python версии 3.9 и выше, иногда при завершении процесса интерпретатора этот самый процесс может зависнуть. И как мы понимаем, это может стать первопричиной невозможности корректного завершения процесса веб-сервера. В таких ситуациях может помочь отключение включенного по умолчанию параметра WSGIDestroyInterpreter.
Для этого изменим конфигурационный файл модуля:
# nano /etc/apache2/mods-enabled/wsgi.conf
В конец основной секции IfModule добавим соответствующий параметр:
<IfModule mod_wsgi.c>
...
#https://modwsgi.readthedocs.io/en/develop/configuration-directives/WSGIDestroyInterpreter.html
WSGIDestroyInterpreter Off
</IfModule>
После внесения конфигурационных изменений перезапустим службу веб-сервера:
# systemctl restart apache2.service
После этого проблема с закрытием процессов apache2 в ходе ежесуточной ротации логов пропала и общая картина по потреблению памяти на сервере Patchman вернулась в нормальное русло.
Добавить комментарий