Оптимизация NGINX для высокопроизводительных PHP-сайтов

Введение

PHP является стандартом веб-разработки. Он часто запускается вместе с движком PHP-FPM для управления процессами PHP.

NGINX — это надежный веб-сервер, который часто сочетается с PHP-FPM. Он отлично работает при высокой нагрузке.

Вы можете обнаружить, что ваши веб-сайты на базе PHP имеют проблемы с производительностью при настройке NGINX+PHP-FPM. В этой статье я подробно расскажу о том, как настроить NGINX, чтобы обеспечить наилучшие результаты для веб-сайтов PHP.

Я выделю распространенные ошибки и советы по оптимизации, применимые к запуску веб-сайтов PHP в NGINX, включая WordPress.

Настоящая проблема: PHP-FPM, а не NGINX

NGINX часто рассматривается как ключ к высокопроизводительному веб-хостингу, и если у вас возникают проблемы с производительностью NGINX и сервера PHP-FPM, вы бросаетесь на настройку NGINX. Но позвольте мне рассказать вам с самого начала то, что вы уже знаете:

Nginx работает быстро. Вам редко придется его настраивать.

NGINX не зря так популярен в веб-хостинге. Он отлично и быстро работает с любым бэкендом, будь то PHP-FPM, Python или что-то еще! И это происходит потому, что требует минимальных магических настроек. Настоящее волшебство заключается в настройке PHP-FPM и кэшировании. Это краеугольный камень высокопроизводительных веб-сайтов.

Распространенные ошибки настройки PHP-FPM

Ловушка №1. Настройка без кэша

Я начну с основ. Самое главное для высокой производительности — это кэширование.

Кэширование играет решающую роль в настройке производительности PHP. Эффективные механизмы кэширования, такие как Redis для кэширования объектов и баз данных, а также полностраничное кэширование, могут значительно снизить нагрузку на PHP.

Если вы не используете какой-либо полностраничный кэш, есть вероятность, что все ваши усилия по настройке окажутся бесплодными.

Так что не упускайте из виду, что ваш PHP использует полностраничный кеш. Если ни одна из ваших страниц не кэширована, на вашем веб-сайте не будет достаточно ни оперативной памяти, ни ресурсов ЦП для обработки значительного количества посетителей; никакие настройки не помогут ему работать быстрее.

Так что же делать, чтобы избежать этой ловушки? На самом деле две вещи:

  1. Используйте полностраничный кеш. Стандартное решение — Varnish Cache. Varnish отлично работает с WordPress, его легко настроить, он намного превосходит любые плагины кеширования WordPress. Поскольку это программное обеспечение, а не скрипт, оно становится частью вашего веб-стека, поскольку для обслуживания кэшированных страниц не требуется выполнение PHP, что делает кэшированные запросы молниеносными!
  2. Используйте кэш данных. Этот тип кеша гарантирует, что даже на динамических страницах будут кэшироваться тяжелые запросы. Кэш Redis отлично работает с WordPress и его очень легко настроить.

Вы должны использовать оба упомянутых типа кэшей, здесь нет ситуации «или» или выбора. Отказ от любого типа кэширования приведет к тому, что ваш сервер в конечном итоге станет медленным и непригодным для использования.

Питалл №2. Недооценка трафика и отсутствие проактивной настройки

Многим администраторам не удается смоделировать достаточное количество соединений во время первоначальных стресс-тестов, что приводит к сбою PHP-FPM, когда текущие настройки больше не соответствуют существующим шаблонам трафика. Вы должны заранее настроить настройку веб-сервера и переоценить существующие настройки по соображениям безопасности и производительности.

Существует формула для правильного сопоставления настроек PHP-FPM. Это позволит сделать аудит производительности менее частым. Я поделюсь этой формулой ниже.

Ловушка №3. Неправильное распределение ресурсов

Выделение PHP-FPM большего объема оперативной памяти, чем доступно, может привести к сбоям, особенно при доступе к некешированным страницам.

Теперь, когда мы рассмотрели наиболее распространенные ошибки, давайте продолжим и углубимся в оптимизацию настройки PHP-FPM.

Понимание управления процессами PHP-FPM

PHP-FPM может использовать один из трех типов управления процессами:

  • Статический поддерживает фиксированное количество дочерних процессов, обеспечивая немедленную доступность для обработки запросов. Идеально подходит для сайтов с высокой посещаемостью, где время отклика имеет решающее значение.
  • Динамический динамически управляет количеством дочерних процессов в заданных пределах, балансируя использование и доступность ресурсов. Подходит для умеренно загруженных сайтов.
  • Ondemand разветвляет процессы по мере поступления запросов. Лучше всего подходит для сайтов с низким трафиком, где экономия памяти является приоритетом над немедленным ответом.

Настройка PHP-FPM покажется такой простой, если я скажу вам, что все дело в выборе правильного типа управления процессами из тех, которые я упомянул, а затем в настройке нужного количества дочерних процессов.

Так что по сути только два основных параметра!

Этапы настройки PHP-FPM

Важность пространства подкачки при настройке PHP-FPM

