AWS Elastic Beanstalk,运行cronjob

One*_*ema 82 cron crontab amazon-web-services amazon-elastic-beanstalk

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

这是我在配置文件中尝试做的事情没有成功:

container_commands:
  01cronjobs:
    command: echo "*/1 * * * * root php /etc/httpd/myscript.php"
Run Code Online (Sandbox Code Playgroud)

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

有任何想法吗?

Ana*_*ica 90

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

在应用程序的根目录下创建一个名为.ebextensions的文件夹(如果它尚不存在).然后在.ebextensions文件夹中创建一个配置文件.我将使用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
Run Code Online (Sandbox Code Playgroud)

这是Elastic Beanstalk的YAML配置文件.确保将其复制到文本编辑器中时,文本编辑器使用空格而不是制表符.否则,当您将其推送到EB时,您将收到YAML错误.

这样做的是创建一个名为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文件夹中创建一个名为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
Run Code Online (Sandbox Code Playgroud)

所以这个cron作业将以root用户的身份每天每小时运行一次,并将输出丢弃到/ dev/null./ usr/bin/php是php的路径.然后用你的php文件的路径替换some-php-script-here.这显然是假设您的cron作业需要运行PHP文件.

另外,请确保some_cron_job.txt文件在文件末尾有一个换行符,就像评论所说的那样.否则cron将无法运行.

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

