Как я могу предотвратить засорение моего сервера зомби из этого задания cron в Ubuntu для удаления сеансов PHP?
Я недавно заметил, когда вошел в систему, что у меня было несколько тысяч процессов, помеченных как "зомби". После дальнейшего расследования я обнаружил следующее из ps fax
:
701 ? Ss 0:28 cron
3363 ? S 0:00 \_ CRON
3364 ? Ss 0:00 \_ /bin/sh -c [ -x /usr/lib/php5/maxlifetime ] && [ -d /var/lib/php5 ] && find /var/lib/php5/ -depth -mindepth 1 -maxdepth 1 -type f -cmin +$(/usr/lib/php5/maxlifetime) ! -execdir fuser -s {} 2>/dev/null \; -delete
3371 ? S 0:00 \_ find /var/lib/php5/ -depth -mindepth 1 -maxdepth 1 -type f -cmin +24 ! -execdir fuser -s {} ; -delete
3451 ? S 0:02 \_ fuser -s ./sess_jns5af2mvm81e2fg1rbuctlt54
3452 ? Z 0:00 \_ [fuser] <defunct>
3453 ? Z 0:00 \_ [fuser] <defunct>
3454 ? Z 0:00 \_ [fuser] <defunct>
... many, many lines omitted ...
13642 ? Z 0:00 \_ [fuser] <defunct>
Насколько я могу сказать, это сценарий в /etc/cron.d/php
это должно очистить мертвые сессии PHP в 10 и 40 минут после часа.
Изменить: вот текст сценария. Он установлен по умолчанию с PHP на Ubuntu.
# /etc/cron.d/php5: crontab fragment for php5
# This purges session files older than X, where X is defined in seconds
# as the largest value of session.gc_maxlifetime from all your php.ini
# files, or 24 minutes if not defined. See /usr/lib/php5/maxlifetime
# Look for and purge old sessions every 30 minutes
09,39 * * * * root [ -x /usr/lib/php5/maxlifetime ] && [ -d /var/lib/php5 ] && find /var/lib/php5/ -depth -mindepth 1 -maxdepth 1 -type f -cmin +$(/usr/lib/php5/maxlifetime) ! -execdir fuser -s {} 2>/dev/null \; -delete
По какой-то причине (в настоящее время я предполагаю, что веб-сканер с плохим поведением создает новый сеанс при каждом запросе, но я все еще просматриваю логи), иногда в сеансах php оставляют много тысяч /var/lib/php/
и когда этот скрипт будет запущен, он с радостью вызовет новый процесс fuser для каждого из них. Это быстро достигает предела процесса и приводит к ползанию.
Что я могу сделать, кроме простого удаления этого задания cron и очистки вещей вручную?
5 ответов
Вероятно, было бы лучше переместить логику из find
к сценарию, который просматривает все файлы в командной строке, чтобы увидеть, к ним ли обращаются, и если нет, удалите их:
#!/bin/bash
for x; do
if ! /bin/fuser -s "$x" 2>/dev/null; then
rm "$x"
fi
done
Затем измените работу cron на
09,39 * * * * root [ -x /usr/lib/php5/maxlifetime ] && [ -d /var/lib/php5 ] && find /var/lib/php5/ -depth -mindepth 1 -maxdepth 1 -type f -cmin +$(/usr/lib/php5/maxlifetime) -execdir thatscript.sh {} +
Это будет иметь find
собрать все файлы сессий, соответствующие максимальному возрасту, затем запустить thatscript.sh
со всеми из них одновременно (из-за +
вместо ;
). Затем скрипт отвечает за то, чтобы убедиться, что файл не используется, и за его удаление. Сюда, find
должен иметь только одного непосредственного ребенка, и у bash не должно быть проблем с очисткой fuser
а также rm
дети.
От find
В документах неясно, найдет ли find автоматически разделит список имен файлов на несколько исполнений, если они превысят пределы оболочки / ОС (и 13000 файлов могут сделать это... более старые версии bash имели предел аргумента командной строки по умолчанию где-то около 5000) В этом случае вы можете изменить -execdir thatscript.sh {} +
в -print0 | xargs -0 thatscript.sh
иметь xargs
разделить файлы.
В качестве альтернативы, если у вас не установлен диск noatime
, менять -cmin
в -amin
и полностью бросить тесты:
09,39 * * * * root [ -x /usr/lib/php5/maxlifetime ] && [ -d /var/lib/php5 ] && find /var/lib/php5/ -depth -mindepth 1 -maxdepth 1 -type f -amin +$(/usr/lib/php5/maxlifetime) -delete
Это удалит все файлы сеанса, к которым последний раз обращались больше, чем [выходные данные maxlifetime
команда] минут назад. Пока у вас нет никаких php-процессов, которые открывают сеанс, а затем сидят без дела долго (по умолчанию для этого maxlifetime в Debian кажется 24 минуты, что было бы очень долгим временем для загрузки страницы), ничего не делая, это не должно уничтожать любые сеансы, используемые в настоящее время.
У меня есть эта проблема также на Ubuntu 11.10, и я решил эту проблему путем редактирования:
/etc/cron.d/php5
и замените код на:
09,39 * * * * root [ -x /usr/lib/php5/maxlifetime ] && [ -d /var/lib/php5 ] && find /var/lib/php5/ -depth -mindepth 1 -maxdepth 1 -type f -cmin +$(/usr/lib/php5/maxlifetime) -delete
Это задание Ubuntu 11.04 cron для php.
Исправьте скрипт так, чтобы он ожидал своих потомков или игнорировал SIG_CHILD. Можете ли вы поставить сценарий где-нибудь, чтобы мы могли его увидеть?
Обновление: похоже, вы вызываете ошибку в find
!
Я решил это для клиента, переместив сеансы из файловой системы в memcache. У них не было процессов зомби, но все еще были миллионы сессий, которые cronjob не мог продолжать удалять. Потребовалось около 10 минут, чтобы установить memcache, перенастроить php.ini, протестировать его и добавить несколько графиков munin, чтобы посмотреть размер memcache. Presto - нагрузка на сервер снизилась, все довольны.
http://www.dotdeb.org/2008/08/25/storing-your-php-sessions-using-memcached/ http://www.ducea.com/2009/06/02/php-sessions-in-memcached/
Это будет также очень полезно:
https://bugs.launchpad.net/ubuntu/+source/php5/+bug/876387
Прочтите комментарии № 4 и № 8, причем последний даже сам исправляет фьюзер!