これは Livesense Advent Calendar 2023 DAY 15 の記事です。
はじめに
転職ドラフト事業部でエンジニアをしている冨田です。 今回は転職ドラフトでバッチ処理を管理するために使っているecscheduleに v0.10.0 から追加されたpruneオプションを使おうとしてハマっていることについて書きたいと思います。
バッチ処理の運用(現状)
転職ドラフトは、Ruby on Railsを使ったシンプルなモノリス構成であり、AWS上(ECS on Fargete)で動いています。日々の運用で必要な定期的に実行したい処理、いわゆるバッチ処理はRakeタスクで定義しており、Amazon EventBridgeに作成したルールからECSタスクを起動することにより実行しています。
このEventBridgeにルールを登録するために、OSSであるecscheduleを使っており、CI/CD(GitHub Actions)のパイプラインにルールの適用コマンド(ecschedule apply ~)を含めておいて、リリース時に自動で反映されるようにしています。
課題感
ecscheduleは設定ファイル(YAMLファイル)に書かれた実行時の定数(Rakeコマンド)やスケジュール(cron式)などから、簡単にEventBridgeにルールを反映させることができる素晴らしいツールです。しかし、設定ファイルから削除されたルールはEventBridgeからは自動で削除されないため、AWSコンソールに入るなどして手動で削除する必要があり少し面倒でした。できるなら本番環境を手動で変更したくはないですよね。
pruneオプションの追加
そんな中で、先日、コードレビューの中でecschedule公式のドキュメントを読む機会があり、ecschedule v0.10.0からpruneオプションというものが追加されていることに気づきました。
公式いわく、pruneオプションは
In version v0.9.1 and earlier, when rules were renamed or deleted from the configuration, the old rules remained and had to be deleted manually. With the -prune option introduced in v0.10.0, you can now automatically remove these old rules.
とのことで、つまり設定ファイルから削除した古いルールを自動でEventBridgeから削除してくれるオプションだそうです。
pruneオプション追加時のPRから、肝となる実装の変更点を抜粋すると以下となります。
- ルールを適用(ecschedule apply)するときに各ルールにタグ(キーをecschedule:tracking-id、値をクラスタ名としたもの)をつける→これによりecscheduleの管理下であるルールを識別する
- pruneオプションをつけていると、EventBridge上のecschedule管理下のルールと設定ファイルのルールの差分から孤立してしまったルールを抽出する
- 抽出したルールを削除する
あくまで、ecscheduleを使って作られたルールのみpruneの対象になるということで、手動で追加した既存のルールが勝手に削除されることはないようです。
もちろん、転職ドラフトでも使えそうということで導入してみることにしました。
導入から失敗
ecscheduleのバージョンアップ
まずはバージョンアップです。転職ドラフトで使っているecscheduleのバージョンではpruneオプションが使えないので、v0.11.2まで一気にあげました(pruneオプションはv0.10.0から使えるようになったとのことですが、変更点を軽く確認した上でせっかくなので最新版にしました)。
何も考えずにアップデートした状態だけの状態で、ルールの適用コマンドを実行すると特に問題なく動きました(環境によっては権限がなくて怒られるかもしれません)。
EventBridgeのUIを見ると、適用されたルールにキーがecschedule:tracking-id、値がstaging(←試した環境のクラスタ名です)のタグが付与されていることがわかります。
pruneオプション付与して実行→失敗(権限不足)
無事アップデートできたので、うきうきで本命のpruneオプションをつけてecscheduleのapplyを実行してみましたが、権限不足で実行できませんでした。
というわけで、実装のコードを見たり、エラーログを見ながらGitHub Actionsに以下の権限を追加しました(環境によって多少変わるかもしれません)。
- resource-groups:SearchResources
- cloudformation:DescribeStacks
- cloudformation:ListStackResources
- tag:GetResources
- events:DescribeRule
- events:RemoveTargets
- events:DeleteRule
なお、後で気づきましたが、pruneオプション追加時のPRのコメント内にヒントがありました...... github.com
成功?→やっぱり失敗(存在すべきルールが消されている)
権限追加によりpruneオプションをつけてecscheduleのapplyを実行し、特にエラーなく実行できました。 めでたしとなれば良いですが、EventBridgeのWebUIからから確認すると、設定されるべきルールが明らかに足りていないことに気づきました。少しGitHub Actionsのログを見てみると、存在すべきルールを削除していることがわかりました。 はじめは何が起こってるか分かりませんでしたが、よく考えると転職ドラフトにおけるルールの適用方法に問題があったことに気づきました。
原因
転職ドラフトは、基本的にはRails製のモノリス構成ではありますが重いバッチ処理を実行するためにアプリケーション本体から切り離された別のECSタスクがあり、同じECSクラスタ内に同居しています。 そして、このECSタスク用にもバッチの設定ファイルがあり、ecscheduleを使ってルールの適用をしています。
GitHub Actionsの中で、この複数のバッチの設定ファイルをそれぞれ異なるタイミングで適用させるようなフローになっています。 このような使い方をするとpruneオプションを使ったときに問題が起きます。
具体的に何が問題だったかを簡単に説明します。前提として、内容が異なる2つの設定を異なるタイミングで2回適用させることになります。
1つ目の設定ファイルの適用は問題なくできます。
問題は2つ目の設定ファイルの適用時で、以下のようになってしまいます。
- 2つ目の設定ファイルを適用する
- 2つ目の設定ファイルに定義されてないecschedule管理下のルールとして、1つ目の設定ファイルで追加したルールが抽出される
- 1つ目の設定ファイルのルールが削除される
先に述べたように、削除するルールはecscheduleによって追加されたタグにより識別されたルールと設定ファイルのルールの差分が元になります。 今回のターゲットは同じクラスタなので、タグの値が2つの設定ファイルで全く同じになり、適用ごとに設定ファイルの識別ができません。
というわけで、転職ドラフトではpruneオプションを素直に使えませんでした。
解決策
ひとまず解決策を2つ考えました。
プラン1:設定ファイルごとに任意のタグをつける
そうです。ルールのタグの値にクラスタ名ではなく、設定ファイルごとに任意の値をつけると複数の設定ファイルのルールが区別でき、2回目の適用で消されることがありません。 ただし、この修正はecschedule自体の修正が必要です。ということで、早速ecscheduleに修正PRを出しました。 現在は受け入れられるかの結果待ちです。 github.com
プラン2:ルールの適用を1回にまとめる
2つ目の解決策ですが、シンプルにルールの適用を1回にまとめるということです。 幸いにも、適用させようとしているルールは転職ドラフト内で管理しているものなので、設定ファイルを1つにまとめることも可能ではあります(CIのパイプラインを少し変更しないといけないのですが)。 大きな問題はありませんが、本当は別のものとして動かしたくて分離したタスクなのに、ルールの適用に関してはpruneオプションを使いたいがために中途半端に共通化してしまうのはいまいちな気がしています。
結局どっちにしたの?
まだ決まっていません。今回、2種類の解決策を書いたのは、まだどちらにするか決め兼ねているためです。 個人的には、プラン1の方法がルールの適用タイミングが分離できてかつ、既存の修正がほとんどいらないため望ましいなと思っています。 ただ、今出しているPRがリジェクトされたらプラン2の方法にしようと思っています。フォークしてまでプラン1の方法にしたいほどでもないと思っています(本家に追従するためのメンテナンスも大変そうですし)
おわりに
ecscheduleは非常に良いツールでお世話になっています!また、pruneオプションも待ちに待った機能でした。 しかし、実行方法によってはpruneオプションが思ったように動かないことがあるということが分かりました。 今回は、本番適用まで間に合わずに雑な感じのまとめになってしまいましたが、引き続き改善を図っていきたいと思います!(また、進捗でれば追記する予定です)
補足
EventBridge経由でECSを起動する方法はECSの起動まで保証されず、たまに動かないことがあるそうなので、あまりクリティカルな処理には向かないようです。そのため、クリティカルな用途にはStep Functionsなどを使うことが薦められているようです。 Capacity is unavailable at this time. Please try again later or in a different availability zone | AWS re:Post
また、弊社におけるecschedule以外のECSを動かす実例ついてはこちらにまとめられています。どうやらEventBridge Schedulerを使っているようです。こちらも良さそうですね。 made.livesense.co.jp