AWS Elastic Beanstalk,运行一个cronjob

我想知道是否有办法设置一个cronjob /任务执行每一分钟。 目前我的任何实例都应该能够运行这个任务。

这是我试图做的configuration文件没有成功:

container_commands: 01cronjobs: command: echo "*/1 * * * * root php /etc/httpd/myscript.php" 

我不确定这是否是正确的方法

有任何想法吗?

这就是我向Elastic Beanstalk添加cron作业的方法:

在应用程序的根目录创build一个名为.ebextensions的文件夹,如果它不存在的话。 然后在.ebextensions文件夹中创build一个configuration文件。 出于说明目的,我将使用example.config。 然后将此添加到example.config

 container_commands: 01_some_cron_job: command: "cat .ebextensions/some_cron_job.txt > /etc/cron.d/some_cron_job && chmod 644 /etc/cron.d/some_cron_job" leader_only: true 

这是Elastic Beanstalk的YAMLconfiguration文件。 确保将其复制到文本编辑器中时,文本编辑器将使用空格而不是制表符。 否则,当你将它推送到EB时,你会得到一个YAML错误。

所以这个是创build一个名为01_some_cron_job的命令。 命令按字母顺序运行,因此01确保它作为第一个命令运行。

然后该命令将获取名为some_cron_job.txt的文件的内容,并将其添加到/etc/cron.d中名为some_cron_job的文件中。

该命令然后更改/etc/cron.d/some_cron_job文件的权限。

leader_only键确保该命令仅在被认为是领导者的ec2实例上运行。 而不是运行在您可能正在运行的每个ec2实例上。

然后在.ebextensions文件夹内创build一个名为some_cron_job.txt的文件。 你将把你的cron作业放在这个文件中。

举个例子:

 # The newline at the end of this file is extremely important. Cron won't run without it. * * * * * root /usr/bin/php some-php-script-here > /dev/null 

因此,这个cron作业将以root用户每天的每一小时的每一分钟运行,并将输出丢弃到/ dev / null。 / usr / bin / php是php的path。 然后用php的pathreplace一些php脚本。 这显然假设你的cron作业需要运行一个PHP文件。

此外,请确保some_cron_job.txt文件在文件末尾有一个换行符,就像注释所示。 否则cron不会运行。

更新:当Elastic Beanstalk扩展您的实例时,此解决scheme存在问题。 例如,假设您有一个运行cron作业的实例。 您的stream量会增加,因此Elastic Beanstalk会将您扩展到两个实例。 leader_only将确保您只有一个cron作业在两个实例之间运行。 您的stream量减less,Elastic Beanstalk将您缩小到一个实例。 但是不是终止第二个实例,Elastic Beanstalk终止了第一个领导者的实例。 您现在没有任何正在运行的cron作业,因为它们仅在第一个已终止的实例上运行。 请参阅下面的评论。

更新2:只需从下面的评论中明确说明:AWS现在可以防止自动实例终止。 只要启用它在你的领导实例,你很好去。 – 尼古拉斯·Arévalo16年10月28日在9:23

这是现在正式的做法(2015+)。 请先尝试一下,这是目前最简单的方法,也是最可靠的方法。

根据目前的文件,人们可以在他们所谓的工作层上运行定期任务

引用文档:

AWS Elastic Beanstalk支持在运行预定义configuration的环境中使用包含容器名称中的“v1.2.0”的解决scheme堆栈的工作环境层的定期任务。 你必须创造一个新的环境。

有趣的是关于cron.yaml的部分:

要调用周期性任务,您的应用程序源包必须在根级别包含一个cron.yaml文件。 该文件必须包含有关您要安排的定期任务的信息。 使用标准的crontab语法来指定这个信息。

更新:我们能够得到这个工作。 以下是我们的经验(Node.js平台)的一些重要问题:

  • 当使用cron.yaml文件时,确保你有最新的awsebcli ,因为旧版本不能正常工作。
  • 创造新的环境(至less在我们的情况下)至关重要,不只是克隆旧环境。
  • 如果您想确保您的EC2 Worker Tier实例支持CRON,请将其eb ssheb ssh ),并运行cat /var/log/aws-sqsd/default.log 。 它应该报告为aws-sqsd 2.0 (2015-02-18) 。 如果您没有2.0版本,创build您的环境时出错,您需要创build一个新的如上所述。

