Как я ускорил 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+.
- Никаких новых расходов — только правильные настройки того, что уже было.