Взлом
Уважаемые гости! При посещении нашего сайта просим вас ознакомиться с разделами форума, прежде чем оставлять ваши объявления и т.п., а также при обращении за помощью просим быть внимательными: на сайте есть как проверенные специалисты, так и непроверенные. Если вы обратились к специалисту, который проверку НЕ проходил, рекомендуем воспользоваться услугой гарант-сервиса. Спасибо, что посетили XakerPlus!

Оптимизация nginx и php-fpm: Как я победил утечку памяти на Magento под Новый год

DataSQL

Новый
Пользователь.
Magentная аномалия. Настраиваем ngnix для работы с Magento.
acfe499b53b860863ff0411f6ff3d9d7.jpg

Слушай, ситуация классическая до зубного скрежета. Сайт работал, работал, а потом — бац. Новогодние распродажи, маркетологи радостно трут руки, ожидая наплыва клиентов, а сервер ложится от нагрузки. У меня это случилось на VDS с 39 ГБ оперативки и 12 ядрами. Вроде бы ресурсов выше крыши, но процесс php-fpm в связке с MySQL сжирал всю память, и сайт падал.

Я тогда чуть волосы на голове не вырвал. Перезапускал сервисы, добавил 20 ГБ свопа — помогало на час, потом по новой. Пришлось лезть в настройки и разбираться, что там не так.

В этой статье расскажу, как я это всё чинил. Без воды, только то, что реально помогло.

---

С чего всё начиналось

Сервер вроде нормальный:

· Ubuntu
· 39 ГБ RAM
· 12 ядер
· PHP 7.2 + PHP-FPM
· MySQL 5.7
· CMS Magento 2.3.4

Да, версии не новые, я знаю. Но Magento 2.3.4 нормально работает только с PHP 7.2, и апгрейд тянул за собой апгрейд всей CMS, а на это не было времени.

До новогодних распродаж система жила спокойно. Но когда маркетологи включили рекламу и пользователи поперли, начался ад. php-fpm в связке с MySQL жрали всю оперативку, сайт отваливался. Перезапуск помогал на пару часов, потом всё по новой. Добавил 20 ГБ файла подкачки — та же история.

Пришлось вспоминать, как настраивать nginx и php-fpm.

---

Где искать конфиги

Прежде чем что-то менять, надо понять, где это менять. Я сам иногда путался, поэтому давай разложу по полочкам.

Nginx

Конфиги nginx лежат в /etc/nginx. Главный файл — nginx.conf, но обычно настройки сайтов выносят в отдельные файлы в /etc/nginx/conf.d/. Там для каждого сайта свой .conf файл.

Еще важный момент: проверь директиву Include в конфигах. У меня предыдущий админ добавил include /var/www/www/nginx.conf, что позволяло менять настройки прямо из каталога с сайтом, как .htaccess в Apache. Если у тебя такое есть — имей в виду, что после правок этих файлов тоже нужна перезагрузка nginx.

PHP

Сначала смотрим версию:

Bash:
 php-v

У меня 7.2, конфиги лежат в /etc/php/7.2/. Там несколько папок:

· apache2 — если бы я использовал Apache с mod_php (но я не использую)
· cli — настройки для консольного PHP
· fpm — то, что нам нужно. Тут конфиги php-fpm и самого PHP
· mods-available — расширения PHP. В каждом .ini файле можно включить/отключить расширение, закомментировав строку extension

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

---

Включаем HTTP/2

Первое, что я обнаружил — на сервере не был включен HTTP/2. Это протокол, который значительно ускоряет загрузку сайтов за счет мультиплексирования и сжатия заголовков. Для Magento, где куча одновременных запросов, это особенно важно.

Включается элементарно. В файле конфигурации сайта (в /etc/nginx/conf.d/) находим блок server и добавляем http2 в директиву listen:

NGINX:
 server{ listen 443 ssl http2; ssl on; ... }

После изменений перезагружаем nginx:

Bash:
 systemctl reload nginx

---

OPcache: включать или нет

OPcache кэширует скомпилированный байт-код PHP в памяти. Это реально ускоряет работу, но с Magento есть нюансы.

При установке модулей Magento выполняет перекомпиляцию. Если OPcache включен, могут возникнуть проблемы. Приходится либо отключать кэш перед установкой, либо использовать Varnish вместо встроенного кэша Magento.

Включается OPcache одной строкой в php.ini (для fpm он лежит в /etc/php/7.2/fpm/php.ini):

INI:
 opcache.enable= 1

По умолчанию память под кэш выделяется 128 МБ. Можно увеличить:

INI:
 opcache.memory_consumption= 256

Я на своем сервере временно отключил OPcache. Причина: сайт активно дорабатывается, часто ставятся новые модули, и постоянное дерганье с кэшем задолбало. Встроенное кэширование Magento пока работает, но в будущем планирую разобраться с Varnish.

---

Отключаем надоедливых ботов

Боты — это отдельная боль. Они долбят сайт запросами, жрут ресурсы, а пользы ноль. Некоторые вообще игнорируют robots.txt.

Лечится просто — запрещаем их по User-Agent в nginx. В блок server добавляем:

