Настройка MPM Prefork в Apache для Bitrix

В одном проекте на Битрикс возникла необходимость потюнить настройки Prefork для нагрузки 2 тыс. пользователей и более. Во всех стандартных инсталляциях битрикс-окружение автоматически через bvat выполняет расчёт значений для prefork на основе объема ОЗУ сервера, и обычно я никогда не пытался сконфигурировать эти значения на какие-то свои, отличные от стоковых, т.к. проблем не возникало.

В данном же проекте используется кастомное окружение в докере, а потому изначально планировалось просто взять конфиг из bitrix-env и по аналогии запаковать в docker-образ с httpd. Но тащить за собой bvat в контейнер не хотелось и чтобы не усложнять, я обратился к гуглу и документации, чтобы понять, какие же оптимальные настройки я могу выставить для prefork.

Как работает MPM

Чтобы заниматься тюнингом, надо хотя бы базово понимать, с чем приходится иметь дело. MPM – мульти-процессорный модуль, используемый Apache для одновременной обработки множества запросов.

Запускается единый родительский процесс, который управляет дочерними форками. Они-то и обслуживают соединения. А чтобы клиентам не пришлось ждать, пока форкнется новый дочерний процесс при достижении их лимита, существует некий резерв процессов, которые в состоянии idle, т.е. простаивают без дела и ждут клиентов, которым не хватило имеющихся процессов. Т.е. на каждый клиентский запрос будет создаваться свой процесс в apache и при этом все процессы будут изолированы друг от друга, что вполне себе безопасно.

Родительский процесс обычно запускается от имени root, чтобы иметь возможность запускаться на привилегированных портах и обычно это 80 порт. А вот дочерние процессы уже запускаются от непривилегированного пользователя, который обычно указывается в директивах User и Group конфигурационного файла.

Параметры StartServers, MinSpareServers, MaxSpareServers и MaxRequestWorkers (MaxClients в 2.2 версии) регулируют, как родительский процесс создает дочерние для обслуживания запросов. А MaxConnectionsPerChild контролирует, как часто сервер перезапускает процессы, убивая старые и запуская новые.

StartServers – кол-во процессов, которые запускаются при старте сервера. Например, если StartServers равен 5, а кол-во клиентов 100, то каждую секунду будет создаваться экспоненциальное кол-во дочерних процессов, пока не достигнет нужного кол-ва для обслуживания. Т.е. порождается 1 процесс, таймаут в одну секунду, порождается 2 процесса, снова таймаут секунда, и порождается уже 4 процесса и т.д. Форки процессов вызывают некие задержки и нужно стараться не допускать ситуаций, когда процессов не хватает.

MinSpareServers, MaxSpareServers – кол-во тех самых процессов “на подхвате”, которые будут запущены в idle, но не будут обрабатывать клиентские запросы, пока этого не потребуется.

MaxRequestWorkers – кол-во одновременных клиентских запросов. По умолчанию имеет значение 256, т.е. всего может быть 256 процессов, если говорить про MPM Prefork. Если запросов будет больше этого числа, то образуется очередь до числа, зависящего от директивы ListenBacklog. Ранее директива называлась MaxClients.

MaxConnectionsPerChild – также присутствует директива, которая указывает, сколько соединений будет обрабатывать один дочерний процесс, после чего умирать. Это некий защитный механизм, сделанный для того, чтобы не происходили случайные утечки памяти. По дефолту значение 0, поэтому стоит обратить на него внимание при тюнинге.

Из вышеописанного становится понятно, что MPM Prefork весьма требователен к наличию доступной памяти, а потому нужно это помнить и не выставлять высоких значений.

Тюнинг MPM Prefork

MPM Prefork, как правило, редко требует какого-то тюнинга, и во многих случаях допустимо оставить вышеописанные параметры без изменений. Но если всё же возникает такая необходимость, то главным параметром является MaxRequestWorkers, от которого и начинаются все расчёты, связанные с потребляемой оперативной памятью.

  • Для отправной точки необходимо подсчитать средний размер (average) потребляемой памяти для одного процесса Apache. На просторах интернета была найдена такая команда, которая  пропорционально делит общее использование памяти на число активных процессов Apache и добавляется сверх значения PSS, что наиболее точно показывает размер одного процесса:
