programing

cronjob을 실행하는 AWS Elastic Beanstalk

nasanasas 2020. 9. 23. 07:40
반응형

cronjob을 실행하는 AWS Elastic Beanstalk


매분 실행하도록 cronjob / task를 설정하는 방법이 있는지 알고 싶습니다. 현재 모든 인스턴스에서이 작업을 실행할 수 있습니다.

이것은 성공하지 않고 구성 파일에서 시도한 것입니다.

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

이것이 올바른 방법인지 잘 모르겠습니다.

어떤 아이디어?


다음은 Elastic Beanstalk에 크론 작업을 추가 한 방법입니다.

.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

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라는 파일을 생성합니다. 이 파일에 크론 작업을 배치합니다.

예를 들면 다음과 같습니다.

# 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

따라서이 크론 작업은 루트 사용자로 매일 매시간 1 분마다 실행되고 / dev / null에 대한 출력을 버립니다. / usr / bin / php는 php의 경로입니다. 그런 다음 some-php-script-here를 php 파일의 경로로 바꿉니다. 이것은 분명히 cron 작업이 PHP 파일을 실행해야한다고 가정합니다.

또한 some_cron_job.txt 파일에 주석이 말한 것처럼 파일 끝에 개행 문자가 있는지 확인하십시오. 그렇지 않으면 cron이 실행되지 않습니다.

업데이트 : Elastic Beanstalk가 인스턴스를 확장 할 때이 솔루션에 문제가 있습니다. 예를 들어 cron 작업이 실행중인 인스턴스가 하나 있다고 가정 해 보겠습니다. 트래픽이 증가하여 Elastic Beanstalk가 최대 2 개의 인스턴스로 확장합니다. leader_only는 두 인스턴스간에 하나의 크론 작업 만 실행하도록합니다. 트래픽이 감소하고 Elastic Beanstalk는 사용자를 하나의 인스턴스로 축소합니다. 그러나 두 번째 인스턴스를 종료하는 대신 Elastic Beanstalk는 리더였던 첫 번째 인스턴스를 종료합니다. 이제 종료 된 첫 번째 인스턴스에서만 실행중인 크론 작업이 실행되지 않습니다. 아래 설명을 참조하십시오.

업데이트 2 : 아래 설명에서이를 명확히합니다. AWS는 이제 자동 인스턴스 종료로부터 보호합니다. 리더 인스턴스에서 활성화하기 만하면됩니다. – Nicolás Arévalo '16 10 월 28 일 9:23


이것이 지금 공식적인 방법입니다 (2015+). 이 방법을 먼저 시도해보십시오. 현재 사용 가능한 가장 쉽고 신뢰할 수있는 방법입니다.

현재 문서에 따르면 소위 작업자 계층 에서 주기적인 작업을 실행할 수 있습니다.

문서 인용 :

AWS Elastic Beanstalk는 컨테이너 이름에 "v1.2.0"이 포함 된 솔루션 스택으로 사전 정의 된 구성을 실행하는 환경에서 작업자 환경 계층에 대한 주기적 작업을 지원합니다. 새 환경을 만들어야합니다.

또한 흥미로운 부분은 cron.yaml에 대한 부분입니다 .

주기적 작업을 호출하려면 애플리케이션 소스 번들에 루트 수준의 cron.yaml 파일이 포함되어야합니다. 파일에는 예약하려는 정기 작업에 대한 정보가 포함되어 있어야합니다. 표준 crontab 구문을 사용하여이 정보를 지정하십시오.

업데이트 : 우리는이 작업을 할 수있었습니다. 다음은 우리 경험 (Node.js 플랫폼)에서 얻은 몇 가지 중요한 문제입니다.

  • cron.yaml 파일을 사용할 때 이전 버전이 제대로 작동하지 않으므로 최신 awsebcli 가 있는지 확인하십시오 .
  • 또한 이전 환경을 복제하는 것이 아니라 새로운 환경을 만드는 것도 중요합니다 (적어도 우리의 경우에는 그렇습니다).
  • EC2 작업자 계층 인스턴스에서 CRON이 지원되는지 확인하려면 여기에 ssh ( eb ssh)하고 cat /var/log/aws-sqsd/default.log. 로보고되어야합니다 aws-sqsd 2.0 (2015-02-18). 2.0 버전이없는 경우 환경을 만들 때 문제가 발생했으며 위에서 설명한대로 새 환경을 만들어야합니다.

