Lambda と CloudWatch Events を利用した定期ジョブを Serverless で管理する

Adventarを支える技術 Advent Calendar 2019 の13日目です。

今日は定期ジョブの実行について書きます。定期ジョブというのは cron とかで定期的にプログラムを実行するやつのことです。

実行するプログラム

Adventar では、定期ジョブは一つだけしかなくて、定期的に投稿された URL のメタデータをフェッチしてくる、というものです。

Adventar は自分の担当の日に URL を投稿すると、その URL のタイトルと画像を取ってきて表示する機能があります。普段は URL を投稿するリクエスト中でその処理をしていますが、これだと予約投稿のような機能を使いたいときに困ります。

例えば 12/10 に自分の番がくるけど、記事はその前に書いて hatena blog の予約投稿で 12/10 の 0 時に公開されるように設定しておく、Adventar にも前日までにその URL をセットしておく、というのはユースケースとして考えられます。なお、このとき Adventar はその日にならないと投稿した URL は投稿者以外からは見えないようになっています(ネタバレになると面白くないので)。しかし、URL が Adventar に登録された時点では hatena blog のほうもまだ公開されていない状態なので、タイトルや画像が取れません。

そこで、1時間に一回定期ジョブを実行して、その日に URL があるけどタイトルや画像が空のレコードに対して、その URL にアクセスしてメタデータを取ってくる、というジョブを動かしたいのです。

ジョブのプログラムは簡単で、こんな感じです。

https://github.com/adventar/adventar/blob/fa0714e49ea3aa60888532b60b924af0c12bbc80/batch/update_entry_site_info/main.go

CloudWatch Events と Lambda

今回は常駐するサーバーがないので cron を実行する環境がありません。いくつか方法はありますが、CloudWatch Events を使うことにしました。CloudWatch Events は cron のような書式で定期的に何かの処理をトリガーすることができるので、今回はこれを利用します。

CloudWatch Events のトリガーには様々なものが設定できますが、今回のケースは、Lambda か ECS tasks でプログラムを実行するのがよさそうです。

どちらでもよかったのですが、Lambda であれば Serverless フレームワークを使って管理することができそうだったので、今回は Lambda で実行することにしました。

追記: これを書いた後で諸事情により ECS に変更したので、現在は ECS で動いています。

Serverless を使った設定

特に難しいことはなくて、以下のような設定を書くだけです。

https://github.com/adventar/adventar/blob/fa0714e49ea3aa60888532b60b924af0c12bbc80/batch/serverless.yml

これで Serverless が Go のビルドからデプロイ、CloudWatch Events の設定まで全部やってくれます。便利。

注意しないといけないのは、時間が UTC でしか設定できないので、JST で 12/1 の 0 時から12/25 まで、という設定は書きのように少しトリッキーな記述が必要です。なお、これだと12/26にも少し実行されますが、実行されても害はないのでさぼってます。

events:
  - schedule: cron(0 15-23 30 11 ? *)
  - schedule: cron(0 * 1-25 12 ? *)

まとめ

今日は Serverless で定期ジョブを管理する方法について書きました。明日は Lambda を利用した画像のリサイズサーバーについて書きます。