使用supervisor作为CRON

48

有没有办法配置supervisor每隔X秒运行一次某个命令(类似于CRON)?

我看到了使用事件监听器和TICK_事件的示例。

[eventlistener:memmon]
command=memmon -a 200MB -m bob@example.com
events=TICK_60

但它只运行一次命令。


答案是否定的,Supervisord 无法像 Cron 一样运行作业。Events 不具备您想要的功能。 - aaa90210
5个回答

29

为什么要重复发明轮子呢?你可以同时使用 cronsupervisord

在 supervisord 中创建一个任务,使其具有 autostart=false

在 cron 中使用 * * * * * supervisorctl start <taskname> 每分钟启动该任务


Alex,按照你的解决方案,我们如何管理任务运行时间表? - barbushin
这有什么优势,而不是只使用cron吗? - Jimmy T.
@JimmyT。是的,如果您需要一些supervisord功能(如日志记录、执行配置等)。 - pumbo
这并不是重复造轮子。如果您有一个运行应用程序的容器(在supervisord下),您可能希望周期性函数由supervisord本身管理(从而避免在容器内还需要运行“crond”)。 - FelipeFR
Cron 不是一个好的出发点。特别是在 Docker 容器中,它对于诸如 envs、日志和关闭等琐碎问题存在各种问题。它甚至不能执行每分钟以上的任务。我希望它永远消失。 - Gherman

27

问题

正如您在memmon示例中所看到的那样,supervisord并未在每个事件上执行memmon -a 200MB -m bob@example.com。相反,它会启动此事件侦听器一次(或者如果您配置了一个池,则可能会启动几次),然后将每个新事件发送到现有进程的标准输入。

解决方案

因此,确实需要为要在事件上触发的每种其他类型的功能找到或编写与supervisor兼容的事件侦听器。

实现示例方法

设置配置并编写侦听器

编写supervisord.cfg事件部分

[eventlistener:passthru]
command=/tmp/simple.py /bin/date -u +"%%s %%S:%%H:%%d:%%m"
events=TICK_60

(注意- 对于configParser进行转义的%)

编写一个简单的.py事件监听器

通过对文档中示例监听器进行更改,创建此简单的.py监听器,以便使用其第一个参数和任何剩余参数执行:

#! /usr/bin/python
import sys
import subprocess

def write_stdout(s):
    sys.stdout.write(s)
    sys.stdout.flush()

def write_stderr(s):
    sys.stderr.write(s)
    sys.stderr.flush()

def main(args):
    while 1:
        write_stdout('READY\n') # transition from ACKNOWLEDGED to READY
        line = sys.stdin.readline()  # read header line from stdin
        write_stderr(line) # print it out to stderr
        headers = dict([ x.split(':') for x in line.split() ])
        data = sys.stdin.read(int(headers['len'])) # read the event payload
        res = subprocess.call(args, stdout=sys.stderr); # don't mess with real stdout
        write_stderr(data)
        write_stdout('RESULT 2\nOK') # transition from READY to ACKNOWLEDGED

if __name__ == '__main__':
    main(sys.argv[1:])
    import sys

确保主管配置正常工作

$ supervisorctl [-c cfg]
supervisor> status
passthru                         RUNNING   pid 4471, uptime 0:00:32
supervisor> tail passthru
  OKREADY
  RESULT 2
  OKREADY
  ...
supervisor> tail passthru stderr
supervisor> tail passthru stderr
  ver:3.0 server:supervisor serial:0 pool:passthru poolserial:0 eventname:TICK_60 len:15
  1451411161 01:17:29:12 <--- output
  when:1451411160ver:3.0 server:supervisor serial:1 pool:passthru poolserial:1 eventname:TICK_60 len:15
  1451411220 00:17:29:12 <--- output
  when:1451411220

现在date -u +"%s %S:%H:%d:%m"每60秒运行一次。

替换所需的命令

创建一个可执行脚本

/tmp/hiworld.php:

#! /usr/bin/php
<?= "hiya\n"; 

(chmod +x ...)

这段代码用于给文件添加可执行权限。

在supervisord.cfg中更改监听器的参数

需要更改supervisord.cfg文件中监听器的参数。
[eventlistener:passthru]
command=/tmp/simple.py /tmp/hiworld.php
;stdout_logfile=/tmp/passthru 
events=TICK_60
;autorestart=true
;startsecs=0

