AWS
cron
ElasticBeanstalk
sqs

ElasticBeanstalkのワーカー環境で定期バッチ処理を動かしてみた

はじめに

適当になりがちだけど、扱いに悩むことも多い定期バッチ処理。
ちょっとしたタスクならWebサーバ内のcronで呼び出しちゃったりするけど、コンテナ+複数インスタンスの環境で、ちょっとスマートな感じにしたい。
定番はSQS+Lambdaかと思いますが、既にWebアプリ上に機能があったりする場合、ElastiBeanstalk(以下EB)のワーカー環境が便利です!

image.png

こんな仕組みらしい。
ややこしいキューの管理はEBにお任せできちゃいます。

公式ドキュメント:AWS Elastic Beanstalk ワーカー環境

EB環境の作り方等はドキュメントをご参考ください。
AWS Elastic Beanstalk 環境を作成する

APIを用意する

ワーカー環境内のSqsdからHTTP POSTで呼び出されます。
なので、通常のWeb APIとして処理を実装すればOK。
かんたんですね!

とはいえ、同じアプリケーションをWebに公開している場合は外部からアクセスされたくないので、IP制限的なものはかけておきましょう。
こんな感じで、ネットワーク外からのアクセスをブロック。

if (request.remote_ip =~ /(^10\.|^172\.(1[6-9]|2[0-9]|3[0-1])\.|^192\.168\.)/).nil?
  redirect_to root_path
end

cron.yamlを用意する

タスクの名前と↑で用意したAPIのURL、実行スケジュールを定義したcron.yamlを作ります。
scheduleはcronの形式そのまんま。

cron.yaml
version: 1
cron:
 - name: "batch_task_1"
   url: "/batch/task_1"
   schedule: "* *1 * * *"
 - name: "batchtask_2"
   url: "/batch/task_2"
   schedule: "* */12 * * *"

cron.yamlに記述したschedule通りにEBがSQSのキューにメッセージを追加し、それを受け取ったEBのSqsdがHTTP POSTでURLを呼び出されます。
という仕組みのようです。

あとは、ソースバンドルに含めてzip形式にしてアップすれば

おまけ(ハマりポイントその1)
cron.yamlはECRのイメージ内に置いてもダメ

今回の環境はMulti ContainerなEB環境で、Dockerrun.aws.jsonのみをアップロードし、ECRからイメージを持ってきてデプロイしています。
試しにcron.yamlをプロジェクトルートに置いてECRにプッシュしてみたのですが、これではスケジュールは登録されず。
Dockerrun.aws.jsonと一緒にzip化したものをアップしてデプロイ時のソースバンドルとして指定したところ、無事登録できました。
問題なく登録できた場合は、EBダッシュボードのイベント内に

Successfully loaded 2 scheduled tasks from cron.yaml.

のように表示されるはずなので、これを確認しましょう。

おまけ(ハマりポイントその2)
Lhaplusで圧縮したらうまく読み込まれない

これは本当に原因が謎。
ワーカー環境云々というより、EBの仕様かな?
ドキュメントに書いてある通り、送る→圧縮(zip形式)フォルダー(Window環境)としたら問題なくアップできました。

おまけ(ハマりポイントその3)
なぜか5分に1度呼び出される

APIが
204 No Content
を返しているのが原因で、デッドレターキューにメッセージがいってた。。
API処理の最後に以下を追加して解決。(Rails)

    render body: nil