如何在AWS Elastic Beanstalk上设置和使用Laravel调度?

20

情境

作为一个相对新手的Laravel和Elastic Beanstalk用户,我很快就发现自己需要安排操作,就像我们大多数人一样。

过去,我总是使用简单的crontab调度来完成这个任务。所以现在我面前有一系列问题:

  • 如何使用crontab运行Laravel代码?
  • 如何在我的Elastic Beanstalk环境中设置crontab?

找到这些问题的各个答案并不难。然而,将它们结合起来并实际使其正常工作却有点棘手,这就是为什么我决定在这里分享解决方案,供其他人解决这个问题。


环境

  • Laravel 5.6
  • PHP 7.1
8个回答

41

TL;DR:

在这篇文章的末尾可以看到工作中的 .ebextentions 配置。


环境

  • Laravel 5.6
  • PHP 7.1

如何使用crontab运行Laravel代码?

答案当然是最显而易见的,如果你对Laravel稍有了解,你肯定知道答案:使用Scheduling(调度)

我不会让你感到无聊地解释 Laravel 调度的精髓,因为你可以自己阅读文档了解它。

但我们需要记住的关键是 Laravel 调度使用 crontab 来执行,正如文档所描述:

* * * * * php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1

这就引出了下一个,可能有些棘手的问题...
如何在我的 Elastic Beanstalk 环境中设置 crontab?
乍一看,这个问题的答案似乎非常简单。 我在 AWS Knowledge Center 中找到了这篇文章:如何在 Elastic Beanstalk 环境中的 EC2 实例上创建 crontab 作业? 在这里,他们描述了如何使用 .ebextentions 在您的 Elastic Beanstalk EC2 机器上设置 crontab 作业。 简而言之,它会在目录 /etc/cron.d/ 中创建一个新文件,我们将期望的 cron job 放置在其中。
然后,这个目录中的文件将由 crontab 以 root 用户身份进行处理。 我遇到的一些陷阱如下所述:
files:

    # The name of the file should not contain any dot (.) or dash (-), this can
    # cause the script not to run. Underscore (_) is OK.
    "/etc/cron.d/mycron":

        # This permissions is important so that root user can run the script.
        mode: "000644"

        # As the file is run by the root user it needs to be the owner of the file.
        owner: root

        # For consistency it's a good idea to have root as the group aswell.
        group: root

        # NOTE: We need to explicitly tell the cron job to be run as the root user!
        content: |
            * * * * * root /usr/local/bin/myscript.sh 

# There need to be a new line after the actual cron job in the file.

一旦我们避开了所有这些陷阱,就该把上面的 Laravel 调度 cron 作业安装好了。应该看起来像这样:

files:
    "/etc/cron.d/schedule_run":
        mode: "000644"
        owner: root
        group: root
        content: |
            * * * * * root php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1
在很多情况下,这并不会真正起作用。这是因为Laravel调度程序将无法访问您的ENV变量,明显地也不会访问您的数据库设置。
我在这里找到了答案:如何在AWS Elastic Beanstalk Cron上使 Laravel 任务调度正常工作 所以,向George Bönnisch致以最大的敬意; 我向您致敬,先生,感谢您分享这个!
因此,在拼图的最后一块中,我终于能够正确地设置工作:

工作解决方案

文件结构:

[Project root]
    |-- .ebextensions
    |        |-- cronjob.config

cronjob.config:

files:
    "/etc/cron.d/schedule_run":
        mode: "000644"
        owner: root
        group: root
        content: |
            * * * * * root . /opt/elasticbeanstalk/support/envvars && /usr/bin/php /var/www/html/artisan schedule:run 1>> /dev/null 2>&1

commands:
    remove_old_cron:
        command: "rm -f /etc/cron.d/*.bak"

在AWS Elastic Beanstalk上使用Laravel调度的提示!

