Как мне написать bash-скрипт для перезапуска процесса, если он умирает? - PullRequest
204 голосов
/ 30 марта 2009

У меня есть скрипт на python, который будет проверять очередь и выполнять действия с каждым элементом:

# checkqueue.py
while True:
  check_queue()
  do_something()

Как мне написать bash-скрипт, который проверит, запущен ли он, и если нет, запустите его. Примерно следующий псевдокод (или, может быть, он должен что-то вроде ps | grep?):

# keepalivescript.sh
if processidfile exists:
  if processid is running:
     exit, all ok

run checkqueue.py
write processid to processidfile

Я позвоню из crontab:

# crontab
*/5 * * * * /path/to/keepalivescript.sh

Ответы [ 7 ]

578 голосов
/ 30 марта 2009

Избегайте PID-файлов, крон или чего-либо еще, что пытается оценить процессы, которые не являются их дочерними элементами.

Существует очень веская причина, почему в UNIX вы можете ТОЛЬКО ждать своих детей. Любой метод (ps parsing, pgrep, хранение PID, ...), который пытается обойти проблему, имеет недостатки и имеет зияющие дыры в нем. Просто скажи нет .

Вместо этого вам нужен процесс, который контролирует ваш процесс, чтобы быть его родителем. Что это значит? Это означает, что только процесс, который запускает , ваш процесс может надежно ожидать его завершения. В bash это абсолютно тривиально.

until myserver; do
    echo "Server 'myserver' crashed with exit code $?.  Respawning.." >&2
    sleep 1
done

Приведенный выше фрагмент кода bash запускается myserver в цикле until. Первая строка начинается с myserver и ожидает окончания. Когда он заканчивается, until проверяет его статус выхода. Если статус выхода - 0, это означает, что он завершился изящно (что означает, что вы попросили его как-то отключиться, и он сделал это успешно). В этом случае мы не хотим перезапускать его (мы просто попросили его закрыть!). Если состояние выхода - , а не 0, until запустит тело цикла, которое выдает сообщение об ошибке на STDERR и перезапускает цикл (обратно к строке 1) через 1 секунду .

Почему мы ждем секунду? Потому что, если что-то не так с последовательностью запуска myserver и она сразу падает, у вас будет очень интенсивный цикл постоянного перезапуска и сбоя в ваших руках. sleep 1 снимает с этого напряжение.

Теперь все, что вам нужно сделать, это запустить этот bash-скрипт (возможно, асинхронно), и он будет отслеживать myserver и перезапускать его при необходимости. Если вы хотите запустить монитор при загрузке (после перезагрузки сервера), вы можете запланировать его в cron (1) вашего пользователя с правилом @reboot. Откройте свои правила cron с помощью crontab:

crontab -e

Затем добавьте правило для запуска сценария монитора:

@reboot /usr/local/bin/myservermonitor

* * В качестве альтернативы одна тысяча тридцать восемь; посмотрите на inittab (5) и / etc / inittab. Вы можете добавить туда строку, чтобы myserver начинался с определенного уровня инициации и автоматически появлялся.

Редактировать.

Позвольте мне добавить информацию о том, почему не для использования файлов PID. Пока они очень популярны; они также очень несовершенны, и нет никаких причин, по которым вы просто не сделали бы это правильно.

Учтите это:

  1. Переработка ПИД-регулятора (уничтожение неправильного процесса):

    • /etc/init.d/foo start: начало foo, запись PID foo в /var/run/foo.pid
    • Некоторое время спустя: foo как-то умирает.
    • Некоторое время спустя: любой случайный процесс, который запускается (назовите его bar), принимает случайный PID, представьте, что он берет старый PID foo.
    • Вы замечаете, что foo ушел: /etc/init.d/foo/restart читает /var/run/foo.pid, проверяет, живо ли оно, находит bar, думает, что это foo, убивает его, запускает новый foo.
  2. Файлы PID устарели. Вам нужна слишком сложная (или я должен сказать, нетривиальная) логика, чтобы проверить, не устарел ли файл PID, и любая такая логика снова уязвима для 1..

  3. Что если у вас даже нет прав на запись или вы находитесь в среде только для чтения?

  4. Это бессмысленное чрезмерное усложнение; Посмотрите, насколько простой мой пример выше. Нет необходимости усложнять это.

См. Также: По-прежнему ли некорректны PID-файлы при правильной работе?

Кстати; разбирается даже хуже, чем PID-файлы ps! Никогда не делай этого.

  1. ps очень непереносимо. В то время как вы найдете его почти в каждой системе UNIX; его аргументы сильно различаются, если вы хотите нестандартный вывод. И стандартный вывод предназначен ТОЛЬКО для потребления человеком, а не для синтаксического анализа!
  2. Синтаксический анализ ps приводит к МНОГИМ ложных срабатываний. Возьмите пример ps aux | grep PID, и теперь представьте, что кто-то начинает процесс с номером где-то в качестве аргумента, который совпадает с PID, с которым вы смотрели своего демона! Представьте двух человек, начинающих сеанс Х, и вы хотите, чтобы Х убил ваш. Это просто все виды плохих.

Если вы не хотите сами управлять процессом; Есть несколько совершенно хороших систем, которые будут выполнять функции мониторинга ваших процессов. Посмотрите, например, runit .

23 голосов
/ 30 марта 2009

Посмотрите на monit (http://mmonit.com/monit/). Он обрабатывает запуск, остановку и перезапуск вашего скрипта и может выполнять проверки работоспособности и перезапуски при необходимости.

Или сделать простой скрипт:

while true
do
/your/script
sleep 1
done
8 голосов
/ 30 марта 2009

Самый простой способ сделать это - использовать flock on file. В скрипте Python вы бы сделали

lf = open('/tmp/script.lock','w')
if(fcntl.flock(lf, fcntl.LOCK_EX|fcntl.LOCK_NB) != 0): 
   sys.exit('other instance already running')
lf.write('%d\n'%os.getpid())
lf.flush()

В оболочке вы можете проверить, работает ли она:

if [ `flock -xn /tmp/script.lock -c 'echo 1'` ]; then 
   echo 'it's not running'
   restart.
else
   echo -n 'it's already running with PID '
   cat /tmp/script.lock
fi

Но, конечно, вам не нужно тестировать, потому что, если он уже запущен и вы перезапустите его, он завершится с 'other instance already running'

Когда процесс умирает, все его файловые дескрипторы закрываются и все блокировки автоматически снимаются.

6 голосов
/ 30 марта 2009

Вы должны использовать monit, стандартный инструмент Unix, который может отслеживать различные вещи в системе и реагировать соответствующим образом.

Из документов: http://mmonit.com/monit/documentation/monit.html#pid_testing

check process checkqueue.py with pidfile /var/run/checkqueue.pid
       if changed pid then exec "checkqueue_restart.sh"

Вы также можете настроить monit для отправки вам электронных писем при перезагрузке.

5 голосов
/ 30 марта 2009
if ! test -f $PIDFILE || ! psgrep `cat $PIDFILE`; then
    restart_process
    # Write PIDFILE
    echo $! >$PIDFILE
fi
2 голосов
/ 22 октября 2018

Я не уверен, насколько он переносим между операционными системами, но вы можете проверить, содержит ли ваша система команду «run-one», то есть «man run-one». В частности, этот набор команд включает в себя «run-one-постоянно», что, кажется, именно то, что нужно.

со страницы руководства:

Run-One-постоянно КОМАНДА [ARGS]

Примечание: очевидно, что это можно вызвать из вашего скрипта, но это также устраняет необходимость иметь скрипт вообще.

1 голос
/ 24 мая 2010

Я с большим успехом использовал следующий скрипт на многих серверах:

pid=`jps -v | grep $INSTALLATION | awk '{print $1}'`
echo $INSTALLATION found at PID $pid 
while [ -e /proc/$pid ]; do sleep 0.1; done

Примечания:

  • Он ищет процесс Java, поэтому я можно использовать jps, это гораздо больше в соответствии с распределениями, чем пс
  • $INSTALLATION содержит достаточно пути процесса, что совершенно однозначно
  • Используйте режим сна, ожидая, пока процесс умрет, избегайте чрезмерного использования ресурсов:)

Этот сценарий фактически используется для закрытия запущенного экземпляра tomcat, который я хочу завершить (и ждать) в командной строке, поэтому запуск его как дочернего процесса просто не подходит для меня.

...