jamieb의 응답과 관련하여 비정상적으로 언급했듯이 'leader_only'속성을 사용하여 하나의 EC2 인스턴스 만 cron 작업을 실행하도록 할 수 있습니다.

http://docs.amazonwebservices.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html 에서 인용 한 내용 :

leader_only를 사용할 수 있습니다. 하나의 인스턴스가 Auto Scaling 그룹의 리더로 선택됩니다. leader_only 값이 true로 설정된 경우 명령은 리더로 표시된 인스턴스에서만 실행됩니다.

나는 내 eb에서 비슷한 것을 달성하려고 노력하고 있으므로 그것을 해결하면 내 게시물을 업데이트 할 것입니다.

최신 정보:

좋아, 이제 다음 eb 구성을 사용하여 작동하는 cronjob이 있습니다.

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

기본적으로 cronjobs로 임시 파일을 만든 다음 임시 파일에서 읽도록 crontab을 설정 한 다음 나중에 임시 파일을 삭제합니다. 도움이 되었기를 바랍니다.


위에서 언급했듯이 crontab 구성 설정의 근본적인 결함은 배포시에만 발생한다는 것입니다. 클러스터가 자동으로 확장 된 다음 다시 축소됨에 따라 첫 번째 서버를 끄는 것이 좋습니다. 또한 장애 조치가 없을 것입니다.

몇 가지 조사를 한 후 AWS 계정 전문가와상의하여 아이디어를 전달하고 제가 생각 해낸 솔루션을 검증했습니다. OpsWorks를 사용하면 집을 사용하여 파리를 죽이는 것과 비슷하지만 이 작업을 수행 할 수 있습니다 . Task Runner와 함께 Data Pipeline 을 사용할 수도 있지만 실행할 수있는 스크립트의 기능이 제한되어 있으므로 전체 코드베이스에 액세스하여 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");
}

따라서이 과정과 작동 방식을 살펴보면 일반적으로 모든 EC2 인스턴스에서 하듯이 crontab에서 스크립트를 호출합니다. 각 스크립트는 처음에 이것을 포함 (또는 내가 사용하는 각각에 대해 단일 파일을 포함)하여 ElasticBeanstalk 객체를 설정하고 모든 인스턴스의 목록을 검색합니다. 목록의 첫 번째 서버 만 사용하고 자신과 일치하는지 확인합니다. 일치하는 경우 계속 진행되고 그렇지 않으면 죽고 종료됩니다. 확인한 결과 반환 된 목록이 일관성이있는 것 같습니다. 기술적으로는 각 인스턴스가 예약 된 크론을 실행하기 때문에 1 분 정도만 일관성이 있으면됩니다. 변경되면 문제가되지 않습니다. 다시 한 번 작은 창에만 관련이 있기 때문입니다.

이것은 결코 우아하지는 않지만 추가 서비스로 비용을 늘리거나 전용 EC2 인스턴스를 가져야하는 것이 아니고 장애 발생시 장애 조치가 필요한 특정 요구 사항에 적합했습니다. cron 스크립트는 SQS에 배치되는 유지 관리 스크립트를 실행하고 클러스터의 각 서버는 실행을 돕습니다. 적어도 이것은 귀하의 필요에 맞는 경우 대체 옵션을 제공 할 수 있습니다.

-데이비


저는 AWS 지원 에이전트와 이야기를 나눴으며 이것이 저를 위해 작동하게 된 방법입니다. 2015 년 솔루션 :

your_file_name.config를 사용하여 .ebextensions 디렉터리에 파일을 생성합니다. 구성 파일 입력에서 :

파일 :
  "/etc/cron.d/cron_example":
    모드 : "000644"
    소유자 : 루트
    그룹 : 루트
    내용 : |
      * * * * * 루트 /usr/local/bin/cron_example.sh

  "/usr/local/bin/cron_example.sh":
    모드 : "000755"
    소유자 : 루트
    그룹 : 루트
    내용 : |
      #!/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-출력 텍스트 | awk '/ InService $ / {print $ 4}'| 정렬 | 머리 -1`

      # 동일한 지 테스트합니다.
      [ "$ FIRST"= "$ INSTANCE_ID"]

명령 :
  rm_old_cron :
    명령 : "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로 표시되는 인스턴스입니다. 이것이 반드시 완전히 부팅되고 크론을 실행할 준비가되었음을 의미하지는 않습니다.

기본 beanstalk 역할을 사용하는 경우 IAM 역할을 설정할 필요가 없습니다.


