Как мне написать 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. Пока они очень популярны; они также очень несовершенны, и нет никаких причин, по которым вы бы просто не сделали это правильно.
Учти это:
Утилизация ПИД (убивает неправильный процесс):
/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
,
PID файлы устарели. Вам нужна слишком сложная (или я должен сказать, нетривиальная) логика, чтобы проверить, не устарел ли файл PID, и любая ли такая логика снова уязвима для
1.
,Что если у вас даже нет прав на запись или вы находитесь в среде только для чтения?
Это бессмысленное чрезмерное усложнение; Посмотрите, насколько простой мой пример выше. Нет необходимости усложнять это вообще.
Смотрите также: PID-файлы все еще имеют недостатки, когда делают это "правильно"?
Кстати; разбирается даже хуже чем PID файлы ps
! Никогда не делай этого.
ps
очень непереносимо. В то время как вы найдете его почти в каждой системе UNIX; его аргументы сильно различаются, если вы хотите нестандартный вывод. И стандартный вывод предназначен ТОЛЬКО для потребления человеком, а не для синтаксического анализа!- анализ
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