Маленький сервер с Apache и MySQL падает по нехватке памяти при обходе поисковыми роботами: лимиты рабочих процессов под реальный объём памяти

Что было. Небольшая машина с 1,5 ГБ памяти и 2 ГБ подкачки, стек Apache + PHP + MySQL. Раз в несколько дней сервер зависал и требовал жёсткой перезагрузки; в журнале ядра OOM-killer. Мониторинг показывал нормальное потребление, а затем резкий скачок.

Что нашли. Настройки Apache по умолчанию (MaxClients 150, десятки запасных потоков) позволяют запустить сотни рабочих процессов; при обходе сайта поисковым роботом одновременных запросов становится много, каждый процесс с PHP занимает десятки мегабайт, память исчерпывается за секунды. Автор снизил лимиты до MaxClients 15 и малых запасных значений; сбои стали реже, но один повторился, и причина осталась смешанной (соседние службы тоже ели память). Отмечено решённым условно.

Как считать лимит для Apache.

  1. Память одного рабочего процесса: ps -ylC apache2 --sort:rss | awk '{sum+=$8; n++} END {print sum/n/1024 " MB"}' (для prefork с PHP-модулем типично 30–80 МБ).
  2. Свободная память под Apache: всё минус ОС (~200 МБ), MySQL (его innodb_buffer_pool_size плюс ~100 МБ), прочие службы.
  3. MaxRequestWorkers = свободная память / память процесса. На 1,5 ГБ это 8–12, не 150.
  4. В /etc/apache2/mods-available/mpm_prefork.conf: StartServers 2, MinSpareServers 2, MaxSpareServers 4, MaxRequestWorkers 10, MaxConnectionsPerChild 1000 (перезапуск процессов защищает от утечек). Перезапустить Apache.

Что лучше сделать вместо тонкой настройки prefork.

  • Перейти на mpm_event + php-fpm: Apache обслуживает статику и соединения лёгкими потоками, а PHP живёт в отдельном пуле с собственным лимитом (pm.max_children), который считается так же. Это стандарт для современных Ubuntu.
  • Ограничить MySQL: innodb_buffer_pool_size 256–384 МБ на такой машине, performance_schema = OFF, разумные max_connections.
  • Добавить подкачку 1–2 ГБ (уже была) и vm.swappiness=10: подкачка не решает, но даёт время OOM-killer’у не срабатывать на коротком пике.
  • Убрать лишнее: мониторинг с веб-интерфейсом, панели, торрент-клиенты и прочее, что «поставили попробовать», на 1,5 ГБ роскошь.
  • Кэш страниц (модуль кэширования или обратный прокси) для анонимных запросов: роботы получают готовый HTML, PHP не запускается.

Как поймать виновника до падения. dmesg -T | grep -A20 -i "out of memory" покажет таблицу процессов на момент OOM: кто съел больше всего, там и настраивать. Мониторинг с интервалом 10 секунд (vmstat 10, free -m в лог) ловит пик, который пятиминутный мониторинг пропускает.

Параметр описан в документации Apache.