关于jamieb的回应,以及alrdinleal提到,您可以使用'leader_only'属性来确保只有一个EC2实例运行cron作业。

引用来自http://docs.amazonwebservices.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html

你可以使用leader_only。 一个实例被选为Auto Scaling组的领导者。 如果leader_only值设置为true,则该命令仅在标记为首领的实例上运行。

我试图在我的eb上实现一个类似的东西,所以将更新我的post,如果我解决它。

更新:

好吧,我现在有使用以下ebconfiguration工作的cronjobs:

 files: "/tmp/cronjob" : mode: "000777" owner: ec2-user group: ec2-user content: | # clear expired baskets */10 * * * * /usr/bin/wget -o /dev/null http://blah.elasticbeanstalk.com/basket/purge > $HOME/basket_purge.log 2>&1 # clean up files created by above cronjob 30 23 * * * rm $HOME/purge* encoding: plain container_commands: purge_basket: command: crontab /tmp/cronjob leader_only: true commands: delete_cronjob_file: command: rm /tmp/cronjob 

本质上,我用cronjob创build一个临时文件,然后设置crontab从临时文件读取,然后删除临时文件。 希望这可以帮助。

如上所述,build立任何crontabconfiguration的根本缺陷是它只发生在部署。 随着群集自动扩展,然后退后,它也成为第一台closures的服务器。 另外也不会出现故障,这对我来说是至关重要的。

我做了一些调查,然后与我们的AWS账户专家进行了交谈,以反驳我的想法,并validation了我提出的解决scheme。 你可以用OpsWorks做到这一点 ,虽然有点像使用房子杀死苍蝇。 也可以在任务运行器中使用数据pipe道(Data Pipeline) ,但是在脚本中它的执行能力有限,我需要能够运行PHP脚本,并访问整个代码库。 您还可以在ElasticBeanstalk集群外部专用一个EC2实例,但是不会再进行故障切换。

所以这就是我想出来的,显然是非传统的(正如AWS代表所评论的),可能被认为是黑客行为,但是它工作起来并且在故障切换时是可靠的。 我select了一个使用SDK的编码解决scheme,我将在PHP中展示,尽pipe你可以用你喜欢的任何语言来做同样的方法。

 // contains the values for variables used (key, secret, env) require_once('cron_config.inc'); // Load the AWS PHP SDK to connection to ElasticBeanstalk use Aws\ElasticBeanstalk\ElasticBeanstalkClient; $client = ElasticBeanstalkClient::factory(array( 'key' => AWS_KEY, 'secret' => AWS_SECRET, 'profile' => 'your_profile', 'region' => 'us-east-1' )); $result = $client->describeEnvironmentResources(array( 'EnvironmentName' => AWS_ENV )); if (php_uname('n') != $result['EnvironmentResources']['Instances'][0]['Id']) { die("Not the primary EC2 instance\n"); } 

因此,通过这个步骤以及如何操作…您可以像在每个EC2实例中通常那样从crontab调用脚本。 每个脚本在开头都包含这个(或者每个脚本包含一个单独的文件),它build立一个ElasticBeanstalk对象并检索所有实例的列表。 它只使用列表中的第一个服务器,并检查它是否与自己匹配,如果它继续,则继续,否则它将死亡并closures。 我已经检查过了,返回的列表似乎是一致的,从技术上讲,只需要一分钟左右的一致性,因为每个实例都执行预定的cron。 如果它确实发生了变化,那就没有关系,因为它只是与那个小窗口相关。

这种方式不够优雅,但却适合我们的特定需求 – 这不是要增加额外的服务来增加成本,或者必须有专门的EC2实例,并且在发生任何故障的情况下可以进行故障切换。 我们的cron脚本运行维护脚本,将其放入SQS中,集群中的每个服务器都可以帮助执行。 至less这可能会给你一个替代select,如果它适合您的需求。

-Davey

如果你使用的是Rails,你可以使用when-elasticbeanstalk gem 。 它允许您在所有实例上运行cron作业,或者只运行一个cron作业。 它会检查每一分钟以确保只有一个“领导者”实例,并且如果没有的话,会自动将一个服务器提升为“领导者”。 这是必需的,因为Elastic Beanstalk在部署期间只有领导的概念,并且可以随时在缩放时closures任何实例。

