PHP5-FPM случайно начинает потреблять много CPU

У меня возникла действительно странная проблема, и я не уверен, как отлаживать ее дальше. У меня есть экземпляр NGINX + PHP5-FPM + APC Amazon Ubuntu, и на нем установлен веб-сайт, который представляет собой сложную фреймворк PHP. При попытке отладить проблему я уменьшил поток до этого: множество больших классов включается, создаются основные объекты, запускается сеанс, извлекается массив конфигураций из memcached, файл XML извлекается из memcached, HTML шаблоны включены, вывод отправляется клиенту.

Затем я использую инструмент http_load для размещения сайта под нагрузкой 20 запросов в секунду: http_load -timeout 10 -rate 20 -fetches 10000./urls.txt

То, что происходит дальше, довольно странно. top показывает кучу процессов php5-fpm, порожденных каждым из нескольких процессоров, и все работает гладко, как это:

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
28440 www-data 20 0 67352 10m 5372 S 4.3 1.8 0:20.33 php5-fpm
28431 www-data 20 0 67608 10m 5304 S 3.3 1.8 0:16.77 php5-fpm
28444 www-data 20 0 67352 10m 5372 S 3.3 1.8 0:17.17 php5-fpm
28445 www-data 20 0 67352 10m 5372 S 3.0 1.8 0:16.83 php5-fpm
28422 www-data 20 0 67608 10m 5292 S 2.3 1.8 0:18.99 php5-fpm
28424 www-data 20 0 67352 10m 5368 S 2.0 1.8 0:16.59 php5-fpm
28438 www-data 20 0 67608 10m 5304 S 2.0 1.8 0:17.91 php5-fpm
28439 www-data 20 0 67608 10m 5304 S 2.0 1.8 0:23.34 php5-fpm
28423 www-data 20 0 67608 10m 5292 S 1.7 1.8 0:20.02 php5-fpm
28430 www-data 20 0 67608 10m 5300 S 1.7 1.8 0:15.77 php5-fpm
28433 www-data 20 0 67352 10m 5372 S 1.7 1.8 0:17.08 php5-fpm
28434 www-data 20 0 67608 10m 5292 S 1.7 1.8 0:18.56 php5-fpm
20648 memcache 20 0 51568 8192 708 S 1.3 1.3 2:51.06 memcached
28420 www-data 20 0 69876 13m 6300 S 1.3 2.3 0:20.89 php5-fpm
28421 www-data 20 0 67608 10m 5300 S 1.3 1.8 0:21.19 php5-fpm
28429 www-data 20 0 9524 2260 992 S 1.3 0.4 0:11.68 nginx
28435 www-data 20 0 67608 10m 5304 S 1.3 1.8 0:18.58 php5-fpm
28437 www-data 20 0 67352 10m 5372 S 1.3 1.8 0:17.87 php5-fpm
28441 www-data 20 0 67608 10m 5292 S 1.3 1.8 0:20.75 php5-fpm

Затем через некоторое время, которое может быть где-то между секундой и минутами, несколько (обычно два) процесса php5-fpm внезапно потребляют весь процессор:

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
28436 www-data 20 0 67608 10m 5304 R 48.5 1.8 0:23.68 php5-fpm
28548 www-data 20 0 67608 10m 5276 R 45.2 1.7 0:07.62 php5-fpm
28434 www-data 20 0 67608 10m 5292 R 2.0 1.8 0:23.28 php5-fpm
28439 www-data 20 0 67608 10m 5304 R 2.0 1.8 0:26.63 php5-fpm

На этом этапе все застревает, а все новые запросы HTTP-запросов. Если я остановлю инструмент http_load, php5-fpm будет висеть там в течение нескольких минут. Интересно, что если я php5-fpm stop на php5-fpm, процессы php5-fpm исчезнут, но любые команды, которые используют файловую систему, будут иметь проблемы с выполнением. Например, если я попытаюсь загрузить файл через ssh, top будет показано следующее: для запуска фактической загрузки требуется много минут:

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3298 sshd 20 0 7032 876 416 R 75.2 0.1 0:04.52 sshd
3297 sshd 20 0 7032 876 416 R 24.9 0.1 0:04.49 sshd

В журнале ошибок PHP обычно есть следующее:

[05-Dec-2012 20:31:39] WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 8 children, there are 0 idle, and 58 total children
[05-Dec-2012 20:32:08] WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 16 children, there are 0 idle, and 66 total children

Журнал ошибок Nginx заливается этими данными:

2012/12/05 20:31:36 [error] 4800#0: *5559 connect() to unix:/dev/shm/php-fpm-www.sock failed (11: Resource temporarily unavailable) while connecting to upstream, client: ..., server: ec2-....compute-1.amazonaws.com, request: "GET /usa/index.php?page=contact_us HTTP/1.0", upstream: "fastcgi://unix:/dev/shm/php-fpm-www.sock:", host: "ec2-....compute-1.amazonaws.com"

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

nginx.conf: http://pastebin.com/uaD56hJF

pool.d/www.conf: http://pastebin.com/mFeeUULC

=== ОБНОВЛЕНИЕ 1 ===

Конфигурация сайта: http://pastebin.com/qvinVNhB

=== ОБНОВЛЕНИЕ 2 ===

Также было обнаружено, что dmesg сообщает об ошибках, подобных этому