Прежде чем приступить к настройке параметров PHP-FPM, вам необходимо «защитить» свой сервер достаточным пространством подкачки.

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

Настройка PHP-FPM — это, по сути, не что иное, как попытка заставить его использовать как можно больше ОЗУ и ЦП из имеющихся, но не быть достаточно жадным, чтобы брать больше, чем физическая ОЗУ.

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

Проще говоря, выделите достаточно места подкачки, чтобы ваш сервер не вышел из строя.

Проверьте свою текущую конфигурацию

Настройки PHP-FPM настраиваются для пула. Обычно на каждом веб-сайте имеется один пул, и его конфигурация находится в файле с таким путем: /etc/php-fpm.d/www.conf. Настоятельно рекомендуется называть ваши пулы и соответствующие имена файлов доменными именами ваших веб-сайтов. Например /etc/php-fpm.d/example.com.confи [example.com]поверх содержимого файла.

Все директивы настройки, обсуждаемые далее, должны быть скорректированы в файлах конфигурации пула.

Установите тип управления процессом static: как это сделать

Статическое управление процессами часто рекомендуется для сценариев с высоким трафиком, поскольку оно поддерживает фиксированное количество дочерних процессов. Это обеспечивает немедленную доступность для обработки запросов и может привести к более стабильному использованию ЦП и памяти. Нет никаких процессов разветвления и уничтожения, все просто более стабильно.

Есть ресурсы, которые скажут вам, что для бюджетных VPS это ondemand единственный хороший вариант. Ну, это не так. Подходит static для любого веб-сайта, включая сервер низкого уровня. Вам просто нужно сделать правильные математические вычисления для других настроек.

Таким образом, наша объективно правильная конфигурация типа управления процессами примерно такая:

pm = static

Это было легко. Давайте перейдем к следующему важному вопросу, который немного сложнее.

Установите количество рабочих PHP-FPM

Теперь, когда мы определили тип управления процессами, нам нужно сообщить PHP-FPM, сколько процессов мы хотим постоянно держать на нашем сервере.

Благодаря управлению процессами static эти процессы будут готовы обрабатывать запросы в любое время. Эти процессы называются рабочими процессами.

Мы хотим иметь как можно больше рабочих процессов, чтобы одновременно можно было обрабатывать больше посетителей. Но наличие слишком большого количества рабочих процессов приведет к исчерпанию оперативной памяти и началу подкачки, что приведет к проблемам с производительностью. Таким образом, нужное количество рабочих процессов необходимо рассчитывать на основе доступной памяти вашего сервера и среднего размера ваших процессов PHP.

Количество рабочих процессов для пула PHP-FPM определяется директивой pm.max_children. Давайте рассмотрим основные принципы подсчета правильной стоимости.

Формула для рабочих PHP Max

Для расчета pm.max_children учитывайте как оперативную память, так и процессор вашего сервера.

Чтобы полностью использовать все ядра ЦП, вам необходимо установить pm.max_childrenхотя бы их количество.

Например, pm.max_children = 32 на машине с 32-ядерным ЦП гарантирует, что все модули ЦП будут задействованы в сценарии высокой нагрузки, например, во время пиков трафика.

Однако решающим фактором для правильного значения этого параметра является доступная оперативная память.

Я выделил available RAM жирным шрифтом, потому что другим резидентным программам для своей работы всегда потребуется часть вашей физической оперативной памяти.
Типичным примером является ваша база данных.

Итак, формула нашего тюнинга такова:

pm.max_children = (Total RAM in MB - Total RAM taken by other programs in MB)/(Average PHP worker RAM in MB)

Как получить эти индивидуальные значения?

  • Общий объем оперативной памяти сервера обычно известен согласно вашему плану хостинга. Если у вас есть сомнения, вы всегда можете проверить это free -h и найти первое отображаемое число. Конвертируйте его в мегабайты для дальнейших вычислений (если оно уже не отображается в мегабайтах).
  • Оперативную память, занимаемую резидентными программами, подсчитать сложнее. Здесь можно быть грубым с цифрами. Для простоты это должен быть размер вашей базы данных в мегабайтах. Но база данных хранится на диске, скажете вы? Да, но с MySQL вы должны стремиться иметь столько же буферов (и, следовательно, использования памяти), сколько и размер самой базы данных. Однако, если вы не уверены, предположим, что резидентные программы занимают около 20% всей памяти.
  • Для среднего объема оперативной памяти на процесс PHP примите значение 128 МБ. На самом деле существуют методы, которые вы можете использовать для расчета этого показателя с учетом особенностей вашего веб-сайта. Однако рабочие процессы PHP-FPM имеют тенденцию со временем увеличиваться в размерах, в зависимости от других параметров. В любом случае, если вы хотите максимально легко вычислить это значение самостоятельно, вам необходимо предположить, что на веб-сервере есть только один пул, а также убедиться, что ваш пул PHP-FPM работает достаточное количество времени. Используйте следующую команду:
ps -u example -o rss= -C php-fpm | awk '{sum+=$1; count++} END {if (count > 0) print (sum/count)/1024 " MB"; else print "No PHP-FPM processes found"}'

