我试图在Elastic Beanstalk上为我的Django应用程序设置日常任务。似乎没有一种可接受的方式来设置它,因为芹菜节拍是Django中定期任务的首选解决方案,但对于负载均衡的环境来说并不是很好。
我已经看到一些解决方案,例如使用leader_only = True设置芹菜节拍,只运行一个实例,但这会留下单点故障。我已经看到其他解决方案允许芹菜的许多实例击败并使用锁来确保只有一个任务通过,但除非失败的实例重新启动,否则这最终仍会完全失败?我已经看到的另一个建议是有一个单独的实例来运行芹菜节拍,但这仍然是一个问题,除非它有一些方法在它失败时重新启动。
这个问题有没有合适的解决方案?我宁愿不必照看调度程序,因为很容易注意到我的任务直到一段时间才运行。
答案 0 :(得分:1)
如果你使用Redis的作为你的经纪人,查看安装RedBeat如芹菜节拍调度:https://github.com/sibson/redbeat
此调度程序使用锁定Redis来确保仅运行一个Beat实例。借助此功能,您可以在每个节点的工作进程上启用心跳,并删除对leader_only=True
的使用。
celery worker -B -S redbeat.RedBeatScheduler
假设您有一个具有节拍锁定功能的工人A和一个工人B。如果工人A死亡,则工人B将在configurable时间后尝试获取该节拍锁定。
答案 1 :(得分:0)
我建议制作一个与cron一起运行的management command。
使用此方法,您可以使用完整的Django ORM,所有方法等。在try / except中包装脚本,您可以选择以任何方式记录故障 - 电子邮件通知,Sentry等外部日志系统,直接到数据库等。
我使用supervisord来运行cron并且运行良好。它依赖于经过时间考验的工具,不会让你失望。
最后,使用数据库单例来跟踪批处理作业是否已经运行或者当前在多个Django实例运行负载均衡的环境中运行并不是一种不好的做法,即使你觉得有点icky。 DB是一种非常可靠的方法,可以告诉您是否正在处理数据库。
关于cron的一个令人烦恼的事情是它没有导入Django可能需要的环境变量。我用一个简单的Python脚本解决了这个问题。
它在启动时写入crontab,包含所需的环境变量等。这个例子适用于EBS上的Ubuntu,但应该是相关的。
#!/usr/bin/env python
# run-cron.py
# sets environment variable crontab fragments and runs cron
import os
from subprocess import call
from master.settings import IS_AWS
# read django's needed environment variables and set them in the appropriate crontab fragment
eRDS_HOSTNAME = os.environ["RDS_HOSTNAME"]
eRDS_DB_NAME = os.environ["RDS_DB_NAME"]
eRDS_PASSWORD = os.environ["RDS_PASSWORD"]
eRDS_USERNAME = os.environ["RDS_USERNAME"]
try:
eAWS_STAGING = os.environ["AWS_STAGING"]
except KeyError:
eAWS_STAGING = None
try:
eAWS_PRODUCTION = os.environ["AWS_PRODUCTION"]
except KeyError:
eAWS_PRODUCTION = None
eRDS_PORT = os.environ["RDS_PORT"]
if IS_AWS:
fto = '/etc/cron.d/stortrac-cron'
else:
fto = 'test_cron_file'
with open(fto,'w+') as file:
file.write('# Auto-generated cron tab that imports needed variables and runs a python script')
file.write('\nRDS_HOSTNAME=')
file.write(eRDS_HOSTNAME)
file.write('\nRDS_DB_NAME=')
file.write(eRDS_DB_NAME)
file.write('\nRDS_PASSWORD=')
file.write(eRDS_PASSWORD)
file.write('\nRDS_USERNAME=')
file.write(eRDS_USERNAME)
file.write('\nRDS_PORT=')
file.write(eRDS_PORT)
if eAWS_STAGING is not None:
file.write('\nAWS_STAGING=')
file.write(eAWS_STAGING)
if eAWS_PRODUCTION is not None:
file.write('\nAWS_PRODUCTION=')
file.write(eAWS_PRODUCTION)
file.write('\n')
# Process queue of gobs
file.write('\n*/8 * * * * root python /code/app/manage.py queue --process-queue')
# Every 5 minutes, double-check thing is done
file.write('\n*/5 * * * * root python /code/app/manage.py thing --done')
# Every 4 hours, do this
file.write('\n8 */4 * * * root python /code/app/manage.py process_this')
# etc.
file.write('\n3 */4 * * * root python /ode/app/manage.py etc --silent')
file.write('\n\n')
if IS_AWS:
args = ["cron","-f"]
call(args)
在supervisord.conf中:
[program:cron]
command = python /my/directory/runcron.py
autostart = true
autorestart = false