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