更新我切换到使用AWS OpsWorks,不再维护这个gem。 如果您需要比Elastic Beanstalk基础知识更多的function,我强烈build议切换到OpsWorks。

您真的不想在Elastic Beanstalk上运行cron作业。 由于您将有多个应用程序实例,这可能会导致竞争条件和其他奇怪的问题。 我最近实际上是在这篇文章的页面上发表了博客 (第四或第五篇)。 简短版本:根据应用程序的不同,使用像SQS这样的作业队列或像iron.io这样的第三方解决scheme。

我跟一个AWS支持代理进行了交谈,这就是我们如何为我工作的。 2015解决scheme:

使用your_file_name.config在.ebextensions目录中创build一个文件。 在configuration文件input中:

 文件:
   “/etc/cron.d/cron_example”:
    模式:“000644”
    所有者:根
    组:根
    内容:|
       * * * * * root /usr/local/bin/cron_example.sh

   “/usr/local/bin/cron_example.sh”:
    模式:“000755”
    所有者:根
    组:根
    内容:|
       #!/斌/庆典

       /usr/local/bin/test_cron.sh || 出口
       echo“Cron运行在``date` >> /tmp/cron_example.log
       #现在做只应该在1个实例上运行的任务...

   “/usr/local/bin/test_cron.sh”:
    模式:“000755”
    所有者:根
    组:根
    内容:|
       #!/斌/庆典

       METADATA = /select/ AWS / bin中/ EC2的元数据
       INSTANCE_ID =`$ METADATA -i |  awk'{print $ 2}'`
       REGION =`$ METADATA -z |  awk'{print substr($ 2,0,length($ 2)-1)}``

       #find我们的Auto Scaling组名称。
       ASG =`aws ec2 describe-tags --filters“Name = resource-id,Values = $ INSTANCE_ID”\
         --region $ REGION  - 输出文字|  awk'/ aws:autoscaling:groupName / {print $ 5}'`

       #find组中的第一个实例
       FIRST =`aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names $ ASG \
         --region $ REGION  - 输出文字|  awk'/ InService $ / {print $ 4}'| sorting| 头-1“

       #testing它们是否相同。
       [“$ FIRST”=“$ INSTANCE_ID”]

命令:
   rm_old_cron:
    命令:“rm * .bak”
     cwd:“/etc/cron.d”
     ignoreErrors:true

该解决scheme有两个缺点:

  1. 在后续部署中,Beanstalk将现有的cron脚本重命名为.bak,但cron仍将运行它。 您的Cron现在在同一台机器上执行两次。
  2. 如果你的环境扩大,你会得到几个实例,所有的运行你的cron脚本。 这意味着您的邮件拍摄重复,或您的数据库存档重复

解决方法:

  1. 确保任何创buildcron的.ebextensions脚本也会在后续部署中删除.bak文件。
  2. 具有执行以下操作的帮助程序脚本: – 从元数据获取当前实例ID – 从EC2获取当前的Auto Scaling组名称标签 – 获取该组中的EC2实例列表,按字母顺序sorting。 – 从列表中拿出第一个例子。 – 将步骤1中的实例ID与步骤4中的第一个实例ID进行比较。然后,您的cron脚本可以使用此帮助程序脚本来确定它们是否应该执行。

警告:

  • 用于Beanstalk实例的IAM Role需要ec2:DescribeTags和autoscaling:DescribeAutoScalingGroups权限
  • 从中select的实例是通过Auto Scaling显示为InService的实例。 这并不一定意味着它们已经完全启动并准备运行你的cron。

如果您使用默认的beanstalkangular色,则不必设置IAMangular色。

使用files而不是container_commands更可读的解决scheme:

文件:
   “/etc/cron.d/my_cron”:
    模式:“000644”
    所有者:根
    组:根
    内容:|
       #覆盖默认电子邮件地址
       MAILTO = “example@gmail.com”
       #每隔五分钟运行一次Symfony命令(以ec2用户身份)
       * / 10 * * * * ec2-user / usr / bin / php / var / app / current / app / console do:something
    编码:纯文本
命令:
   #删除Elastic Beanstalk创build的备份文件
   clear_cron_backup:
    命令:rm -f /etc/cron.d/watson.bak

请注意,格式不同于通常的crontab格式,因为它指定用户运行命令为。

当有新领导人出现时,有人在想领导者只是自动扩展问题。 我似乎无法弄清楚如何回复他们的意见,但看到这个链接: http : //blog.paulopoiati.com/2013/08/25/running-cron-in-elastic-beanstalk-auto-scaling-环境/

要控制Auto Scaling是否可以在缩放时终止特定实例,请使用实例保护。 您可以在Auto Scaling组或单个Auto Scaling实例上启用实例保护设置。 当Auto Scaling启动实例时,该实例将inheritanceAuto Scaling组的实例保护设置。 您可以随时更改Auto Scaling组或Auto Scaling实例的实例保护设置。

http://docs.aws.amazon.com/autoscaling/latest/userguide/as-instance-termination.html#instance-protection

我有另一个解决scheme,如果一个PHP文件需要通过cron运行,如果你已经设置任何NAT实例,那么你可以把cronjob NAT实例,并通过wget运行PHP文件。

2017:如果你正在使用Laravel5 +

你只需要2分钟来configuration它:

  • 创build一个工作者层
  • 安装laravel-aws-worker

    composer require dusterio/laravel-aws-worker

  • 添加一个cron.yaml到根文件夹:

将cron.yaml添加到您的应用程序的根文件夹(这可以是您的回购的一部分,或者您可以在部署到EB之前添加此文件 – 重要的是该文件在部署时存在):

 version: 1 cron: - name: "schedule" url: "/worker/schedule" schedule: "* * * * *" 

而已!

现在App\Console\Kernel所有任务都将被执行

详细说明和解释: https : //github.com/dusterio/laravel-aws-worker

如何在Laravel中编写任务: https ://laravel.com/docs/5.4/scheduling

所以我们一直在为此苦苦挣扎,经过与AWS代表的讨论,我终于想出了我认为是最好的解决scheme。

与cron.yaml一起使用工作层绝对是最简单的修复方法。 但是,文档没有明确说明的是,这会将工作放在您用于实际运行作业的SQS队列的末尾 。 如果你的cron作业是时间敏感的(尽可能多),这是不可接受的,因为它取决于队列的大小。 一个select是使用一个完全独立的环境来运行cron作业,但我认为这是过度的。

其他一些选项,比如检查是否是列表中的第一个实例,也不是很理想。 如果目前的初审正在closures,该怎么办?

实例保护也可能带来问题 – 如果该实例被locking/冻结,该怎么办?

重要的是理解AWS本身如何pipe理cron.yamlfunction。 有一个SQS守护进程,它使用一个Dynamo表来处理“领导选举”。 它经常写在这张桌子上,如果现在的领导人没有写一会儿,下一个会接任领导。 守护进程如何决定将哪个实例激发到SQS队列中。

我们可以改变现有的function,而不是试图重写我们自己的function。 你可以在这里看到完整的解决scheme: https : //gist.github.com/dorner/4517fe2b8c79ccb3971084ec28267f27

这在Ruby中,但是您可以轻松地将其调整为具有AWS开发工具包的任何其他语言。 本质上,它检查当前的领导,然后检查状态,以确保它处于良好的状态。 它会一直循环,直到当前领导者处于良好状态,并且如果当前实例是领导者,则执行该作业。

这里是一个修复事件,你想在PHP中做到这一点。 您只需在您的.ebextensions文件夹中使用cronjob.config即可正常工作。

 files: "/etc/cron.d/my_cron": mode: "000644" owner: root group: root content: | empty stuff encoding: plain commands: 01_clear_cron_backup: command: "rm -f /etc/cron.d/*.bak" 02_remove_content: command: "sudo sed -i 's/empty stuff//g' /etc/cron.d/my_cron" container_commands: adding_cron: command: "echo '* * * * * ec2-user . /opt/elasticbeanstalk/support/envvars && /usr/bin/php /var/app/current/index.php cron sendemail > /tmp/sendemail.log 2>&1' > /etc/cron.d/my_cron" leader_only: true 

环境获取文件的环境variables。 你可以像上面那样在tmp / sendemail.log上debugging输出。

希望这可以帮助一个人,因为它肯定帮助我们!

想知道下面的工作

 container_commands: 01_some_cron_job: command: "echo '* * * * * ls' | crontab" leader_only: true