Elastic Beanstalk的一个关键特性是它可以在需要时自动扩展并添加更多的服务器,因此您可能想要查看Laravel调度中的新功能:在一个服务器上运行任务

在许多情况下,您不希望您的cron作业在多个服务器上执行。例如,如果您有一个定时命令用于发送电子邮件,则不希望多次发送这些邮件。

注意:根据文档,这需要您将memcached或redis作为缓存引擎。如果没有,请查看AWS服务Elasticache

注意2:当使用onOneServer()时,您必须使用name()方法(在调用onOneServer()之前)为预定的任务命名。如下所示:

$schedule->command('my:task')
    ->name('my:task')
    ->daily()
    ->onOneServer();

2
是的,至少在这篇文章写作时是这样。如果我没有命名它,就会出现错误。也许在5.7或5.8版本中已经改变了,我还没有检查过。 - Niklas
这个答案在 Laravel 和 AWS 上的调度方面真的是非常完整!感谢您节省了我很多时间! - Dhrumil Bhankhar
2
在ebextensions配置中,remove_old_cron命令部分上面是什么?旧的cron是否生成了任何.bak文件需要我们清理?您能否详细说明其有用性? - Dhrumil Bhankhar
3
据@DhrumilBhankhar所说,当创建一个新的schedule_run时,旧的schedule_run会被添加.bak后缀。remove_old_cron是用来删除旧的(如其名称所示)。 - Ehsan
这个不起作用! - ackerchez
显示剩余5条评论

4

您可以在Amazon Linux 2上使用此功能。通过使用.ebextensions配置,您可以直接运行命令。

首先,您需要在单独的文件中配置该命令,在.ebextensions下创建名为cron_job.txt的文件并添加以下行。

* * * * * root . /opt/elasticbeanstalk/deployment/env && /usr/bin/php /var/www/html/artisan schedule:run 1>> /var/www/html/laralog.log 2>&1

请注意,Amazon Linux 2与Amazon Linux 1的第一部分不同。

它会加载环境变量:

Linux 1:. /opt/elasticbeanstalk/support/envvars
Linux 2:. /opt/elasticbeanstalk/deployment/env

并且在此单独文件中初始化命令后,

我们需要通过init.config文件将其触发,在.ebextensions中定义容器命令,如下所示:

container_commands:
   03cronjob:
        command: 'cat .ebextensions/cron_jobs.txt > /etc/cron.d/cron_jobs && chmod 644 /etc/cron.d/cron_jobs'

就是这样,您可以尝试一下,看看是否成功执行了cron作业。

您也可以阅读这篇解释性文章: https://medium.com/qosoor/the-ultimate-guide-to-setup-cron-jobs-with-laravel-elastic-beanstalk-d497daaca1b0

希望这有所帮助


这对任何人都有效吗? - ackerchez
这对任何人都有效吗? - undefined

4
更简单的方法是使用新的周期性任务功能。对于cron作业,使用.ebextensions可能会导致多台机器运行相同的作业或其他自动扩展竞争条件。在cron.yaml中定义的作业仅由Worker环境加载,并保证每次只有一台机器(即leader)运行。它具有良好的同步机制,以确保没有重复。文档中提到:Elastic Beanstalk使用领导选举来确定worker环境中哪个实例排队执行定期任务。每个实例都尝试通过写入Amazon DynamoDB表来成为leader。成功的第一个实例是leader,并且必须继续向表中写入以保持leader状态。如果leader停止服务,则另一个实例将迅速取代它。

创建针对单个或多个Workers的Cron

cron.yaml放置在项目的根目录中。
version: 1
cron:
  - name: "schedule"
    url: "/worker/schedule"
    schedule: "* * * * *"

需要考虑的一件事是,在Beanstalk中,定期任务被设计为向你应用程序中的URL发出HTTP POST请求,从而触发你想要运行的作业。这与它如何使用SQS管理队列类似。

对于Laravel