更新2: 从下面的评论中明确说明:AWS现在可以防止自动实例终止.只需在你的领导实例上启用它就可以了. - NicolásArévalo2016年10月28日9:23

  • 我已经使用了你的建议已经有一段时间了,最​​近遇到了一个问题,导致某个领导者切换,导致运行cron的多个实例.为了解决这个问题,我将`01_some_cron_job`更改为`02_some_cron_job`并添加了`01_remove_cron_jobs`以及以下内容:`command:"rm /etc/cron.d/cron_jobs || exit 0"`.这样,在每次部署之后,只有领导者才会拥有`cron_jobs`文件.如果领导者改变了,你可以重新部署,并且将修复crons以再次运行. (10认同)
  • AWS现在可以防止自动实例终止.只需在你的领导实例上启用它就可以了. (5认同)
  • 我建议不要依赖`leader_only`属性.它仅在部署期间使用,如果您缩小规模或"领导者"实例失败,您将遇到问题[参考](https://forums.aws.amazon.com/thread.jspa?messageID=411720#411720) (3认同)
  • 不要这样做.这太不可靠了.我让它工作的唯一方法是运行一个微实例并使用CURL从那里运行cron作业.这保证只有一个实例运行它并且安装了crons的leader不会终止. (2认同)

xar*_*lis 56

这是现在正式完成的方式(2015年以上).请先尝试一下,这是目前最简单的方法,也是最可靠的方法.

根据当前的文档,人们可以在他们所谓的工人层运行定期任务.

引用文档:

AWS Elastic Beanstalk支持在运行预定义配置的环境中的工作线程环境层的定期任务,其中包含容器名称中包含"v1.2.0"的解决方案堆栈.您必须创建一个新环境.

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

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

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

  • 使用cron.yaml文件时,请确保您拥有最新的awsebcli,因为旧版本无法正常运行.
  • 创建新环境也至关重要(至少在我们的情况下是这样),而不仅仅是克隆旧环境.
  • 如果要确保EC2 Worker Tier实例支持CRON,请ssh into it(eb ssh),然后运行cat /var/log/aws-sqsd/default.log.它应该报告为aws-sqsd 2.0 (2015-02-18).如果您没有2.0版本,那么在创建环境时出现问题,您需要创建一个如上所述的新环境.

  • 感谢这个 - 菜鸟问题 - 我需要我的cron每小时检查一次我的网络应用程序的数据库以查看即将发生的日历事件,并在发生时发送提醒电子邮件.这里最好的设置是什么,我应该将cron.yaml URL指向我的Web应用程序中的路由吗?或者我应该让我的工作人员env应用程序访问数据库?在这方面很少! (5认同)
  • @christian我们这样做的方式,我们在两个不同的环境中运行相同的应用程序(因此不需要特殊的配置) - 工作者和普通的Web服务器.工作环境通过设置我们的应用程序查找的ENV变量来启用一些特殊路径.这样,您可以在cron.yaml中设置特殊的仅限工作线路,同时拥有与普通应用程序共享代码库的奢侈.您的工作者应用程序可以轻松访问与Web服务器相同的资源:数据库,模型等. (5认同)
  • 有一件事我不清楚是否有办法避免必须分配额外的EC2机器只是为了通过cron.yaml运行cron作业.理想情况下,它将与为HTTP请求提供服务的机器(即Web层)运行在同一台机器上. (4认同)
  • 关于cron.yaml,有一篇很棒的博客文章:[在亚马逊网络服务(AWS)上运行cron作业Elastic Beanstalk - Medium](https://medium.com/@joelennon/running-cron-jobs-on-amazon-网络服务,AWS-弹性魔豆,a41d91d1c571) (2认同)

bet*_*ife 31

关于jamieb的响应,并且正如alrdinleal所提到的,您可以使用'leader_only'属性来确保只有一个EC2实例运行cron作业.

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

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

我试图在我的eb上实现类似的东西,所以如果我解决它将更新我的帖子.

更新:

好吧,我现在使用以下eb配置工作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
Run Code Online (Sandbox Code Playgroud)

基本上,我使用cronjobs创建一个临时文件,然后将crontab设置为从临时文件中读取,然后删除临时文件.希望这可以帮助.

  • 所以我(最后)发现了如何防止领导者被自动缩放终止 - 自定义自动缩放终止策略.请参阅http://docs.aws.amazon.com/AutoScaling/latest/DeveloperGuide/us-termination-policy.html (7认同)
  • 您如何确保运行此crontab的实例不会被自动缩放终止?默认情况下,它终止最旧的实例. (3认同)

小智 12

如上所述,建立任何crontab配置的根本缺陷是它只在部署时发生.随着群集自动放大,然后退回,最好也是第一个关闭的服务器.此外,没有故障转移,对我来说这是至关重要的.

我做了一些研究,然后与我们的AWS账户专家交谈,以反映想法并确认我提出的解决方案.你可以用OpsWorks完成这个任务,虽然它有点像用房子来杀死苍蝇.也可以将数据管道与任务运行器一起使用,但这在它可以执行的脚本中具有有限的能力,并且我需要能够运行PHP脚本,并且可以访问整个代码库.您还可以在ElasticBeanstalk集群之外专用EC2实例,但之后您不会再进行故障转移.

所以这就是我想出来的,这显然是非常规的(正如AWS代表所评论的那样)并且可能被认为是一种黑客行为,但它可以运行并且可以通过故障转移实现.我选择了使用SDK的编码解决方案,我将在PHP中展示,尽管您可以使用您喜欢的任何语言执行相同的方法.

// 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");
}
Run Code Online (Sandbox Code Playgroud)

因此,逐步介绍它以及它如何运作...您可以像往常一样在每个EC2实例上调用crontab中的脚本.每个脚本在开头都包含它(或者每个脚本包含一个文件,因为我使用它),它建立一个ElasticBeanstalk对象并检索所有实例的列表.它仅使用列表中的第一个服务器,并检查它是否与自身匹配,如果它继续,则会死亡,然后关闭.我已经检查过并且返回的列表似乎是一致的,从技术上讲,它只需要一分钟左右一致,因为每个实例都执行预定的cron.如果确实发生了变化,那就不重要了,因为它只与那个小窗口有关.

这并不优雅,但适合我们的特定需求 - 这不是通过额外服务增加成本或必须有专用EC2实例,并且在发生任何故障时都会进行故障转移.我们的cron脚本运行维护脚本,这些脚本放入SQS,集群中的每个服务器都有助于执行.如果符合您的需求,至少可以为您提供备用选项.

-Davey


Ken*_*Ken 9

我与AWS支持代理进行了交谈,这就是我们为此工作的方式.2015解决方案:

使用your_file_name.config在.ebextensions目录中创建一个文件.在配置文件输入中:

files:
  "/etc/cron.d/cron_example":
    mode: "000644"
    owner: root
    group: root
    content: |
      * * * * * root /usr/local/bin/cron_example.sh

  "/usr/local/bin/cron_example.sh":
    mode: "000755"
    owner: root
    group: root
    content: |
      #!/bin/bash

      /usr/local/bin/test_cron.sh || exit
      echo "Cron running at " `date` >> /tmp/cron_example.log
      # Now do tasks that should only run on 1 instance ...

  "/usr/local/bin/test_cron.sh":
    mode: "000755"
    owner: root
    group: root
    content: |
      #!/bin/bash

      METADATA=/opt/aws/bin/ec2-metadata
      INSTANCE_ID=`$METADATA -i | awk '{print $2}'`
      REGION=`$METADATA -z | awk '{print substr($2, 0, length($2)-1)}'`

      # Find our Auto Scaling Group name.
      ASG=`aws ec2 describe-tags --filters "Name=resource-id,Values=$INSTANCE_ID" \
        --region $REGION --output text | awk '/aws:autoscaling:groupName/ {print $5}'`

      # Find the first instance in the Group
      FIRST=`aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names $ASG \
        --region $REGION --output text | awk '/InService$/ {print $4}' | sort | head -1`

      # Test if they're the same.
      [ "$FIRST" = "$INSTANCE_ID" ]

commands:
  rm_old_cron:
    command: "rm *.bak"
    cwd: "/etc/cron.d"
    ignoreErrors: true

该解决方案有两个缺点:

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

解决方法:

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

警告:

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

如果使用默认的beanstalk角色,则不必设置IAM角色.


dig*_*noe 7

如果您正在使用Rails,则可以使用never-elasticbeanstalk gem.它允许您在所有实例上运行cron作业或只运行一个.它会检查每一分钟以确保只有一个"领导者"实例,并且如果没有,则会自动将一个服务器提升为"领导者".这是必需的,因为Elastic Beanstalk在部署期间仅具有leader的概念,并且可以在扩展时随时关闭任何实例.

更新 我切换到使用AWS OpsWorks并且不再维护此gem.如果您需要比Elastic Beanstalk基础知识更多的功能,我强烈建议您切换到OpsWorks.


jam*_*ieb 6

你真的不想在Elastic Beanstalk上运行cron作业.由于您将拥有多个应用程序实例,因此可能会导致竞争条件和其他奇怪问题.我实际上最近在博客上写了这篇文章(页面上的第4或第5个提示).简短版本:根据应用程序,使用SQS等作业队列或iron.io等第三方解决方案.

  • @JamesAlday 这个问题已经很老了。自从我写了上面的评论以来,AWS 引入了一种优雅的方式来处理 Elastic Beanstalk 上的 cron 作业,方法是选择一个正在运行的服务器作为主服务器。话虽如此,听起来您正在滥用 cron + MySQL 作为作业队列。不过,在提供具体建议之前,我需要对您的应用程序有很多了解。 (2认同)

Seb*_*rin 6

2017:如果你使用的是 Laravel5+

您只需要 2 分钟即可配置它:

  • 创建一个工人层
  • 安装 laravel-aws-worker

    composer require dusterio/laravel-aws-worker

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

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

version: 1
cron:
 - name: "schedule"
   url: "/worker/schedule"
   schedule: "* * * * *"
Run Code Online (Sandbox Code Playgroud)

就是这样!

App\Console\Kernel现在将执行您的所有任务

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

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