AWS Elastic Beanstalk、cronjobを実行


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ジョブを一度に
起動します

はい!間違いなくleader_onlyフラグを使用して、コマンドを変更してみます。
Onema

回答:


96

これは、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というファイルを作成します。このファイルに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へのパスです。次に、some-php-script-hereをphpファイルへのパスに置き換えます。これは明らかに、cronジョブがPHPファイルを実行する必要があることを前提としています。

また、コメントのように、some_cron_job.txtファイルの最後に改行があることを確認してください。そうしないと、cronは実行されません。

更新: Elastic Beanstalkがインスタンスをスケールアップするときに、このソリューションに問題があります。たとえば、cronジョブが実行されているインスタンスが1つあるとします。トラフィックが増加するため、Elastic Beanstalkは最大2つのインスタンスにスケーリングします。leader_onlyは、2つのインスタンス間で1つのcronジョブのみが実行されるようにします。トラフィックが減少し、Elastic Beanstalkによって1つのインスタンスに縮小されます。ただし、Elastic Beanstalkは、2番目のインスタンスを終了する代わりに、リーダーであった最初のインスタンスを終了します。終了した最初のインスタンスでのみ実行されていたため、cronジョブは実行されていません。 以下のコメントを参照してください。

更新2: 以下のコメントからこれを明確にするだけです:AWSはインスタンスの自動終了に対する保護を備えています。リーダーインスタンスで有効にするだけで、問題ありません。–NicolásArévalo16年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ファイルを持ちます。リーダーが変わった場合、再デプロイするだけで、cronが修正され、もう一度実行されるようになります。
Willem Renzema 2013年

4
leader_only財産に依存しないことをお勧めします。これはデプロイ時にのみ使用され、スケールダウンするか、「リーダー」インスタンスが失敗した場合、問題の参照
arnaslu

2
これを行わないでください。それはあまりにも信頼できません。これを機能させる唯一の方法は、マイクロインスタンスを実行し、そこからCURLを使用してcronジョブを実行することです。これにより、1つのインスタンスでのみ実行され、cronがインストールされているリーダーが終了しないことが保証されます。
ベンシンクレア2014年

1
私は小さなルビスクリプトでこれを修正しようとしました、それはここで見つけることができます:github.com/SocialbitGmbH/AWSBeanstalkLeaderManager
Thomas Kekeisen

8
AWSは、インスタンスの自動終了に対する保護を備えています。リーダーインスタンスで有効にするだけで、問題ありません。
ニコラス・アレバロ

58

これは、現在(2015以降)の正式な方法です。これを最初に試してください。これは、現在利用できる最も簡単な方法であり、最も信頼できる方法でもあります。

現在のドキュメントによると、いわゆるワーカー層で定期的なタスクを実行できます。

ドキュメントの引用:

AWS Elastic Beanstalkは、コンテナ名に「v1.2.0」を含むソリューションスタックで事前定義された構成を実行している環境で、ワーカー環境層の定期的なタスクをサポートします。新しい環境を作成する必要があります。

cron.yamlに関する部分も興味深いです。

定期的なタスクを呼び出すには、アプリケーションソースバンドルにcron.yamlファイルをルートレベルで含める必要があります。ファイルには、スケジュールする定期的なタスクに関する情報が含まれている必要があります。この情報は、標準のcrontab構文を使用して指定します。

更新:この作業を行うことができました。私たちの経験からのいくつかの重要な落とし穴(Node.jsプラットフォーム)を以下に示します。

  • 古いバージョンは正しく機能しないため、cron.yamlファイルを使用するときは、最新のawsebcliを使用してください。
  • 古い環境を複製するだけでなく、新しい環境を作成することも重要です(少なくとも私たちの場合はそうでした)。
  • EC2ワーカー層インスタンスでCRONがサポートされていることを確認したい場合はeb ssh、それにsshして()、実行しcat /var/log/aws-sqsd/default.logます。として報告する必要がありaws-sqsd 2.0 (2015-02-18)ます。2.0バージョンがない場合、環境の作成中に何か問題が発生し、上記のように新しい環境を作成する必要があります。

2
cron.yamlについては、すばらしいブログ投稿があります。AmazonWeb サービス(AWS)でのcronジョブの実行Elastic Beanstalk —中
jwako