重新加载supervisord并测试 (重新读取似乎无法检测到此更改)
supervisor> reload
   Really restart the remote supervisord process y/N? y
   Restarted supervisord
supervisor> status
   passthru                         RUNNING   pid 6017, uptime 0:00:10
supervisor> tail passthru stderr
supervisor> status
   passthru                         RUNNING   pid 6017, uptime 0:00:21
supervisor> status
   passthru                         RUNNING   pid 6017, uptime 0:01:01
supervisor> tail passthru stderr
   ver:3.0 server:supervisor serial:316 pool:passthru poolserial:0 eventname:TICK_60 len:15
    hiya
   when:1418926740
supervisor> 

结束

现在,所需的命令每60秒运行一次。 现在您可以调整权限、位置、日志等具体内容。


我尝试了 command=python /home/supervisor.py "php -f /home/test.php",但它不起作用。我的意思是 supervisorctl 显示它正在运行,但 test.php 从未被调用。你能提供一些可行的例子吗? - barbushin
在我的例子中,我会将 command=/tmp/simple.py /bin/date 改为 command=/tmp/simple.py /home/test.php,并在 test.php 的顶部加上 #! /.../php。但是,删除引号并使用 php 的完整路径也可能有效。 - lossleader
它可以正常工作,使用一个命令及其参数,其中命令是第一个参数。正如我在上一条评论中所说,您试图引用一个命令及其参数,这将生成一个不可执行的单个参数,如果您坚持使用引号,则必须使用shell来解除引号,例如command= listener mycommand arg1 arg2command=listener /bin/sh -c "mycommand arg1 arg2" 是有意义的,但 command=listener "mycommand arg1 arg2" 不是。 - lossleader
我仍然无法使用包含参数的命令使其工作。您能否更新您的答案,提供一些包含参数的命令? - barbushin
@barbushin,我已经为日期添加了一些参数,展示了我所能想到的唯一转义问题。 - lossleader

20

Supervisor不容易支持这个功能。

但是为了实现你的目标,你可以使用supervisor来启动cron(例如用于docker容器):

https://gist.github.com/martinrusev/7015e393d46647dbad15

在docker中安装cron(如debian类docker中的apt-get install cron

在supervisor配置中:

[program:cron]
command=cron -f -L 15
autostart=true
autorestart=true

-f 是指后台运行,-L 15 是为了输出所有 cron 日志。

然后使用用户 crontab、全局的 /etc/crontab 或任何 crontab 特殊目录(如 /etc/cron.hourly, /etc/cron.daily 等)。


StackOverflow要求答案必须在该网站上显示,而不是外部网站。我已经为你的回答点赞了,但对于其他人的情况,我编辑了你的回答。 - Cyrille Pontvieux
这是对于Docker容器和Supervisor最适合我的最佳答案。 - Mohamed Ali Nakouri

14

你可以调用bash的sleep命令:

[program:mycmd]
command=bash -c 'sleep 300 && exec <your command here>'
autorestart=true

这将每5分钟运行您的命令。不要忘记exec部分,以替换bash进程并使supervisor获取正确的退出代码。


2
可能是最简单的cronjob解决方案,但您错过了autorestart选项或while循环以便再次运行它! 所以:选项A) [program:cleanup] command=bash -c 'date >> /var/log/prueba.log && sleep 5' autorestart=true选项B) command=bash -c 'while true; do date >> /var/log/prueba.log; sleep 5; done' autorestart=true - Gonzalo Cao
如果你想在启动时运行它,然后再等待,只需交换 sleep 和 exec - exec <your command here> && sleep 300 - Tomáš Fejfar

2
你可以使用crobtab来管理和调度你的supervisor程序。
使用命令supervisorctl start <program_name> 注意:这将只启动一个supervisor程序实例。如果它已经在运行,而crobtab尝试再次触发它,supervisorctl start命令将不会启动一个新的实例。

这已经是将近一年前回答的一部分了 - 你还想补充些什么吗? - Nico Haase
4
是的,cron 只能在任何给定时间运行 supervisor 程序的一个实例。即使在 supervisor 程序仍在运行时触发了 cron,cron 也无法启动新实例。 - Raman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接