Хакер - Magentная аномалия. Настраиваем ngnix для работы с Magento
hacker_frei
Денис Колисниченко
Содержание статьи
- Вкратце о сервере
- Конфигурационные файлы
- Включение HTTP 2.0
- OPcache: быть или не быть?
- Запрещаем доступ ботов
- Кастомная maintenance-страница для «Мадженты»
- Настройка пула www
- Выводы
По настройке сервера nginx созданы тысячи мануалов, но иногда случаются ситуации, не описанные ни в одном руководстве. Предпосылкой для этой публикации послужила вовсе не моя предыдущая статья про «Апач», а реальный кейс: на VDS с 39 Гбайт оперативки nginx кушал всю память, после чего сайт «ложился». Как мне удалось справиться с этой напастью, я сейчас расскажу.
ВКРАТЦЕ О СЕРВЕРЕ
Итак, имеем самый обычный VDS с 39 Гбайт оперативки, 12 ядрами и «Убунтой» на борту. PHP 7.2, PHP-FPM, MySQL 5.7. Версии ПО, может, немного и древние, но установлены неспроста: такая конфигурация обусловлена требованиями CMS Magento 2.3.4. Более новая версия PHP повлекла бы за собой обновление версии CMS, а этого по ряду причин делать было нельзя.
Как обычно, все прекрасно работало до одного момента: пока не начались традиционные новогодние распродажи и маркетологи не притащили на сайт кучу трафика. Вот тут и начались проблемы. Наиболее характерная из них — процесс php-fpm в паре с MySQL выжирал всю оперативку. Перезапуск сервисов с добавлением 20 Гбайт свопа проблему решил ненадолго. Дальше пошел процесс настройки nginx и php-fpm, прежде всего — последнего, поскольку именно его параметры влияют на эксплуатационные особенности CMS. Я не буду рассматривать установку и настройку nginx с самого начала — скорее всего, у тебя уже все настроено. Даже если это не так, в сети можно найти множество статей и руководств на эту тему. Сконцентрируемся лучше на параметрах, непосредственно влияющих на производительность сервера.
КОНФИГУРАЦИОННЫЕ ФАЙЛЫ
Прежде чем приступить к дальнейшему чтению статьи, нужно понимать, что и где и редактировать. Конфигурация nginx хранится в каталоге /etc/nginx. Основной конфиг — nginx.conf, но времена одного большого файла уже давно прошли, и в зависимости от содержимого nginx.conf конфигурация веб‑сервера может быть разбросана по всей файловой системе.
Как правило, конфиги сайтов хранятся в каталоге /etc/nginx/conf.d. Для каждого сайта принято создавать отдельный конфиг. Далее нужно проанализировать содержимое файлов конфигурации на предмет директивы Include. Если первоначальную настройку выполнял не ты, поищи во всех файлах каталога nginx файлы с директивой Include — так ты поймешь, что и откуда берется. Например, в моем случае кто‑то до меня добавил директиву include /var/www/www/nginx.conf, чтобы некоторые параметры можно было вынести в каталог документов веб‑сервера и редактировать их без редактирования основной конфигурации сервера. Что‑то вроде .htaccess в «Апаче», вот только после изменения этого файла все равно придется делать reload сервису.
Далее перейдем к параметрам PHP. У него есть несколько конфигураций. Прежде всего введи команду php –v, чтобы выяснить номер версии. Так вот, конфигурация твоей версии PHP хранится в каталоге /etc/php/<номер версии>. В этом каталоге ты найдешь четыре подкаталога. Да, это три разные конфигурации и список модулей:
- apache2 — конфиг для модуля mod_rewrite «Апача». Если у тебя «Апач», то настройки PHP хранятся здесь;
- cli — параметры консольной версии PHP, они вступают в силу, если ты запускаешь выполнение скрипта с консоли командой
php <имя_скрипта>; - fpm — наш случай, а именно конфигурация сервиса
php-fpmи самого PHP, работающего в связке nginx, PHP-FPM и PHP; - mods-available — доступные расширения PHP. Здесь хранятся .ini-файлы, по одному для каждого установленного расширения. Закомментировав строчку
extensionвнутри этого файла, ты можешь выключить расширение.
Чтобы узнать, какой именно файл конфигурации PHP используется, помести в корень сервера PHP-скрипт, вызывающий php_info(). Эта функция и покажет (кроме всего прочего) локацию и имя файла конфигурации.
ВКЛЮЧЕНИЕ HTTP 2.0
Если не считать статического кеша, то самое крутое, что ты можешь сделать в настройках nginx, — это включить HTTP 2.0. Некоторые админы почему‑то забывают об этом. В моем случае так и было: я уже получил преднастроенный подрядчиком сервер, в котором почему‑то забыли включить версию 2.0 протокола HTTP. Думаю, не нужно говорить о том, как медленно работала Magento.
Больше дела, меньше слов: открой конфигурацию сайта в каталоге /etc/nginx/conf.d/<имя_сайта>.conf. Найди блок server и добавь (если этого еще не сделано) http2 в директиву listen. Должно получиться так:
server {
listen 443 ssl http2;
ssl on;
...
}
Номер порта и SSL установи по своему усмотрению, но поскольку пример реальный, то SSL уже есть на твоем сайте, так как это стандарт по умолчанию.
OPCACHE: БЫТЬ ИЛИ НЕ БЫТЬ?
OPcache используется для кеширования скомпилированного байт‑кода PHP-скриптов в оперативной памяти. С одной стороны, при использовании OPcache повысится производительность и снизится нагрузка на сервер — PHP уже не будет создавать байт‑код при выполнении скрипта, а станет использовать откомпилированную версию из кеша. С другой стороны, в процессе использования сложных CMS вроде Magento могут возникнуть проблемы. При установке расширения этого движка происходит так называемая перекомпиляция Magento. Лучше эту процедуру производить при выключенном кеше, а затем снова его включить.
Для этого редактируется конфиг, перезагружается сервис php-fpm, производятся необходимые действия, а потом снова все повторяется — редактирование конфига и перезапуск сервиса. Также возникнут проблемы при использовании собственной системы кеширования Magento. Здесь поможет использование Varnish. В общем, резюмировать можно следующее: если ты решишь включить OPcache, то в случае с Magento тебе придется отказаться от использования собственной системы кеширования и разбираться с настройками Varnish. Установка расширений доставляет меньше неудобств, поскольку выполняется не так часто.
Для включения OPcache нужно открыть конфигурацию PHP, в нашем случае это /etc/php/7.2/fpm/php.ini, и добавить в него всего одну строчку:
opcache.enable = 1
Этим ты активируешь opcache с настройками по умолчанию. Как минимум можно задать параметр opcache.memory_consumption, который регулирует размер памяти (в мегабайтах), выделяемый для OPcache. Значение по умолчанию — 128, минимально допустимое значение — 8:
opcache.memory_consumption = 256
С остальными настройками OPcache можно ознакомиться здесь. На своем сервере я отключил OPcache по двум причинам. Во‑первых, сайт активно допиливается, из‑за чего очень часто приходится добавлять в него новые функции или изменять уже имеющиеся, что при включенном OPcache не совсем удобно. Во‑вторых, пока нет никакого желания устанавливать Varnish, хотя в скором времени это придется сделать. На данный момент используется штатная система кеширования.
ЗАПРЕЩАЕМ ДОСТУП БОТОВ
Боты во время своего обхода сайта расходуют драгоценные ресурсы. При неблагоприятном стечении обстоятельств (например, случайные или намеренные визиты нескольких ботов сразу) сайт может упасть, не выдержав нагрузки. Каждый GET-запрос страницы тянет за собой использование ресурсов сервера: загрузка элементов страницы (картинки, CSS-таблицы и прочее), обращение к базе данных и, возможно, к сторонним ресурсам. Бот — это не пользователь, который откроет одну страницу и изучает ее контент, бот открывает страницу за страницей, что может вызвать негативные последствия.
К сожалению, не все боты читают содержимое robots.txt. Избавиться от ботов можно в конфиге nginx. Открой файл конфигурации твоего сайта, отыщи блок server и добавь в него следующие строки:
if ($http_user_agent ~* SemrushBot|semrush|PetalBot|petalbot|MJ12Bot|AhrefsBot|bingbot|DotBot|LinkpadBot|SputnikBot|statdom.ru|WebDataStats|Jooblebot|Baiduspider|openstat.ru) {
return 403;
}
Думаю, смысл этих строк понятен всем: мы смотрим на http_user_agent и, если агент пользователя содержит одну из запрещенных строк, возвращаем ошибку 403. Это не весь список ботов, ты можешь дополнить его. Узнать имя бота можно из access.log — в логе сохраняется вся нужная инфа.
КАСТОМНАЯ MAINTENANCE-СТРАНИЦА ДЛЯ «МАДЖЕНТЫ»
При переводе в режим обслуживания Magento отображает непримечательное сообщение о том, что сайт закрыт. Выглядит очень непрезентабельно. Мы можем создать кастомную страницу, которая будет отображаться, когда сайт закрыт на обслуживание. Просто добавь в конфиг твоего сайта следующие строки (внутри блока server):
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устанавливает каталог, в котором установлено приложение; - по умолчанию режим обслуживания выключен —
set $maintenance off; - режим обслуживания включается, если в корневом каталоге приложения есть файл
maintenance.enable; - режим обслуживания не работает для IP-адресов
188.xx.yy.zzи188.aa.bb.cc. Здесь ты можешь указать свои адреса; - если режим обслуживания включен, возвращается страница
maintenance.html, находящаяся в корне (MAGE_ROOT). Именно сюда следует поместить страничку с нужным текстом и оформлением. Оформление (CSS) лучше расположить внутри этого файла, как и картинки (в SVG), — сайт в режиме обслуживания должен минимально нагружать сервер.
НАСТРОЙКА ПУЛА WWW
Теперь перейдем к опциям PHP-FPM. Как правило, значимые параметры хранятся в конфиге пула — в каталоге /etc/php/версия/fpm/pool.d/. В нем есть один или несколько конфигов. Далее приведен пример моего файла:
[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. В большинстве случаев используется динамическая конфигурация (pm = dynamic), при которой количество процессов PHP-FPM меняется динамически, в зависимости от количества запросов. При этом имеется минимальное количество процессов, которые будут запущены при старте FPM-сервиса. Остальные параметры трактуются так:
pm.max_children— максимальное количество дочерних процессов, которые будут созданы, по сути это максимальное количество запросов, которые может обслужить пул. АналогApacheMaxClientsдля «Апача»;pm.start_servers— число дочерних процессов, которые будут созданы при запуске пула, используется только в динамической конфигурации;pm.min_spare_servers— минимальное количество дочерних процессов в статусе Idle;pm.max_spare_servers— максимальное количество дочерних процессов в статусе Idle;pm.max_requests— максимальное количество запросов, которое должен обработать процесс FPM перед его перезапуском (помогает избежать утечек памяти). Здесь нужно экспериментировать, для кого‑то подойдет значение 1000, для кого‑то значение нужно увеличивать.
Можно экспериментировать с этими значениями вручную, а можно воспользоваться скриптом php_settings.sh. На всякий случай код скрипта:
#!/usr/bin/env bash
if [[ -z ${1} ]] || [[ -z ${2} ]]; then
echo -e "Run script: ./php_settings.sh <Memory size MB> <PHP-FPM version>\n./php_settings.sh 1024 7.4"
exit 1
fi
_php_version=${2}
_cpu_cores=$(( $(lscpu | awk '/^Socket/{ print $2 }') * $(lscpu | awk '/^Core/{ print $4 }') ))
_mem_usage=${1}
_average_proc_mem=$(ps --no-headers -o "rss,cmd" -C php-fpm${_php_version} | awk '{ sum+=$1 } END { printf ("%d\n", sum/NR/1024) }')
echo -e "average_proc_mem=${_average_proc_mem}\n#################"
echo pm.max_children=$(awk "BEGIN { printf ${_mem_usage} / ${_average_proc_mem} }")
echo pm.start_servers=$(awk "BEGIN { printf ${_cpu_cores} * 4 }")
echo pm.min_spare_servers=$(awk "BEGIN { printf ${_cpu_cores} * 2 }")
echo pm.max_spare_servers=$(awk "BEGIN { printf ${_cpu_cores} * 4 }")
Использовать скрипт просто: задаешь объем (в мегабайтах) оперативки и версию PHP, затем получаешь строки, которые нужно скопировать в www.conf (конфигурация пула). Используя код скрипта, можно все посчитать и вручную, однако автоматизация еще никогда не мешала. Вывод скрипта может быть таким:
average_proc_mem=159
#################
pm.max_children=251.17
pm.start_servers=48
pm.min_spare_servers=24
pm.max_spare_servers=48
В конфиг нужно скопировать не все, а только строки после ###. Первая строка намекает на примерный объем процесса, который у тебя должен получиться. Если max_children не целое число, его можно округлить до ближайшего целого.

С приведенными настройками сервак был перезагружен (перезагружать вообще не нужно было, но для чистоты эксперимента это необходимо). Спустя девять часов работы в обычном режиме расход памяти не превысил 11 Гбайт, что не может не радовать.

Также не забудь включить slow-лог:
slowlog=/var/log/php-slow.log
request_slowlog_timeout=30s
Если какой‑то скрипт выполняется больше 30 секунд, то инфа о нем будет помещена в файл /var/log/php-slow.log. Это поможет при выявлении узких мест на сайте.
ВЫВОДЫ
По большому счету каждая CMS имеет свои индивидуальные особенности и может потребовать собственных настроек сервера. Для Magento оптимальная конфигурация nginx описана в этой публикации. Статья хоть и не раскрывает всех тонкостей настройки nginx, однако приведенных в ней инструкций вполне достаточно, чтобы сделать существенно «лучше, чем было».
Читайте ещё больше платных статей бесплатно: https://t.me/hacker_frei