5
これをありがとう-新人の質問-私のcronは、今後のカレンダーイベントについて、1時間に2回、私のWebアプリのデータベースをチェックし、そのときにリマインダーメールを送信する必要があります。ここでの最良の設定は何ですか、私はcron.yaml URLを自分のWebアプリのルートにポイントする必要がありますか または、ワーカーのenvアプリにデータベースへのアクセスを許可する必要がありますか?これについてはほとんどありません!
クリスチャン2015年

5
@christianこれを行う方法では、同じアプリを2つの異なる環境で実行します(したがって、特別な設定は必要ありません)-ワーカーと一般的なWebサーバーです。ワーカー環境には、アプリが検索するENV変数を設定することで有効になるいくつかの特別なルートがあります。このようにして、通常のアプリとコードベースを共有する贅沢さを保ちながら、cron.yamlにワーカーのみの特別なルートを設定できます。ワーカーアプリは、Webサーバーと同じリソース(データベース、モデルなど)に簡単にアクセスできます
xaralis

1
@JaquelinePassos v1.2.0はソリューションスタックバージョンです。新しい環境を作成するときに、作成するソリューションスタックのバージョンを選択できます。v1.2.0より新しいものなら何でも実行できます。URLについては、ファイルパスではなく、アプリケーションがリッスンするURLである必要があります。Django管理コマンドを実行することはできません。HTTPリクエストのみを実行します。
xaralis

4
私には不明確なことの1つは、cron.yamlを介してcronジョブを実行するためだけに追加のEC2マシンを割り当てる必要を回避する方法があるかどうかです。理想的には、HTTPリクエストを処理するマシン(つまり、Web層)と同じマシンで実行します。
ヴェンツェルヤコブ2016年

31

jamiebの応答に関して、そして通常のように、「leader_only」プロパティを使用して、1つのEC2インスタンスのみがcronジョブを実行するようにすることができます。

http://docs.amazonwebservices.com/elasticbeanstalk/latest/dg/customize-containers-ec2.htmlからの引用:

あなたはleader_onlyを使用することができます。1つのインスタンスがAuto Scalingグループのリーダーとして選択されます。leader_only値がtrueに設定されている場合、コマンドは、リーダーとしてマークされているインスタンスでのみ実行されます。

私は私のebで同様のことを達成しようとしているので、解決したら投稿を更新します。

更新:

さて、私は今、次のeb設定を使用してcronジョブを実行しています:

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を実行しているインスタンスが自動スケーリングによって終了しないようにするにはどうすればよいですか?デフォルトでは、最も古いインスタンスを終了します。
セバスチャン

1
それは私がまだ解決できていない問題です。現在のリーダーがEBによって終了された場合、leader_onlyコマンドが新しいリーダーに適用されないという、amazonの機能の欠陥として私を襲います。何か思いついたらシェアしてね!
beterthanlife 2013

7
そこで、私は(最終的に)リーダーが自動スケーリングによって終了するのを防ぐ方法を発見しました-カスタムの自動スケーリング終了ポリシー。docs.aws.amazon.com/AutoScaling/latest/DeveloperGuide/…を
beterthanlife

1
@Nateあなたはおそらくこれまでにこれを理解したでしょうが、これらが実行される順序の私の読み取りに基づいて、「コマンド」は「container_commands」の前に実行されるため、ファイルを作成してから削除し、次にcrontabを実行してみます。
clearf

1
@Sebastienは、最も古いインスタンスを保持するために、ここで私が何をすべきかを示します。1-インスタンスの終了保護をENBABLEに変更します。2-Auto Scale Groupに移動し、EBS環境IDを見つけて、[編集]をクリックし、終了ポリシーを「NewestInstance」に変更します
Ronaldo Bahia

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");
}

したがって、これとその動作方法について説明します...すべてのEC2インスタンスで通常行うように、crontabからスクリプトを呼び出します。各スクリプトには、最初にこれが含まれています(または私が使用するように、それぞれに単一のファイルが含まれています)。これにより、ElasticBeanstalkオブジェクトが確立され、すべてのインスタンスのリストが取得されます。リストの最初のサーバーのみを使用し、それ自体が一致するかどうかを確認します。一致する場合は続行し、一致しない場合は終了して終了します。私がチェックしたところ、返されたリストは一貫しているようです。技術的には、各インスタンスがスケジュールされたcronを実行するため、1分程度だけ一貫している必要があります。変更されたとしても、その小さなウィンドウにのみ関連するため、問題にはなりません。

