Как мне написать bash-скрипт для перезапуска процесса, если он умирает?

У меня есть сценарий 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

12 ответов

Решение

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

Есть очень веская причина, почему в 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 снимает напряжение с этого.

Теперь все, что вам нужно сделать, это запустить этот скрипт (вероятно, асинхронно), и он будет контролировать 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, записывать foo PID для /var/run/foo.pid
    • Некоторое время спустя: foo умирает как-то.
    • Некоторое время спустя: любой случайный процесс, который запускается (назовите его bar) берет случайный 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, с которым вы смотрели своего демона! Представьте, что два человека начинают сеанс X, а вы ищете X, чтобы убить свой. Это просто все виды плохого.

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

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

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

while true
do
/your/script
sleep 1
done

В линию:

while true; do <your-bash-snippet> && break; done

например

while true; do openconnect x.x.x.x:xxxx && break; done

Самый простой способ сделать это - использовать 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'

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

      watch "yourcommand"

Он перезапустит процесс, если/когда он остановится (после 2-секундной задержки).

      watch -n 0.1 "yourcommand"

Чтобы перезапустить его через 0,1 с вместо 2 секунд по умолчанию

      watch -e "yourcommand"

Чтобы остановить перезапуски, если программа завершается с ошибкой.

Преимущества:

  • встроенная команда
  • одна линия
  • легко использовать и запомнить.

Недостатки:

  • Отображать результат команды на экране только после ее завершения

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

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

проверить процесс checkqueue.py с помощью pidfile /var/run/checkqueue.pid
       если изменен pid, то exec "checkqueue_restart.sh"

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

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

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

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

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

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

Я использую это для своего процесса npm

#!/bin/bash
for (( ; ; ))
do
date +"%T"
echo Start Process
cd /toFolder
sudo process
date +"%T"
echo Crash
sleep 1
done

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

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, это гораздо более согласованно для всех дистрибутивов, чем PS
  • $INSTALLATION содержит достаточно пути процесса, это совершенно однозначно
  • Используйте режим сна, ожидая, пока процесс умрет, избегайте использования ресурсов:)

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

      while true; do; pgrep -f 'htop' >/dev/null && echo 'OK' || (htop& echo 'Restart'); sleep 5; done

htop- пример команды.

htop&- работать в фоновом режиме.

>/dev/null- не отображать PID.

sleep 5- интервал для проверки того, что процесс все еще работает. В примере 5 секунд.

Вы также можете использовать docker с python + ваш скрипт на python, с опцией: --restart, например:

Докер запускает --rm -ti --restart, если не остановлен jfloff/alpine-python python yourscript.py

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