mysurik.ru

Как я ускорил PHP на FastPanel: opcache и правильный FPM

Как я ускорил PHP на FastPanel: opcache и правильный FPM

Сайт тормозил. Не сильно, но достаточно, чтобы меня это раздражало. Каждое открытие страницы — пауза. Я смотрел на секундомер в PageSpeed и вздыхал.

Блог на WordPress у меня крутится на VPS с FastPanel. Сначала я грешил на тему, потом на хостинг, потом на всё подряд. А оказалось — дело было в том, как я настроил PHP.

Точнее, почти не настроил.

Как я докатился до тормозов

Когда я переезжал на свой VPS, я думал: «Ну, FastPanel сам всё настроит, мне остаётся только сайт залить». Отчасти так и было. Но FastPanel по умолчанию не выжимает из PHP максимум.

А WordPress без оптимизации PHP — это как машина с невыключенным ручником. Едет, но не так, как могла бы.

Мой VPS — не сервер-зверь. Так что каждая секунда на счету. Я заметил, что страницы генерируются за 400-700 миллисекунд, а я хотел уложиться в 200.

Первый шаг: включаем opcache

Opcache — это кеш скомпилированного PHP-кода. Без него каждый запрос заставляет PHP разбирать и компилировать файлы заново. С ним — он берёт готовый результат из памяти.

В FastPanel PHP-конфиги лежат в папке /etc/php/*/fpm/. Я открыл php.ini и ужаснулся: opcache был либо выключен, либо настроен по-минимуму.

Я выставил такие параметры:

opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60

revalidate_freq=60 означает: PHP перепроверяет файлы раз в минуту. Для блога, где я не меняю код каждую секунду, это идеально. Для продакшена вообще норма.

После перезапуска PHP-FPM я сразу заметил разницу. Время генерации страницы упало примерно в полтора раза. Просто так, одной настройкой.

Ох, почему я не сделал этого раньше?

Второй шаг: правильный FPM

Но opcache — это только половина дела. Вторая половина — как работает сам PHP-FPM.

По умолчанию FastPanel создаёт пул с параметрами pm = dynamic и скромными лимитами. Для моего VPS с 2 ядрами это означало: PHP-процессы заканчиваются, запросы встают в очередь.

Я почитал логи и увидел сообщения вида «server reached pm.max_children setting». Это значит — я упёрся в потолок процессов.

Что я сделал. Открыл конфиг пула сайта (он в /etc/php/*/fpm/pool.d/), выставил:

pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 8

Ключевое правило, которое я усвоил: число процессов PHP = сколько памяти может съесть каждый. На VPS с 2 ГБ памяти и WordPress-сайтом 20 детей — безопасный максимум. Если памяти меньше — режь. Если сайт тяжёлый — тоже режь, иначе OOM-killer будет стучать в дверь.

Сначала я попробовал pm = ondemand — процессы создаются только когда нужны. Звучит экономно. Но на самом деле на популярном сайте это создаёт задержки: каждый новый запрос ждёт, пока поднимется новый PHP-процесс. Я вернул dynamic — отклик стал стабильнее.

Момент Икс: OOM-killer

И тут началась веселуха.

Я поставил max_children = 40. Думал: «Чем больше, тем лучше». И через час сайт упал. Совсем. Nginx отдавал 502, а в логах была страшная надпись про OOM-killer — ядро убивало процессы, потому что память закончилась.

Сервер не умер, но PHP-процессы дёргались и умирали. WordPress с его плагинами жрёт много памяти на каждый запрос. 40 детей × 300 МБ = 12 ГБ, а у меня всего 2 ГБ.

Фух. Я тогда реально испугался за сайт. Слава богу, у меня были бэкапы и я быстро откатил настройки.

Вот тебе мой урок на блюдечке: никогда не выставляй max_children в потолок. Считай так: доступная память / средний размер PHP-процесса. У меня вышло 20 — и это уже с запасом.

Проверяем себя: как я смотрел нагрузку

Чтобы не гадать на кофейной гуще, я поставил себе правило: перед любой настройкой смотрю, что происходит на сервере. Благо у меня уже был мониторинг — Netdata висела на сервере с первого дня.

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

Это и подтвердило мою догадку: дело не в «слабом хостинге», а в том, что PHP не хватало «рабочих рук». Вот тебе и весь диагноз.

Совет: прежде чем крутить настройки, посмотри на сервер хотя бы сутки. Если у тебя нет Netdata или чего-то подобного — поставь, это бесплатно и быстро. Иначе будешь крутить вслепую, как я в первый раз.

Ещё одна удобная штука — команда htop в момент, когда сайт тормозит. Открываешь, смотришь на столбец с PHP-FPM процессами. Если они все «горят» — значит, не хватает процессов или они перегружены. Если процессор свободен, а сайт всё равно тормозит — проблема где-то в другом месте: база, сеть, диск.

Правда, с HDD у меня была ещё одна история. Сайт тормозил не из-за PHP, а из-за диска, который «спал» и просыпался долго. Netdata это показала: I/O в очередь, ожидание на диске. Это уже совсем другая задача, но сам принцип — сначала смотрим, потом крутим — спасает от провалов.

Третий шаг: realpath_cache и другие мелочи

После этого я покопался глубже и нашёл ещё пару настроек, которые дали небольшой, но приятный бонус.

Во-первых, realpath cache. Это кеш путей к файлам. Для WordPress с кучей подключаемых файлов — полезно:

realpath_cache_size=4096K
realpath_cache_ttl=600

Во-вторых, expose_php=Off. Это скрывает версию PHP в заголовках. Не столько про скорость, сколько про безопасность — зачем отдавать злоумышленникам лишнюю информацию?