これは決して洗練された方法ではありませんが、追加のサービスによるコストの増加や専用のEC2インスタンスの必要がなく、障害が発生した場合にフェイルオーバーするという特定のニーズに適していました。私たちのcronスクリプトはSQSに配置されるメンテナンススクリプトを実行し、クラスター内の各サーバーは実行を支援します。それがあなたのニーズに合うなら、少なくともこれはあなたにあなたに別の選択肢を与えるかもしれません。

-デイビー


php_uname( 'n')がプライベートDNS名(例:ip-172.24.55.66)を返すことがわかりましたが、これは探しているインスタンスIDではありません。php_uname()を使用する代わりに、私はこれを使用してしまいました:$instanceId = file_get_contents("http://instance-data/latest/meta-data/instance-id"); 次に、その$ instanceId 変数を使用 して比較を行います。
Valorum 2015

1
Instances配列が、Describe呼び出しごとに同じ順序で表示されるという保証はありますか?最初に並べ替えられたエントリが現在のinstanceIdかどうかを確認する前に、各エントリの['Id']フィールドを配列に抽出し、PHPで並べ替えることをお勧めします。
ガブリエル

この回答に基づいて、私はこのソリューションを作りました:stackoverflow.com/questions/14077095/…-非常に似ていますが、二重実行される可能性はありません。
TheStoryCoder

11

私はAWSサポートエージェントと話をしました、そしてこれが私たちが私のためにこれを機能させる方法でした。2015ソリューション:

your_file_name.configを使用して、.ebextensionsディレクトリにファイルを作成します。設定ファイルの入力:

ファイル:
  "/etc/cron.d/cron_example":
    モード:「000644」
    所有者:ルート
    グループ:ルート
    コンテンツ:|
      * * * * * root /usr/local/bin/cron_example.sh

  "/usr/local/bin/cron_example.sh":
    モード:「000755」
    所有者:ルート
    グループ:ルート
    コンテンツ:|
      #!/ bin / bash

      /usr/local/bin/test_cron.sh || 出口
      echo "Cron running at" `date` >> /tmp/cron_example.log
      #1つのインスタンスでのみ実行されるタスクを実行します...

  "/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自動スケーリング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

このソリューションには2つの欠点があります。

  1. 以降のデプロイメントでは、Beanstalkは既存のcronスクリプトの名前を.bakに変更しますが、cronは引き続きそれを実行します。Cronが同じマシンで2回実行されます。
  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ロールを設定する必要はありません。


7

Railsを使用している場合は、everyday-elasticbeanstalk gemを使用できます。これにより、すべてのインスタンスまたは1つのインスタンスのみでcronジョブを実行できます。「リーダー」インスタンスが1つだけであることを毎分チェックして確認し、存在しない場合は1つのサーバーを「リーダー」に自動的に昇格させます。Elastic Beanstalkにはデプロイ中のリーダーの概念しかなく、スケーリング中にいつでもインスタンスをシャットダウンできるため、これが必要です。

更新 私はAWS OpsWorksの使用に切り替え、このgemを維持しなくなりました。Elastic Beanstalkの基本で利用できる以上の機能が必要な場合は、OpsWorksに切り替えることを強くお勧めします。


OpsWorksを使用してどのように解決したか教えていただけませんか?cronジョブを実行するカスタムレイヤーを実行していますか?
Tommie

ええ、私は1つのサーバーでのみ実行されるadmin / cronレイヤーを持っています。私はすべてのcronジョブを保持するカスタムクックブックをセットアップしました。AWSのドキュメントはdocs.aws.amazon.com/opsworks/latest/userguide/…にあります。
2014

@dignoe OpsWorksを使用してcronジョブを実行するために1つのサーバーを割り当てた場合、Elastic Beanstalkを使用して同じことですが、1つのサーバーの環境を使用してcronジョブを実行できます。ロードバランサーを使用しても、インスタンスの最大値と最小値を1に設定すると、少なくともサーバーインスタンスを常に節約できます。
Jose Nobile、2015