[6483131.164331] php5-fpm[28687]: segfault at b6ec8ff4 ip b78c3c32 sp bff551f0 error 4 in ld-2.13.so[b78b5000+1c000]

=== ОБНОВЛЕНИЕ 3 ===

У нас есть новый экземпляр Amazon EC2 на всякий случай, чтобы исключить возможные проблемы с оборудованием. Также я использую php-fastcgi, чтобы исключить возможные ошибки fpm. Другие различия незначительны, я думаю, что единственное, что изменилось, - это Ubuntu-> Debian. Та же проблема все еще происходит, за исключением того, что теперь серверу удается немного восстановить после max_execution_time секунд (а затем снова всплескивать).

Я пробовал играть с отдельным test.php, и я не уверен, что это одна и та же проблема, но по крайней мере в top она выглядит одинаково. Я создал test.php и включил кучу библиотек, которые принадлежат нашей структуре. Библиотеки не делают ничего, кроме определения классов или других библиотек, которые определяют классы. Я проверил с APC, и все это успешно обслуживается им. Я начал настаивать на test.php 200 запросов в секунду, и через некоторое время произошло то же самое. За исключением того, что теперь мне удалось получить некоторые ошибки, говорящие "слишком много открытых файлов". Это происходит не всегда, хотя иногда он просто начинает отсчет времени без вывода ошибки, и несколько процессов php застряли, потребляя весь процессор. Я играл с ним немного, но я думаю, что здесь есть корреляция: контролируя количество включенных библиотек или немного меняющихся запросов/второй скорости, я могу контролировать, когда произойдет всплеск CPU. Я увеличил соответствующие переменные ОС, но проблема все еще существует, хотя для этого требуется больше времени (также обратите внимание, что я установил ограничения на значения N раз больше, чем общее количество запросов, которые я выполняю во время тестов).

fs.file-max = 70000
...
*       soft    nofile   10000
*       hard    nofile  30000
...
worker_rlimit_nofile 10000;
...
(reloaded all the configs and made sure the new system vars actually took affect)

Таким образом, следующее лучшее и единственное объяснение, которое я могу придумать до сих пор, заключается в том, что, хотя APC предполагается вытащить файлы из памяти, внутренне он реализован таким образом, который все еще использует дескриптор файла всякий раз, когда вызывается PHP include-s. И либо потому, что он выпускает их с задержкой, либо когда в какой-то неудачный момент слишком много запросов поступает в тот же момент, система запускает наши дескрипторы, а входящие HTTP-запросы быстро складываются в огромную очередь. Я попытаюсь как-то проверить это.

Ответ 1

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

Я бы pm.max_requests = 10000 сокращении pm.max_requests = 10000 к чему-то более разумному, например pm.max_requests = 500. Это означает, что "не используйте каждый экземпляр больше, чем X количество запросов". Хорошо, чтобы этот показатель не был слишком высоким, потому что это дает вам устойчивость в отношении возможных ошибок PHP-движка.

Я думаю, что реальная проблема наиболее вероятна в ваших PHP-скриптах. Трудно сказать, не зная больше.

EDIT: рассмотрите uncommenting ;request_terminate_timeout = 0 и установите его как-то вроде request_terminate_timeout = 20. Затем ваши скрипты будут завершены в течение 20 секунд. Вы, скорее всего, увидите изменение в поведении, но я думаю, что ваш сайт может остаться в живых. Это указывает на ошибку PHP-скрипта.

EDIT2: Моя собственная конфигурация php-fpm выглядит следующим образом:

[example.com]
listen = /var/run/sockets/example.com.socket
user = www-data
group = www-data
pm = dynamic
pm.start_servers = 5
pm.max_children = 15
pm.min_spare_servers = 5
pm.max_spare_servers = 10
pm.max_requests = 500
php_flag[expose_php] = off
php_flag[short_open_tag] = on

EDIT3: я обнаружил что-то неожиданное в вашей конфигурации nginx, но это может быть ничего.

Вы используете fastcgi_ignore_client_abort on; что вызывает проблемы в рабочих процессах в старых версиях nginx. Я сам не видел эту проблему, так как я запускаю собственный компилятор из последней версии. Здесь описание проблемы на сайте nginx:

В 1.0.2 POST-запросы обрабатываются неправильно, если установлено значение fastcgi_ignore_client_abort, что может привести к сбою рабочих процессов. Переключение fastcgi_ignore_client_abort обратно в значение по умолчанию (off) должно решить эту проблему.

Ответ 2

Простой трюк, но очень полезный для сокращения использования процессора до 50%, просто отредактируйте конфигурацию php-fpm:

pm = dynamic

и измените его на:

pm = ondemand

Ответ 3

Я сейчас сталкиваюсь с этой проблемой и хочу указать вам на этот пост:

Как определить, какой сценарий выполняется в процессе PHP-FPM

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

Забавно, это было на сервере, который был безупречно быстрым. Я думаю, что обновление WordPress (плагин или ядро) может быть очень ответственным.

Ответ 4

У меня такая же проблема. Я попробовал переконфигурировать PHP-FPM и NGINX и не очень далеко. Один из наших ребят отключил v8js.php(http://php.net/manual/en/book.v8js.php), и он исправил проблему. Я предлагаю отключить любые php-модули, пока вы не найдете нарушителя спокойствия. Надеюсь, это поможет кому-то.