Назад | Перейти на главную страницу

Как я могу уберечь мой сервер от зомби из этого задания Ubuntu cron по удалению сессий 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

По какой-то причине (в настоящее время я предполагаю, что веб-сканер с плохим поведением создает новый сеанс по каждому запросу, но я все еще просматриваю журналы), иногда в /var/lib/php/, и когда этот сценарий запускается, он с радостью порождает новый процесс фьюзера для каждого из них. Это быстро достигает предела процесса и заставляет ползать.

Что я могу сделать, кроме простого удаления этого задания cron и очистки вручную?

У меня тоже эта проблема убунту 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

Это убунту 11.04 cron для php.

Вероятно, было бы лучше перенести логику с 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 минуты, что было бы очень долгое время для загрузки страницы) ничего не делая, это не должно уничтожать текущие сеансы.

Исправьте сценарий, чтобы он ждал своих дочерних элементов или игнорировал SIG_CHILD. Вы можете поместить сценарий туда, где мы его увидим?

Обновить: Похоже, вы запускаете ошибку в find!

Я решил это для клиента, переместив сеансы из файловой системы в memcache. У них не было зомби-процессов, но все же были миллионы сессий, которые cronjob не мог уследить за удалением. На установку memcache, перенастройку php.ini, тестирование и добавление нескольких графиков munin, чтобы посмотреть размер memcache, потребовалось около 10 минут. 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, причем последний даже сам исправляет термоэлемент!