Перейти к содержимому

nginx и PHP-FPM - разделение статики, пулы, разбор медленных ответов

Разбираемся, как связка веб-сервера и PHP обслуживает сайт, и находим, где именно теряется время ответа посетителю.

Решение

Смотрим, кто что обслуживает:

Окно терминала
# статика уходит с диска мимо PHP, динамика - в сокет процессов PHP
grep -rn 'location ~\|fastcgi_pass\|proxy_pass' /etc/nginx/bx/conf/*.conf | head
ss -x | grep php-fpm | head -3 # сокеты пулов PHP

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

Смотрим настройки пула сайта:

Окно терминала
# у каждого сайта пула свой файл: пределы задаются на сайт, а не на сервер
grep -E 'pm|max_children|max_requests|listen' /etc/php-fpm.d/bx0.conf
# число процессов подбирают под объём памяти, а не по совету из интернета

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

Разбираем медленные ответы:

Окно терминала
# журнал медленных запросов: показывает, в какой функции застрял процесс
grep -n 'slowlog\|request_slowlog_timeout' /etc/php-fpm.d/bx0.conf
tail -40 /var/log/php-fpm/bx0-slow.log 2>/dev/null

Журнал медленных запросов называет конкретное место в коде, а не просто время. Это отличает «сайт медленный» от «медленный один запрос к внешнему сервису внутри одного компонента».

Проверяем, где именно теряется время:

Окно терминала
# время ответа веб-сервера и время работы PHP пишутся отдельно
tail -20 /var/log/nginx/access.log | awk '{print $NF, $7}' | tail -10
# большая разница между ними - очередь к процессам PHP, а не медленный код
# число процессов в пуле считают от памяти узла, а не от числа посетителей

Разделение времени на ожидание и выполнение сразу отсекает половину гипотез о причинах. Долгое ожидание лечится числом процессов и кэшированием, а долгое выполнение - уже правкой самого кода страницы.

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

Связка с Apache нужна далеко не каждому проекту. Она даёт поддержку файлов с локальными правилами доступа, которые часто приносят с общего хостинга, но стоит заметной памяти на каждый процесс. Проект, не зависящий от таких файлов, работает быстрее без неё.

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

Типичные проблемы

Сайт отвечает медленно при небольшой нагрузке.

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

Память кончается при всплеске посещаемости.

Процессов в пуле слишком много для объёма памяти сервера. Каждый процесс держит свою копию PHP и потребляет память независимо от соседей.

Правки конфигурации пропали.

Правки делались в сгенерированных файлах конфигурации. Окружение переписывает их при каждом изменении настроек через своё меню.

Статика отдаётся через PHP.

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

Ошибки Apache видны посетителям.

Веб-сервер передаёт страницы ошибок второго сервера посетителю как есть. Их перехватывают отдельной настройкой обработки ошибок.

Частые вопросы

Сколько процессов PHP держать?

Столько, сколько помещается в память с запасом: объём памяти делят на реальное потребление одного процесса. Значение подбирают под сайт, готового числа нет.

Нужен ли Apache рядом с nginx?

Только если проект опирается на файлы с локальными правилами доступа. Без них связка из одного веб-сервера проще и экономнее по памяти.

Почему после переезда сайт стал медленнее?

Чаще всего пул настроен по умолчанию, а не под новый сервер. Число процессов и пределы памяти переносят и пересчитывают отдельно.

Где смотреть, что именно тормозит?

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

Смежное

Первоисточник