Здесь example необходимо изменить имя пользователя вашего пула PHP-FPM, которое определяется директивой user = конфигурации пула.

После выполнения всех математических вычислений согласно формуле для pm.max_children убедитесь, что вы обновили конфигурацию пула PHP-FPM, указав правильное значение, например X:

pm = static
pm.max_children = X
; comment the values of these directives
; pm.start_servers =...
; pm.min_spare_servers =...
; pm.max_spare_servers =...

Как видите, я закомментировал некоторые директивы, поскольку они на самом деле не применимы к выбранному нами типу управления процессами static.

Я также специально не придавал этому примеру никакого конкретного значения, чтобы предотвратить то, что другие руководства заставляют вас делать — копировать и вставлять.
Никогда не существует единого хорошего значения, pm.max_children подходящего для всех серверов! Вы всегда должны рассчитывать его правильно, исходя из доступной оперативной памяти.

Это может быть 5 как на каком-нибудь низкоуровневом сервере, так и 50 на среднем уровне, но если вы укажете неправильное значение, ваш сервер с радостью сделает за вас только одно — сбой 🙂

Вы можете проверить правильность обновленной конфигурации, выполнив php-fpm -t команду.

Стресс-тест, тонкая настройка и мониторинг

Чтобы смоделировать трафик и соответствующим образом настроить параметры, выполните нагрузочное тестирование.

Вы можете выбрать инструмент, например, wrk для моделирования высокого трафика. Чтобы установить его, запустите:

yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install wrk

Запустите следующий тест в терминале, наблюдая за своим сервером (в другом сеансе терминала):

wrk -t12 -c400 -d30s https://www.example.com/

При этом выполняется стресс-тест в течение 30 секунд с использованием 12 потоков и сохранением 400 открытых HTTP- соединений.

Я рекомендую установить параметр -t на количество ядер ЦП на вашем сервере в вашей команде wrk.

Выберите значение -c, которое выше pm.max_children установленного вами. Это будет моделировать сценарий, в котором количество входящих запросов превышает возможности сервера для обработки одновременных процессов PHP.

После запуска теста проверьте, исчерпана ли ваша свободная оперативная память настолько, что ваш сервер начал подкачку. Бегайте free -h и ищите б/у свап.

Если используемый объем подкачки не увеличился в достаточной степени, можно с уверенностью предположить, что вы можете увеличить его pm.max_requests еще немного и, таким образом, использовать больше памяти и повысить производительность. Используйте приросты 5при тестировании или, 10 если у вас есть больше оперативной памяти, с которой можно поиграть.

Используйте инструменты NGINX Amplify для мониторинга использования памяти и ЦП процессами PHP-FPM. Обратите особое внимание на использование свопа; чрезмерное использование подкачки может указывать на необходимость изменить конфигурацию и снизить значение pm.max_children.

Итеративно настраивайте конфигурацию PHP-FPM, поскольку она никогда не выполняется единоразово. Он требует постоянных корректировок на основе реальных моделей использования и данных о производительности сервера.

Примечания к pm.max_requests

Вам следует знать о третьем довольно важном параметре pm.max_requests. Он контролирует количество запросов, которые обрабатывает дочерний процесс перед его ротацией.

Это важно. Даже при статическом управлении процессами рекомендуется в конечном итоге обновить рабочий процесс, запустив его заново.
Это гарантирует, что никакие утечки памяти не будут перегружать оперативную память вашего сервера.

pm.max_requests = 10000

Я бы рекомендовал установить для этого значения большое, но не бесконечное число. Мы должны гарантировать, что каждый рабочий процесс в конечном итоге будет перезапущен, чтобы освободить память. Но нам также необходимо сбалансировать это со слишком большим количеством нежелательной переработки. Установка этого значения, например, 10приведет к перезапуску при каждом 10-м запросе (на одноядерной машине), так что это никогда не будет хорошим значением.

На низкопроизводительном сервере я бы установил его 500 для безопасности экспериментов, в то время как тщательно протестированный низкопроизводительный сервер или более крупный сервер подойдет 10000 и для многих других задач, при условии, что у вас больше места оперативной памяти для экспериментов.

После настройки этого значения крайне важно повторно запустить стресс-тесты и точную настройку, поскольку это влияет на средний объем памяти, занимаемый рабочим PHP-FPM.

Заключительные мысли: NGINX и искусство настройки PHP

По сути, настройка веб-сайта на основе PHP на NGINX не требует ничего особенного с самим NGINX. Основное внимание должно быть уделено уровню PHP. NGINX, благодаря своей неизменной эффективности, дополняет хорошо настроенную среду PHP.

Помните, что настройка PHP, особенно PHP-FPM, — это скорее искусство, чем строгая наука, сочетающая технические ноу-хау с догадками и экспериментами. Путь к оптимальной производительности непрерывен и требует постоянной корректировки и мониторинга.

Освойте искусство настройки PHP: именно здесь кроется настоящий прирост производительности. Как говорят в мире веб-хостинга: «Настройте свой PHP и позвольте NGINX творить чудеса».