cronjob을 실행하는 AWS Elastic Beanstalk


89

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

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

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

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

어떤 아이디어?


1
명령이 맞습니까? 내 말은 ... 명령 : echo "* / 1 * * * * root php /etc/httpd/myscript.php"> /etc/cron.d/something 어느 쪽이든 사용하는 것이 좋습니다. leader_only 플래그, 그렇지 않으면 모든 시스템은 한 번에이 cron 작업을 실행됩니다
aldrinleal

예! 확실히 leader_only 플래그를 사용하여 명령을 변경해 보겠습니다.
Onema

답변:


97

다음은 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

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에


12
나는 당신의 제안을 얼마 동안 사용해 왔으며 최근에 리더가 어떻게 든 전환하여 여러 인스턴스가 cron을 실행하는 문제에 직면했습니다. 이 문제를 해결하기 위해, 나는 변화 01_some_cron_job02_some_cron_job및 추가 01_remove_cron_jobs다음과 : command: "rm /etc/cron.d/cron_jobs || exit 0". 이렇게하면 모든 배포 후에 리더 만 cron_jobs파일 을 갖게 됩니다. 리더가 변경되면 재배포 만하면 크론이 다시 실행되도록 수정됩니다.
Willem Renzema 2013

4
나는 leader_only재산 에 의존하지 말 것을 제안 합니다. 배포 중에 만 사용되며 축소하거나 "리더"인스턴스가 실패하는 경우 문제 참조
arnaslu

2
이러지마 너무 신뢰할 수 없습니다. 이 작업을 수행하는 유일한 방법은 마이크로 인스턴스를 실행하고 CURL을 사용하여 cron 작업을 실행하는 것입니다. 이렇게하면 하나의 인스턴스 만 실행되고 크론이 설치된 리더가 종료되지 않습니다.
Ben Sinclair 2014 년

1
나는 작은 루비 스크립트를 사용하여이 문제를 해결하려고, 당신은 여기를 찾을 수 있습니다 github.com/SocialbitGmbH/AWSBeanstalkLeaderManager
토마스 Kekeisen

8
AWS는 이제 자동 인스턴스 종료로부터 보호합니다. 리더 인스턴스에서 활성화하기 만하면됩니다.
Nicolás Arévalo

58