特别针对Laravel,您可以创建路由和控制器来处理每个计划任务。但更好的方法是使用Laravel的调度程序,并拥有一个每分钟调用一次的单一路由。

此软件包将自动为您创建这些路由 https://github.com/dusterio/laravel-aws-worker

故障排除权限

如果您在从CodePipeline触发部署时遇到DynamoDB创建Leader表权限问题,那是因为CodePileline服务角色需要 dynamodb:CreateTable。请参阅以下说明 StackOverflow问题

官方弹性Beanstalk定期任务文档


1

0

这是给Docker用户的,如果你遇到了一些问题,那么我认为这篇文章很值得一读。

需要将cron添加到服务器上的schedule_run文件中。然而,即使您在Dockerrun.aws.json文件中添加了container_name,它也会将其更改为该名称加上一些额外的信息,因此您无法使用正常的服务名称来运行cron。

因此,可以使用$(docker ps -qf name=php-fpm),其中name是您的容器名称的一部分,它将返回容器的ID。我的容器名为php-fpm

这是我的工作文件(.ebextensions/01-cron.config)。

files:
"/etc/cron.d/schedule_run":
    mode: "000644"
    owner: root
    group: root
    content: |
        * * * * * root docker exec -t $(docker ps -qf name=php-fpm) sh -c "php artisan schedule:run" >> /var/log/eb-cron.log 2>&1

commands:
    002-remove_old_cron:
        command: "rm -f /etc/cron.d/*.bak"

注意: 第一次运行该cron时,容器可能尚未启动。因为我的示例中的cron每分钟运行一次,所以这并不太重要,因为在第二次运行时,容器已经启动并正常运行。


0

很不幸,@Niklas的采纳答案对我没有起作用。

甚至在这里有更全面的解释:

https://medium.com/qosoor/the-ultimate-guide-to-setup-cron-jobs-with-laravel-elastic-beanstalk-d497daaca1b0

但是就像我说的那样,那对我没用。

对我有用的方法更简单: (.ebextensions/cron.config)

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

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

            date > /tmp/date
            cd /var/www/html/ && php artisan schedule:run >> /dev/null 2>&1

            exit 0

commands:
    remove_old_cron:
        command: "rm -f /etc/cron.d/mycron.bak"

我只是从Laravel文档中复制了命令,并将其放入AWS文档提供的配置文件中。


这个对你有用吗?我无法让它工作。 - ackerchez
@ackerchez 是的,对我有效;我刚刚检查了一下,这个文件仍然存在,并且是一样的,它正常工作并且每天(晚上)按计划触发我的artisan命令。对回复晚了表示抱歉。 - undefined

0

由于旧解决方案中env文件的位置错误,cron未被触发。实际上,任何东西都没有问题。以下是您目前可以使用的命令。我们在所有项目中都使用它。

在.ebextensions文件夹中创建一个cronjob.config文件。然后将这些内容放入文件内部。

files:
    "/etc/cron.d/schedule_run":
        mode: "000644"
        owner: root
        group: root
        content: |
            * * * * * root . /opt/elasticbeanstalk/deployment/env && /usr/bin/php /var/www/html/artisan schedule:run 1>> /var/www/html/storage/logs/laravel_cron.log 2>&1

commands:
    remove_old_cron:
        command: "rm -f /etc/cron.d/*.bak"

-1

经过多次尝试,我找到了一种替代方法来轻松运行cron作业。您可以通过3个步骤轻松运行cron作业。

第1步:创建路由

routes/web.php

Route::get('/cron/run',[HomeController::class, 'cron'])->name('cron');

步骤2:在HomeController中创建一个函数

public function cron()
{
    \Artisan::call("schedule:run");
    return 'run cron successful';
}

步骤三:

使用https://cron-job.org/en/每分钟运行一次URL。


这是一个地雷,会将您的命令暴露给公众。 - pkid169

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