NGINX:
 if($http_user_agent ~* SemrushBot|semrush|PetalBot|petalbot|MJ12Bot|AhrefsBot|bingbot|DotBot|LinkpadBot|SputnikBot|statdom.ru|WebDataStats|Jooblebot|Baiduspider|openstat.ru) { return 403; }

Это не весь список. Я дополнял его, глядя в access.log — там видно, кто реально ходит.

---

Кастомная страница maintenance для Magento

Стандартная страница "сайт на техобслуживании" в Magento выглядит уныло. Я захотел сделать свою, с красивым дизайном, но без лишней нагрузки на сервер.

В конфиг сайта добавил:

NGINX:
 set$MAGE_ROOT /var/www/www; set$maintenance off;

if (-f $MAGE_ROOT/maintenance.enable) { set $maintenance on; }

if ($remote_addr ~ (188.xx.yy.zz|188.aa.bb.cc)) { set $maintenance off; }

if ($maintenance = on) { return 503; }

location /maintenance { }

error_page 503 @maintenance;

location @maintenance { root $MAGE_ROOT; rewrite ^(.*)$ /maintenance.html break; }

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

· $MAGE_ROOT — путь к сайту
· По умолчанию режим обслуживания выключен
· Если в корне появляется файл maintenance.enable, режим включается
· Для указанных IP (например, админа) режим не срабатывает
· При включенном режиме все идут на maintenance.html

Сам maintenance.html я положил в корень сайта. Чтобы не грузить сервер лишними запросами, весь CSS и SVG-картинки я вставил прямо в этот файл.

---

Настройка пула PHP-FPM

Теперь о самом главном — настройке php-fpm. Конфиг пула обычно лежит где-то в /etc/php/версия/fpm/pool.d/. У меня это был www.conf.

После всех экспериментов получился такой конфиг:

INI:
[www] user= www-data group= www-data listen= /var/run/php/php7.2-fpm.sock listen.owner= www-data listen.group= www-data php_admin_value[disable_functions]= exec,passthru,shell_exec,system php_admin_flag[allow_url_fopen]= on php_value[max_input_vars]= 300000

pm = dynamic pm.max_children= 259 pm.start_servers= 48 pm.min_spare_servers= 24 pm.max_spare_servers= 248 pm.max_requests= 2000 request_terminate_timeout= 6000 php_admin_value[memory_limit]= 8192M chdir= /

slowlog = /var/log/php-slow.log request_slowlog_timeout= 30s

Расскажу, за что каждая строчка отвечает.

pm = dynamic — значит, количество процессов будет меняться в зависимости от нагрузки. Еще бывает static (фиксированное число) и ondemand (процессы создаются по запросу). Dynamic — золотая середина.

pm.max_children — это максимальное количество процессов, которое может создать пул. Если у тебя 1000 одновременных запросов, а max_children стоит 200 — 800 человек будут ждать. Слишком большое значение может положить сервер, если процессы сожрут всю память.

pm.start_servers — сколько процессов создается при старте FPM. Я поставил 48.

pm.min_spare_servers — минимальное количество процессов, которые просто висят в памяти и ждут запросы. Если нагрузка падает, FPM не будет убивать процессы ниже этого значения.

pm.max_spare_servers — максимальное количество простаивающих процессов. Если их больше, лишние убиваются.

pm.max_requests — каждый процесс после обработки 2000 запросов перезапускается. Это помогает бороться с утечками памяти. Если какой-то процесс начал жрать память и не отдавать, он хотя бы перезапустится через какое-то время.

request_terminate_timeout — если запрос выполняется дольше 6000 секунд, его тупо убивают. Для Magento иногда нужно много времени, поэтому я поставил с запасом.

memory_limit — 8 ГБ на процесс. Звучит безумно, но Magento умеет жрать память пачками.

slowlog и request_slowlog_timeout — если какой-то скрипт выполняется дольше 30 секунд, он пишется в лог. Потом можно посмотреть и понять, что там тормозит.

Как считать max_children

Просто взять и поставить 259 пальцем в небо — плохая идея. Я использовал скрипт, который сам посчитал примерные значения.

Сначала узнаем, сколько памяти в среднем жрет один процесс php-fpm. Для этого есть команда:

Bash:
 ps--no-headers -o "rss,cmd" -C php-fpm7.2 | awk '{ sum+=$1 } END { printf ("Средний размер процесса: %d МБ\n", sum/NR/1024) }'

Она покажет средний расход памяти в МБ.

Дальше берем всю доступную память сервера (допустим, 39 ГБ = 39936 МБ), вычитаем память под систему, MySQL, nginx, и делим на средний размер процесса. Получаем примерный max_children.

У меня вышло что-то около 250. Я округлил до 259 и оставил запас.

---

Что в итоге

После всех этих плясок с бубном сервер наконец-то перестал падать. Память держалась в районе 11 ГБ, даже когда нагрузка росла. До этого php-fpm жрал все 39 и валился.

Теперь у меня есть работающий конфиг, slow-лог показывает узкие места, боты не долбят сайт, а HTTP/2 немного ускорил загрузку.

Мораль: не надо слепо копировать конфиги из интернета. Надо понимать, что каждая строчка делает, и подбирать значения под свою нагрузку. И обязательно включать slow-log — без него ты как слепой котенок.
 


Сверху