Rails를 사용하는 경우 every-elasticbeanstalk gem을 사용할 수 있습니다 . 모든 인스턴스 또는 하나의 인스턴스에서 크론 작업을 실행할 수 있습니다. 매분마다 "리더"인스턴스가 하나만 있는지 확인하고 서버가 없으면 자동으로 한 서버를 "리더"로 승격합니다. Elastic Beanstalk는 배포 중 리더 개념 만 갖고 있으며 확장하는 동안 언제든지 모든 인스턴스를 종료 할 수 있기 때문에 필요합니다.

업데이트 AWS OpsWorks를 사용하도록 전환했으며 더 이상이 gem을 유지하지 않습니다. Elastic Beanstalk의 기본에서 사용할 수있는 것보다 더 많은 기능이 필요한 경우 OpsWorks로 전환하는 것이 좋습니다.


Elastic Beanstalk에서 크론 작업을 실행하고 싶지는 않습니다. 여러 응용 프로그램 인스턴스가 있으므로 경쟁 조건 및 기타 이상한 문제가 발생할 수 있습니다. 나는 최근에 이것에 대해 블로그를 썼다 (페이지 아래 네 번째 또는 다섯 번째 팁). 짧은 버전 : 애플리케이션에 따라 SQS와 같은 작업 대기열 또는 iron.io 와 같은 타사 솔루션을 사용 합니다 .


files대신 다음을 사용하는 더 읽기 쉬운 솔루션 container_commands:

파일 :
  "/etc/cron.d/my_cron":
    모드 : "000644"
    소유자 : 루트
    그룹 : 루트
    내용 : |
      # 기본 이메일 주소 재정의
      MAILTO = "example@gmail.com"
      # 5 분마다 Symfony 명령 실행 (ec2-user로)
      * / 10 * * * * ec2-user / usr / bin / php / var / app / current / app / console do : something
    인코딩 : 일반
명령 :
  # Elastic Beanstalk에서 생성 한 백업 파일 삭제
  clear_cron_backup :
    명령 : rm -f /etc/cron.d/watson.bak

형식은 명령을 실행할 사용자를 지정한다는 점에서 일반적인 crontab 형식과 다릅니다.


2018 년 기부금 1 센트

올바른 방법 ( django/pythondjango_crontab앱 사용)은 다음 같습니다.

.ebextensions폴더 안에 98_cron.config다음 과 같은 파일을 만듭니다 .

files:
  "/tmp/98_create_cron.sh":
    mode: "000755"
    owner: root
    group: root
    content: |
      #!/bin/sh
      cd /
      sudo /opt/python/run/venv/bin/python /opt/python/current/app/manage.py crontab remove > /home/ec2-user/remove11.txt
      sudo /opt/python/run/venv/bin/python /opt/python/current/app/manage.py crontab add > /home/ec2-user/add11.txt 

container_commands:
    98crontab:
        command: "mv /tmp/98_create_cron.sh /opt/elasticbeanstalk/hooks/appdeploy/post && chmod 774 /opt/elasticbeanstalk/hooks/appdeploy/post/98_create_cron.sh"
        leader_only: true

container_commands대신 해야합니다commands


누군가 새로운 리더가 발생했을 때 leader_only 자동 확장 문제에 대해 궁금해했습니다. 댓글에 응답하는 방법을 알아낼 수없는 것 같지만 다음 링크를 참조하세요. http://blog.paulopoiati.com/2013/08/25/running-cron-in-elastic-beanstalk-auto-scaling- 환경/


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: "* * * * *"

그게 다야!

App\Console\Kernel이제 모든 작업 이 실행됩니다.

자세한 지침 및 설명 : https://github.com/dusterio/laravel-aws-worker

Laravel 내부에서 작업을 작성하는 방법 : https://laravel.com/docs/5.4/scheduling


다음은 솔루션에 대한 전체 설명입니다.

http://blog.paulopoiati.com/2013/08/25/running-cron-in-elastic-beanstalk-auto-scaling-environment/


축소시 Auto Scaling에서 특정 인스턴스를 종료 할 수 있는지 여부를 제어하려면 인스턴스 보호를 사용하십시오. Auto Scaling 그룹 또는 개별 Auto Scaling 인스턴스에서 인스턴스 보호 설정을 활성화 할 수 있습니다. Auto Scaling에서 인스턴스를 시작할 때 인스턴스는 Auto Scaling 그룹의 인스턴스 보호 설정을 상속합니다. Auto Scaling 그룹 또는 Auto Scaling 인스턴스에 대한 인스턴스 보호 설정은 언제든지 변경할 수 있습니다.

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