pgrep httpd | xargs -n1 -I{} cat /proc/{}/smaps | awk '{if ($0 ~ /stack/) {pids+=1} else if ($0 ~/^Shared_/) {shared+=$2} else if ($0 ~ /^Pss:/) {priv+=$2}} END {printf "%.2f MB\n",(priv+shared/(pids*pids))/1024}'

На моем сервере команда выше выдала значение 288.24 Мб – это похоже на правду, если поглядеть:

ps -ylC httpd --sort:rss
  • Далее необходимо подсчитать доступную память сервера, которую можно целиком выделить для апача. Например, на сервере 32 Гб = 32768 Мб.
  • 10% от общего объема необходимо зарезервировать для ОС. Для других процессов, если помимо апача крутится БД или что-то ещё, нужно взять 20 или 30 процентов.
  • 32768 Мб – (32768 * 0,1) = 29 492‬ – именно столько будет полностью выделяться для Apache, а остальные 10% останутся для ОС.

Теперь есть все данные для расчета оптимального значения MaxRequestWorkers. Кол-во памяти сервера (минус 10% процентов под нужды ОС – 29 492 Мб) поделить на средний размер одного процесса httpd – 288.24 Мб. Непосредственно расчёты:

  • MaxRequestWorkers  = 29 492 Мб / 288.24 MB ~ 102
  • StartServers = 30% от MaxRequestWorkers = 102 Мб * 0,3 ~ 30
  • MinSpareSevers = 5% от MaxRequestWorkers = 102 Мб * 0,05 ~ 5
  • MaxSpareServers = 10% от MaxRequestWorkers = 102 Мб * 0,1 ~ 10

MaxConnectionsPerChild (MaxRequestsPerChild) стоит выставить в отличное от нуля значение только тогда, когда начинается утечка памяти. Чем быстрее течет, тем меньшее значение нужно выставлять.

MinSpareSevers и MaxSpareServers не стоит выставлять слишком большими, т.к. они будут выжирать память и при этом не использоваться. В конфигурации битрикс-окружения через bvat для всех выставляется одинаковое число. Например, если для MaxRequestWorkers оптимальное значение 10 с сервером ОЗУ=2048 Мб, то и для остальных StartServers, MinSpareServers, MaxSpareServers выставляется это же значение – таким образом сохраняется память и задействуются максимально эффективно все процессы:

cat /etc/httpd/bx/conf/prefork.conf
# from bitrix_env
# memory: 2048MB
<IfModule mpm_prefork_module>
  StartServers        10
  MinSpareServers     10
  MaxSpareServers     10
  MaxRequestWorkers   10
  MaxRequestsPerChild 5000
</IfModule>

В вышеприведенном конфиге есть один нюанс: если MaxSpareServers равен или меньше, чем MinSpareServers, то его значение автоматически приводится апачем к MinSpareServers + 1.

Заключение

В целом конфиг битрикса удовлетворяет требованиям и по сути значения для MPM prefork высчитываются где-то в потрохах bitrix-env на основе ОЗУ сервера. Такой вариант удобен тем, что эффективно используется вся доступная память сервера, выделенная под апач. Тем не менее, для более тонкой настройки может понадобится использование разных значений для MaxSpareServers и MinSpareServers, чтобы в фоне были процессы “на подхвате” для подстраховки и могли бы обработать запросы клиентов – такой вариант допустим, когда на сервере есть дополнительное кол-во оперативной памяти.

Но самое главное, что лучшая настройка – это настройка на основе статистических данных, т.е. нужно прикрутить мониторинг и наблюдать – всё сугубо индивидуально и требует индивидуального подхода. Данную статью можно использовать как отправную точку для более глубоких настроек и настоящего хайлоада, т.к. мои 2к пользователей по сути – это не так уж и много.

Используемые источники

Понравилась статья? Поделиться с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: