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

Другие вопросы по тегам