PHP 파일이 cron을 통해 실행되어야하고 NAT 인스턴스를 설정 한 경우 NAT 인스턴스에 cronjob을 넣고 wget을 통해 php 파일을 실행할 수있는 경우 이에 대한 또 다른 해결책이 있습니다.


그래서 우리는 한동안 이것으로 어려움을 겪고 있었고 AWS 담당자와 약간의 논의를 거쳐 마침내 최고의 솔루션이라고 생각하는 것이 무엇인지 생각해 냈습니다.

cron.yaml과 함께 작업자 계층을 사용하는 것이 가장 쉬운 해결 방법입니다. 그러나 문서에서 명확하지 않은 것은 실제로 작업을 실행하는 데 사용하는 SQS 대기열 끝에 작업을 배치한다는 것 입니다. 크론 작업이 시간에 민감한 경우 (많은 경우) 대기열 크기에 따라 다르기 때문에 허용되지 않습니다. 한 가지 옵션은 cron 작업을 실행하기 위해 완전히 별도의 환경을 사용하는 것입니다.

목록의 첫 번째 인스턴스인지 확인하는 것과 같은 다른 옵션 중 일부도 이상적이지 않습니다. 현재 첫 번째 인스턴스가 종료되는 중이면 어떻게됩니까?

인스턴스 보호에도 문제가 발생할 수 있습니다. 인스턴스가 잠기거나 고정되면 어떻게됩니까?

이해해야 할 중요한 것은 AWS 자체에서 cron.yaml 기능을 관리하는 방법입니다. Dynamo 테이블을 사용하여 "리더 선택"을 처리하는 SQS 데몬이 있습니다. 이 테이블에 자주 쓰며, 현재 리더가 잠시 동안 글을 쓰지 않으면 다음 인스턴스가 리더로 이어집니다. 이것은 데몬이 SQS 대기열로 작업을 시작할 인스턴스를 결정하는 방법입니다.

우리는 우리 자신의 기능을 다시 작성하려고하지 않고 기존 기능의 용도를 변경할 수 있습니다. 여기에서 전체 솔루션을 볼 수 있습니다 : https://gist.github.com/dorner/4517fe2b8c79ccb3971084ec28267f27

Ruby에 있지만 AWS SDK가있는 다른 언어에 쉽게 적용 할 수 있습니다. 기본적으로 현재 리더를 확인한 다음 상태를 확인하여 상태가 양호한 지 확인합니다. 양호한 상태의 현재 리더가있을 때까지 반복되며 현재 인스턴스가 리더 인 경우 작업을 실행합니다.


여기 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

envvars는 파일에 대한 환경 변수를 가져옵니다. 위와 같이 tmp / sendemail.log에서 출력을 디버깅 할 수 있습니다.

이것이 우리를 확실히 도왔던 누군가에게 도움이되기를 바랍니다!


user1599237 의 답변 원칙에 따라 cron 작업이 모든 인스턴스에서 실행되도록 한 다음 대신 작업의 시작 부분에서 실행 허용 여부를 결정하고 다른 솔루션을 만들었습니다.

실행중인 인스턴스를보고 (그리고 AWS 키와 암호를 저장해야하는) 대신 모든 인스턴스에서 이미 연결중인 MySQL 데이터베이스를 사용하고 있습니다.

단점은없고 장점 만 있습니다.

  • 추가 인스턴스 또는 비용 없음
  • 견고한 솔루션-이중 실행 가능성 없음
  • 확장 가능-인스턴스가 확장 및 축소 될 때 자동으로 작동합니다.
  • 장애 조치-인스턴스에 장애가 발생하면 자동으로 작동합니다.

또는 데이터베이스 대신 일반적으로 공유되는 파일 시스템 (예 : NFS 프로토콜을 통한 AWS EFS)사용할 수도 있습니다 .

The following solution is created within the PHP framework Yii but you can easily adapt it for another framework and language. Also the exception handler Yii::$app->system is a module of my own. Replace it with whatever you are using.

