Elastic Beanstalk 上的 Django 中的定期任务(可能使用 celery beat)

Posted

技术标签:

【中文标题】Elastic Beanstalk 上的 Django 中的定期任务(可能使用 celery beat)【英文标题】:Periodic tasks in Django on Elastic Beanstalk (possibly with celery beat) 【发布时间】:2016-04-20 03:35:45 【问题描述】:

我正在尝试在 Elastic Beanstalk 上为我的 Django 应用程序设置一项日常任务。似乎没有一种可接受的设置方式,因为 celery beat 是 Django 中定期任务的首选解决方案,但不适用于负载平衡环境。

我见过一些解决方案,例如使用 leader_only=True 设置 celery beat,只运行一个实例,但这会留下单点故障。我已经看到其他解决方案允许许多 celery beat 实例并使用锁来确保只有一个任务通过,但是除非重新启动失败的实例,否则这最终不会完全失败吗?我看到的另一个建议是有一个单独的实例来运行 celery beat,但这仍然是一个问题,除非它有某种方式在失败时自行重新启动。

这个问题有什么好的解决办法吗?我宁愿不必照看调度程序,因为很容易注意到我的任务直到一段时间后才运行。

【问题讨论】:

在领导实例上运行有什么问题?如果该实例未通过健康检查,则会提升另一个实例。 如果 celerybeat 失败,我将如何使健康检查失败? 如果您非常担心故障点并且您正在执行多实例负载平衡,那么也许是时候考虑设置专用的日志记录/监控机器或购买服务了。亚马逊提供一个。我喜欢哨兵。 您的问题能得到很好的答案吗?您是否发现在 aws 上运行带有 cron 的管理命令比设置 celery 更可取?? 【参考方案1】:

如果您使用 redis 作为代理,请考虑安装 RedBeat 作为 celery beat 调度程序:https://github.com/sibson/redbeat

此调度程序使用 redis 中的锁定来确保只有一个节拍实例正在运行。有了这个,您可以在每个节点的工作进程上启用节拍并删除leader_only=True 的使用。

celery worker -B -S redbeat.RedBeatScheduler

假设您有带节拍锁的工人 A 和工人 B。如果工人 A 死亡,工人 B 将在 configurable 一段时间后尝试获取节拍锁。

【讨论】:

【参考方案2】:

我建议创建一个使用 cron 运行的 management command。

使用此方法,您可以使用完整的 Django ORM、所有方法等。将您的脚本包装在 try/except 中,您可以选择以任何您希望的方式记录故障 - 电子邮件通知、外部日志系统(如 Sentry)、直接记录到数据库等。

我使用 supervisord 来运行 cron,它运行良好。它依赖于久经考验的工具,不会让您失望。

最后,使用数据库单例来跟踪批处理作业是否已运行或当前正在运行的环境中运行多个 Django 实例并以负载平衡方式运行,这不是一个坏做法,即使您感觉有点讨厌它。 DB 是一种非常可靠的方法,可以告诉您 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

【讨论】:

以上是关于Elastic Beanstalk 上的 Django 中的定期任务(可能使用 celery beat)的主要内容,如果未能解决你的问题,请参考以下文章