我如何写一个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
291329 次浏览

最简单的方法是使用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()

在shell中,你可以测试它是否正在运行:

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'退出

当进程死亡时,它的所有文件描述符将被关闭,所有锁将被自动移除。

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

您应该使用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在它重新启动时给您发送电子邮件。

看看monit (http://mmonit.com/monit/)。它可以处理脚本的启动、停止和重新启动,并且可以执行健康检查,必要时还可以重新启动。

或者做一个简单的脚本:

while true
do
/your/script
sleep 1
done

避免pid文件、cron或任何其他试图计算不是它们的子进程的文件。

在UNIX中,您只能服侍您的子女是有原因的。任何试图解决这个问题的方法(ps解析、pgrep、存储PID等等)都是有缺陷的。只要说没有

相反,您需要监视您的进程的进程作为进程的父进程。这是什么意思?它意味着只有进程开始你的进程可以可靠地等待它结束。在bash中,这绝对是微不足道的。

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

上面这段bash代码在until循环中运行myserver。第一行以myserver开头,并等待它结束。当它结束时,until检查退出状态。如果退出状态是0,这意味着它优雅地结束了(这意味着你要求它以某种方式关闭,并且它成功地关闭了)。在这种情况下,我们不想重新启动它(我们只是要求它关闭!)。如果退出状态是 0until将运行循环体,在STDERR上发出错误消息并重新启动循环(回到第1行)1秒后

我们为什么要等呢?因为如果myserver的启动序列有问题并且它立即崩溃,你将会有一个非常密集的不断重新启动和崩溃的循环。sleep 1带走了它的压力。

现在你所需要做的就是启动这个bash脚本(可能是异步的),它将监视myserver并在必要时重新启动它。如果你想在引导时启动监视器(使服务器“存活”重启),你可以在你的用户的cron(1)中使用@reboot规则来安排它。用crontab打开你的cron规则:

crontab -e

然后添加一个规则来启动监视器脚本:

@reboot /usr/local/bin/myservermonitor

另外;查看inittab(5)和/etc/inittab。你可以在那里添加一行,让myserver在某个初始化级别开始,并自动重生。


编辑。

让我添加一些关于为什么使用PID文件的信息。虽然它们很受欢迎;它们也有很多缺陷,你没有理由不以正确的方式去做。

考虑一下:

  1. PID回收(杀死错误进程):

    • /etc/init.d/foo 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
    • 李< / ul > < / >
    • PID文件失效。你需要过于复杂(或者我应该说,不平凡)的逻辑来检查PID文件是否过时,任何这样的逻辑都容易受到1.的攻击。

    • 如果您甚至没有写访问权,或者处于只读环境中,该怎么办?

    • 这是毫无意义的过度复杂化;看看上面的例子有多简单。根本没必要把事情搞复杂。

参见:在这样做的时候,pid文件仍然有缺陷'对'?

顺便说一下;永远不要这样做。

  1. ps是非常不可移植的。虽然在几乎所有UNIX系统上都可以找到它;如果你想要非标准输出,它的参数变化很大。标准输出仅供人类使用,而不用于脚本解析!
  2. 解析ps会导致大量误报。以ps aux | grep PID为例,现在想象一下有人在某个地方用一个数字作为参数启动一个进程,这个数字恰好与你启动守护进程时使用的PID相同!想象一下,两个人开始了一个X回合,而你在等待X来杀死你的回合。一切都很糟糕。

如果你不想自己管理这个过程;有一些非常好的系统可以充当您的进程的监控器。例如,查看runit

我已经在许多服务器上成功地使用了以下脚本:

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包含了足够的进程路径,这是完全明确的
  • 在等待进程死亡时使用sleep,避免占用资源:)

这个脚本实际上用于关闭一个正在运行的tomcat实例,我希望在命令行中关闭(并等待)它,因此对我来说,不能将它作为子进程启动。

我不确定它在操作系统之间的可移植性如何,但你可以检查你的系统是否包含'run-one'命令,即。“运行一个人”。 具体来说,这组命令包括'run-one-constant ',这似乎正是所需要的

从手册页:

run-one-constant命令[ARGS]

注意:显然这可以在脚本中调用,但它也完全消除了拥有脚本的需要。

我把它用于我的npm进程

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

在线:

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

如果<your-bash-snippet>失败,将不断重新启动<your-bash-snippet>:如果<your-bash-snippet>优雅地停止(返回代码0), && break将停止循环。

在所有情况下重新启动<your-bash-snippet>:

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

例如# 1

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

例如# 2

while true; do docker logs -f container-name; sleep 2; done
watch "yourcommand"

如果/当进程停止(经过2s延迟),它将重新启动进程。

watch -n 0.1 "yourcommand"

在0.1s后重新启动,而不是默认的2秒

watch -e "yourcommand"

如果程序出现错误退出,则停止重新启动。

优点:

  • 内置命令
  • 一行
  • 易于使用和记忆。

缺点:

  • 只在命令执行完成后在屏幕上显示命令的结果