/**
 * Obtain an exclusive lock to ensure only one instance or worker executes a job
 *
 * Examples:
 *
 * `php /var/app/current/yii process/lock 60 empty-trash php /var/app/current/yii maintenance/empty-trash`
 * `php /var/app/current/yii process/lock 60 empty-trash php /var/app/current/yii maintenance/empty-trash StdOUT./test.log`
 * `php /var/app/current/yii process/lock 60 "empty trash" php /var/app/current/yii maintenance/empty-trash StdOUT./test.log StdERR.ditto`
 * `php /var/app/current/yii process/lock 60 "empty trash" php /var/app/current/yii maintenance/empty-trash StdOUT./output.log StdERR./error.log`
 *
 * Arguments are understood as follows:
 * - First: Duration of the lock in minutes
 * - Second: Job name (surround with quotes if it contains spaces)
 * - The rest: Command to execute. Instead of writing `>` and `2>` for redirecting output you need to write `StdOUT` and `StdERR` respectively. To redirect stderr to stdout write `StdERR.ditto`.
 *
 * Command will be executed in the background. If determined that it should not be executed the script will terminate silently.
 */
public function actionLock() {
    $argsAll = $args = func_get_args();
    if (!is_numeric($args[0])) {
        \Yii::$app->system->error('Duration for obtaining process lock is not numeric.', ['Args' => $argsAll]);
    }
    if (!$args[1]) {
        \Yii::$app->system->error('Job name for obtaining process lock is missing.', ['Args' => $argsAll]);
    }

    $durationMins = $args[0];
    $jobName = $args[1];
    $instanceID = null;
    unset($args[0], $args[1]);

    $command = trim(implode(' ', $args));
    if (!$command) {
        \Yii::$app->system->error('Command to execute after obtaining process lock is missing.', ['Args' => $argsAll]);
    }

    // If using AWS Elastic Beanstalk retrieve the instance ID
    if (file_exists('/etc/elasticbeanstalk/.aws-eb-system-initialized')) {
        if ($awsEb = file_get_contents('/etc/elasticbeanstalk/.aws-eb-system-initialized')) {
            $awsEb = json_decode($awsEb);
            if (is_object($awsEb) && $awsEb->instance_id) {
                $instanceID = $awsEb->instance_id;
            }
        }
    }

    // Obtain lock
    $updateColumns = false;  //do nothing if record already exists
    $affectedRows = \Yii::$app->db->createCommand()->upsert('system_job_locks', [
        'job_name' => $jobName,
        'locked' => gmdate('Y-m-d H:i:s'),
        'duration' => $durationMins,
        'source' => $instanceID,
    ], $updateColumns)->execute();
    // The SQL generated: INSERT INTO system_job_locks (job_name, locked, duration, source) VALUES ('some-name', '2019-04-22 17:24:39', 60, 'i-HmkDAZ9S5G5G') ON DUPLICATE KEY UPDATE job_name = job_name

    if ($affectedRows == 0) {
        // record already exists, check if lock has expired
        $affectedRows = \Yii::$app->db->createCommand()->update('system_job_locks', [
                'locked' => gmdate('Y-m-d H:i:s'),
                'duration' => $durationMins,
                'source' => $instanceID,
            ],
            'job_name = :jobName AND DATE_ADD(locked, INTERVAL duration MINUTE) < NOW()', ['jobName' => $jobName]
        )->execute();
        // The SQL generated: UPDATE system_job_locks SET locked = '2019-04-22 17:24:39', duration = 60, source = 'i-HmkDAZ9S5G5G' WHERE job_name = 'clean-trash' AND DATE_ADD(locked, INTERVAL duration MINUTE) < NOW()

        if ($affectedRows == 0) {
            // We could not obtain a lock (since another process already has it) so do not execute the command
            exit;
        }
    }

    // Handle redirection of stdout and stderr
    $command = str_replace('StdOUT', '>', $command);
    $command = str_replace('StdERR.ditto', '2>&1', $command);
    $command = str_replace('StdERR', '2>', $command);

    // Execute the command as a background process so we can exit the current process
    $command .= ' &';

    $output = []; $exitcode = null;
    exec($command, $output, $exitcode);
    exit($exitcode);
}

This is the database schema I'm using:

CREATE TABLE `system_job_locks` (
    `job_name` VARCHAR(50) NOT NULL,
    `locked` DATETIME NOT NULL COMMENT 'UTC',
    `duration` SMALLINT(5) UNSIGNED NOT NULL COMMENT 'Minutes',
    `source` VARCHAR(255) NULL DEFAULT NULL,
    PRIMARY KEY (`job_name`)
)

참고URL : https://stackoverflow.com/questions/14077095/aws-elastic-beanstalk-running-a-cronjob

반응형