6

Elastic Beanstalkでcronジョブを実行したくありません。複数のアプリケーションインスタンスがあるため、これは競合状態やその他の奇妙な問題を引き起こす可能性があります。私は最近これについてブログに書いています(ページの4番目または5番目のヒント)。短いバージョン:アプリケーションに応じて、SQSなどのジョブキューまたはiron.ioなどのサードパーティソリューションを使用します。


SQSは、コードが1回だけ実行されることを保証しません。iron.ioサイトが気に入ったので、チェックしてみます。
Nathan H

また、ブログ投稿では、RDSでInnoDBを使用することをお勧めします。RDSのテーブルを使用してタスクを保存し、InnoDBの「SELECT ... FOR UPDATE」機能を使用して、1つのサーバーだけがこれらのタスクを実行するようにします。アプリは、cronジョブやユーザー操作なしでSQSにどのように連絡しますか?
James Alday、2014

1
@JamesAldayこのSOの質問はかなり古いです。上記のコメントを書いてから、AWSは実行中のサーバーの1つをマスターとして選択することにより、Elastic Beanstalkでcronジョブを処理するエレガントな方法を導入しました。そうは言っても、cron + MySQLをジョブキューとして誤用しているようです。ただし、具体的な推奨事項を提示する前に、アプリについて詳しく知る必要があります。
jamieb 2014

実行するジョブのテーブルをチェックするcronを介して実行するスクリプトがあります。トランザクションを使用すると、複数のサーバーが同じジョブを実行できなくなります。私はSQSを調査しましたが、配布する代わりにすべてのスクリプトを実行するマスターサーバーが必要です。それでも、同じスクリプトを複数回実行しないようにするためのロジックを作成する必要があります。しかし、ユーザーの操作やcronなしでタスクを実行する方法についてはまだ混乱しています。アプリがキュー内のタスクを実行するきっかけは何ですか。
James Alday、2014

4

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"
      #Symfonyコマンドを5分ごとに実行(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形式とは異なります。


ここでの1つの問題は、Elastic Beanstalk EC2インスタンスにはデフォルトでSMTPサービスがセットアップされていないため、ここでMAILTOオプションが機能しない可能性があることです。
ジャスティンフィンケルスタイン

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ジョブを実行します。cron.yamlファイルを作成し、ルートフォルダーに配置します。私が抱えていた1つの問題(cronが実行されていないように見えた後)は、私のCodePipelineにdynamodbの変更を実行する権限がないことを発見したことです。これに基づいて、IAM->ロール-> yourpipelineの下にFullDynamoDBアクセスを追加し、再デプロイ(elastic beanstalk)した後、完全に機能しました。


1

ソリューションの完全な説明は次のとおりです。

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


ASG内に一意のインスタンスを作成するためにleader_onlyを使用しないでください。ASGは、特定のインスタンスを保持できることを保証するのではなく、サービス中のインスタンスの数を保証するだけです。EBヘルスチェックに失敗したため、リーダーインスタンスが終了する場合があります。
2018年

1

そのため、しばらくの間これに取り組んできましたが、AWS担当者との話し合いの末、ようやく私は最良の解決策だと思いました。

cron.yamlでワーカー層を使用することは、間違いなく最も簡単な修正です。ただし、ドキュメントで明確になっていないのは、これにより、実際にジョブを実行するために使用しているSQSキューの最後にジョブが配置されることです。cronジョブが時間の影響を受けやすい場合(多くの場合)、キューのサイズに依存するため、これは受け入れられません。1つのオプションは、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データベースを使用しています。

それにはマイナス面はありませんが、良い面だけがあります:

  • 追加のインスタンスや費用はありません
  • 強固なソリューション-二重実行の可能性なし
  • スケーラブル-インスタンスが拡大または縮小されると自動的に機能します
  • フェイルオーバー-インスタンスに障害が発生した場合に自動的に機能します

または、データベースの代わりに、一般的に共有されるファイルシステム(NFSプロトコルを介したAWS EFSなど)を使用することもできます。

次のソリューションは、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.