В-третьих, max_execution_time=60. WordPress изредка делает тяжёлые операции, и 30 секунд по умолчанию — маловато. Но и не надо ставить 300 — тогда зависший скрипт будет висеть вечно.

Четвёртый шаг: объектный кеш

После того как я настроил opcache и FPM, у меня осталось чувство, что можно выжать ещё. И я был прав.

WordPress постоянно обращается к базе данных. Каждый запрос — это десятки SQL-запросов: темы, плагины, виджеты, кеш. Даже с opcache это создавало нагрузку на MySQL.

Решение — объектный кеш. Я поставил Redis и плагин, который переключает WordPress на него. Проще говоря, результаты запросов к базе сохраняются в памяти. При следующем запросе WordPress берёт их из Redis, а не мучает базу.

Настройка заняла минут двадцать. Поставил Redis через пакетный менеджер, включил сервис, в wp-config.php добавил несколько строк констант:

define('WP_REDIS_HOST', '127.0.0.1');
define('WP_REDIS_PORT', 6379);

И активировал соответствующий плагин в админке. Всё, объектный кеш заработал.

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

Если бы я ставил Redis раньше — сэкономил бы себе кучу нервов. Но, как говорится, учимся на своих ошибках.

Результаты: цифры

После всех настроек я замерил скорость ещё раз.

Было: время генерации страницы 400-700 мс, PageSpeed на мобильном ~70.

Стало: 150-250 мс, PageSpeed ~88-92. И самое главное — сервер перестал захлёбываться, когда я тестировал нагрузку или приходили боты.

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

Плюс — приятный побочный эффект: счёт за VPS не вырос, потому что я не покупал больше ресурсов, а научился использовать то, что есть.

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

Как я проверял, что не сломал ничего

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

Я использовал простую утилиту ab (ApacheBench). Команда вида:

ab -n 200 -c 20 https://mysurik.ru/

Это 200 запросов, 20 одновременно. Показатели простые: время ответа и количество ошибок. Если до настройки у меня было 700 мс на запрос, а после стало 200 — значит, всё работает. Если появляются ошибки 5xx — значит, я что-то перекрутил.

Один раз так и было: после увеличения max_children сайт начал отдавать ошибки, потому что памяти не хватало. Тест нагрузкой показал это сразу, ещё до того, как реальные посетители что-то заметили. Спасибо ему.

Если не хочешь заморачиваться с командной строкой — есть онлайн-сервисы вроде Loader.io или старого доброго GTmetrix. Но для быстрой проверки консоль удобнее.

Пятый шаг: свежая версия PHP

Пока я ковырялся в настройках, заметил, что FastPanel предлагает несколько версий PHP. У меня стояла семёрка — стабильная, надёжная, но уже в возрасте. А на сайте доступна была восьмёрка, и я, честно говоря, до этого даже не смотрел в её сторону.

Переключение заняло пару минут: в панели выбрал новую версию для сайта, дождался перезапуска. И знаешь, что было дальше? Сайт не упал. Вообще. Все плагины завелись, ничего не отвалилось. А скорость чуть подросла — новые версии PHP заметно быстрее на том же железе, это не маркетинг, а реальные бенчмарки.

Вот тут я понял, сколько лет терял. Старая версия — это не только про скорость, это ещё про безопасность: для старых веток прекращают выходить патчи, и каждый месяц ты сидишь на дыре, которую уже не закроют. Так что проверь, какая версия PHP у тебя на сайте, — это бесплатный шаг, который почти всегда даёт плюс к скорости.

Как я убедился, что opcache реально работает

Самый подлый момент в оптимизации — поверить, что ты всё включил, а на деле ничего не работает. Я так однажды настроил opcache и радовался, пока не заглянул в статус. Оказалось, кеш пустой — PHP смотрел на другую версию конфига.

Проверка простая. Через консоль:

php -r 'print_r(opcache_get_status());'

Если в выводе opcache_enabled = 1 и растёт счётчик hits — всё живо, кеш работает. Если hits стоят на нуле или поле opcache_enabled пустое — ты что-то настроил не там. У меня так и было: я редактировал php.ini одной версии PHP, а FastPanel гоняла сайт на другой. Классика.

Ещё вариант — страница phpinfo(): закидываешь временный файл на сайт, открываешь в браузере, ищешь секцию Zend OPcache. Но я после первого раза предпочитаю консоль — быстрее и не оставляет мусор на сервере.

Где копать дальше

Если после opcache и FPM тебе всё ещё мало — смотри в сторону кеша объектов (Redis для WordPress), кеша страниц и оптимизации самой базы данных. У меня всё это тоже подключено, но это уже темы для отдельных историй.

Главное — начни с PHP. Потому что бесплатно и даёт сразу заметный эффект.

И помни главный принцип: ничего не меняй без замеров. Открыл дашборд Netdata, посмотрел, что тормозит, — потом крути. Иначе вместо ускорения получишь ещё один сломанный сайт и потратишь вечер на откат. Я через это прошёл, ты можешь не повторять.

Если вкратце

  • Включил opcache с памятью 128 МБ и revalidate_freq=60.
  • Настроил PHP-FPM: dynamic, max_children=20 (считал по памяти, а не в потолок).
  • OOM-killer научил: 40 процессов на 2 ГБ памяти — смерть сайта, будь аккуратен.
  • Добавил realpath_cache, отключил expose_php.
  • Результат: генерация страницы 400-700 мс → 150-250 мс, PageSpeed 70 → 88+.
  • Никаких новых расходов — только правильные настройки того, что уже было.

Ваш комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *