Как написать bash script, чтобы перезапустить процесс, если он умирает?

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

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

Как написать bash script, который будет проверять, запущен ли он, а если нет, запустите его. Примерно следующий псевдокод (или, может быть, он должен сделать что-то вроде 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

Ответ 1

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

Есть очень веская причина, почему в UNIX вы можете ТОЛЬКО ждать своих детей. Любой метод (разбор ps, 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 script (асинхронно, возможно), и он будет отслеживать myserver и при необходимости перезапускать его. Если вы хотите запустить монитор при загрузке (перезагрузка сервера "выжить" ), вы можете запланировать его в своем пользовательском cron (1) с помощью правила @reboot. Откройте свои правила cron с помощью crontab:

crontab -e

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

@reboot /usr/local/bin/myservermonitor

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


Изменить.

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

Рассмотрим это:

  • Повторная утилизация ПИД (уничтожение неправильного процесса):

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

  • Что делать, если вы даже не имеете права на запись или находитесь в среде только для чтения?

  • Это бессмысленное превышение; посмотрим, насколько прост в моем примере выше. Не нужно вообще усложнять это.

См. также: Являются ли PID файлы по-прежнему ошибочными, когда делаете это правильно?

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

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

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

Ответ 2

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

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

while true
do
/your/script
sleep 1
done

Ответ 3

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

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 not running'
   restart.
else
   echo -n 'it already running with PID '
   cat /tmp/script.lock
fi

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

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

Ответ 4

Вы должны использовать 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

if ! test -f $PIDFILE || ! psgrep `cat $PIDFILE`; then
    restart_process
    # Write PIDFILE
    echo $! >$PIDFILE
fi

Ответ 6

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

С man-страницы:

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

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

Ответ 7

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

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 содержит достаточно пути процесса, что он абсолютно однозначный
  • Используйте сон во время ожидания процесса, чтобы избежать зависания ресурсов:)

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