이것이 지금 공식적인 방법입니다 (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 버전이없는 경우 환경을 만들 때 문제가 발생하여 위에서 설명한대로 새 환경을 만들어야합니다.

2
: cron.yaml 소개하는 멋진 블로그 게시물이 - 중간 아마존 웹 서비스 (AWS) 탄성 콩 줄기에 cron 작업을 실행
jwako

5
감사합니다-신인 질문-내 웹 앱의 데이터베이스에서 예정된 캘린더 이벤트를 한 시간에 두 번 확인하고 확인되면 알림 이메일을 보내려면 크론이 필요합니다. 여기서 가장 좋은 설정은 무엇입니까? cron.yaml URL이 내 웹 앱의 경로를 가리켜 야합니까? 아니면 작업자 환경 앱에 데이터베이스에 대한 액세스 권한을 부여해야합니까? 이것에 대해서는 거의 없습니다!
christian

5
@christian 우리가하는 방식에 따라 두 개의 서로 다른 환경에서 동일한 앱이 실행되고 있습니다 (따라서 특별한 구성이 필요하지 않음)-작업자 및 공통 웹 서버 1. 작업자 환경에는 앱이 찾는 ENV 변수를 설정하여 일부 특수 경로가 활성화됩니다. 이렇게하면 cron.yaml에 특수 작업자 전용 경로를 설정하는 동시에 일반 앱과 공유 코드베이스를 사용할 수 있습니다. 담당 직원의 응용 프로그램은 쉽게 웹 서버와 동일한 리소스에 액세스 할 수 있습니다 : 데이터베이스, 모델, 등
xaralis

1
@JaquelinePassos v1.2.0은 솔루션 스택 버전입니다. 새 환경을 만들 때 만들려는 솔루션 스택 버전을 선택할 수 있어야합니다. v1.2.0보다 새로운 것은 무엇이든해야합니다. URL과 관련하여 파일 경로가 아니라 애플리케이션이 수신하는 URL이어야합니다. Django 관리 명령을 실행할 수 없으며 HTTP 요청 만 수행합니다.
xaralis

4
나에게 분명하지 않은 한 가지는 cron.yaml을 통해 cron 작업을 실행하기 위해 추가 EC2 머신을 할당하지 않아도되는 방법이 있는지 여부입니다. 이상적으로는 HTTP 요청 (예 : 웹 계층)을 서비스하는 시스템과 동일한 시스템에서 실행됩니다.
Wenzel Jakob

31

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

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을 설정 한 다음 나중에 임시 파일을 삭제합니다. 도움이 되었기를 바랍니다.


3
이 crontab을 실행하는 인스턴스가 자동 확장에 의해 종료되지 않도록하려면 어떻게해야합니까? 기본적으로 가장 오래된 인스턴스를 종료합니다.
Sebastien 2013

1
그것은 내가 아직 해결하지 못한 문제입니다. 현재 리더가 EB에 의해 종료 될 때 leader_only 명령이 새 리더에 적용되지 않는다는 것이 아마존 기능의 결함이라고 생각합니다. 뭔가 떠오르면 공유 해주세요!
beterthanlife 2015

7
그래서 (마지막으로) 리더가 자동 확장에 의해 종료되는 것을 방지하는 방법-사용자 지정 자동 확장 종료 정책을 발견했습니다. docs.aws.amazon.com/AutoScaling/latest/DeveloperGuide/…
beterthanlife

1
@Nate 당신은 아마 지금까지 이것을 알아 냈을 것입니다. 그러나 이것들이 실행되는 순서를 읽었을 때, "commands"는 "container_commands"보다 먼저 실행되므로 파일을 만든 다음 삭제 한 다음 crontab을 실행 해보십시오. .
clearf 2014 년

1
@Sebastien 가장 오래된 intance를 유지하기 위해 내가하는 일은 다음과 같습니다. 1-intance의 종료 보호를 ENBABLE로 변경합니다. 2-Auto Scale Group으로 이동하여 EBS 환경 ID를 찾아 편집을 클릭하고 종료 정책을 "NewestInstance"로 변경합니다.
Ronaldo Bahia

12

위에서 언급했듯이 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에 배치되는 유지 관리 스크립트를 실행하고 클러스터의 각 서버는 실행을 돕습니다. 적어도 이것은 귀하의 필요에 맞는 경우 대체 옵션을 제공 할 수 있습니다.

-데이비


php_uname ( 'n')이 찾고있는 인스턴스 ID가 아닌 프라이빗 DNS 이름 (예 : ip-172.24.55.66)을 반환합니다. php_uname ()을 사용하는 대신 $instanceId = file_get_contents("http://instance-data/latest/meta-data/instance-id"); 다음을 사용했습니다. 그런 다음 $ instanceId var를 사용하여 비교를 수행합니다.
Valorum

1
인스턴스 배열이 각 Describe 호출에서 동일한 순서를 제공한다는 보장이 있습니까? 첫 번째 정렬 된 항목이 현재 instanceId인지 확인하기 전에 각 항목의 [ 'Id'] 필드를 배열로 추출하고 PHP에서 정렬하는 것이 좋습니다.
Gabriel

이 답변을 바탕으로이 솔루션을 만들었습니다. stackoverflow.com/questions/14077095/…- 매우 유사하지만 이중 실행 가능성이 없습니다.
TheStoryCoder

11

저는 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 || 출구
      echo ""`date` >> /tmp/cron_example.log에 실행중인 크론
      # 이제 하나의 인스턴스에서만 실행되어야하는 작업을 수행합니다 ...

  "/usr/local/bin/test_cron.sh":
    모드 : "000755"
    소유자 : 루트
    그룹 : 루트
    내용 : |
      #! / bin / bash

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

      # Auto Scaling 그룹 이름을 찾습니다.
      ASG = ʻaws ec2 describe-tags --filters "Name = resource-id, Values ​​= $ INSTANCE_ID"\
        --region $ REGION-출력 텍스트 | awk '/ aws : autoscaling : groupName / {print $ 5}'`

      # 그룹에서 첫 번째 인스턴스 찾기
      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 역할을 설정할 필요가 없습니다.


7

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

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


OpsWorks를 사용하여 어떻게 해결했는지 알려주시겠습니까? 크론 작업을 수행하는 사용자 지정 계층을 실행하고 있습니까?
Tommie 2014

예, 하나의 서버에서만 실행되는 관리자 / cron 계층이 있습니다. 모든 크론 작업을 포함하는 사용자 지정 쿡북을 설정했습니다. AWS는 docs.aws.amazon.com/opsworks/latest/userguide/…에 가이드가 있습니다 .
dignoe

@dignoe OpsWorks를 사용하여 cron 작업을 실행하기 위해 하나의 서버를 할당하면 Elastic Beanstalk를 사용하는 것과 동일한 방식으로 하나의 서버가있는 환경을 사용하여 cron 작업을 실행할 수 있습니다. Load Balancer를 사용하더라도 최대 및 최소 인스턴스를 1로 설정하여 항상 서버 인스턴스를 최소한 보존합니다.
Jose Nobile

6

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


SQS는 코드가 한 번만 실행된다는 것을 보장하지 않습니다. iron.io 사이트가 마음에 들어서 확인해 보겠습니다.
Nathan H

또한 블로그 게시물에서 RDS에서 InnoDB를 사용하는 것이 좋습니다. RDS의 테이블을 사용하여 작업을 저장하고 InnoDB "SELECT ... FOR UPDATE"기능을 사용하여 하나의 서버 만 해당 작업을 실행하도록합니다. 크론 작업이나 사용자 상호 작용없이 앱이 SQS에 어떻게 연결합니까?
James Alday 2014

1
@JamesAlday이 SO 질문은 꽤 오래되었습니다. 위의 의견을 작성한 이후 AWS는 실행중인 서버 중 하나를 마스터로 선택하여 Elastic Beanstalk에서 크론 작업을 처리하는 우아한 방법을 도입했습니다. 그래도 cron + MySQL을 작업 대기열로 오용하는 것 같습니다. 그래도 구체적인 권장 사항을 제공하려면 먼저 앱에 대해 많이 알아야합니다.
jamieb 2014

실행할 작업에 대한 테이블을 확인하는 cron을 통해 실행되는 스크립트가 있습니다. 트랜잭션을 사용하면 여러 서버에서 동일한 작업을 실행할 수 없습니다. SQS를 살펴 봤지만 배포하는 대신 모든 스크립트를 실행하는 마스터 서버가 필요하며 동일한 스크립트를 여러 번 실행하지 않도록 논리를 작성해야합니다. 하지만 사용자 상호 작용이나 크론없이 작업을 실행하는 방법에 대해 여전히 혼란 스럽습니다. 앱이 대기열에서 작업을 실행하도록 트리거하는 이유는 무엇입니까?
James Alday

5

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


3

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 형식과 다릅니다.


여기서 한 가지 문제는 Elastic Beanstalk EC2 인스턴스에 기본적으로 SMTP 서비스가 설정되어 있지 않으므로 여기의 MAILTO 옵션이 작동하지 않을 수 있다는 것입니다.
Justin Finkelstein

3

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



2

Amazon의 최신 예제가 가장 쉽고 효율적입니다 (주기적 작업).

https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features-managing-env-tiers.html

크론 작업을 실행하기 위해 별도의 작업자 계층을 생성합니다. cron.yaml 파일을 만들고 루트 폴더에 저장합니다. 내가 가진 한 가지 문제는 (cron이 실행되지 않는 것 같았던 후) 내 CodePipeline에 dynamodb 수정을 수행 할 권한이 없다는 사실입니다. 이를 기반으로 IAM-> 역할-> yourpipeline 아래에 FullDynamoDB 액세스를 추가하고 (탄력적 인 Beantalk) 재배포 한 후 완벽하게 작동했습니다.


1

ASG 내에서 고유 한 인스턴스를 생성하기 위해 leader_only를 사용하지 마십시오. ASG는 특정 인스턴스를 유지할 수 있음을 보장하지 않지만 서비스중인 인스턴스 수만 보장합니다. EB 상태 확인 실패로 인해 리더 인스턴스가 종료 될 수 있습니다.
mst

1

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

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

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

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

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

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

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


0

축소시 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


0

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


0

여기 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에서 출력을 디버깅 할 수 있습니다.

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


0

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

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

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

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

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

다음 솔루션은 PHP 프레임 워크 Yii 내에서 생성 되지만 다른 프레임 워크 및 언어에 쉽게 적용 할 수 있습니다. 또한 예외 처리기 Yii::$app->system는 내 자신의 모듈입니다. 사용중인 것으로 교체하십시오.

/**
 * 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);
}

이것은 내가 사용하는 데이터베이스 스키마입니다.

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`)
)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.