EventBridge Scheduler で Lambda を定期実行するベストプラクティス

はじめに
Lambda を「毎日決まった時間に動かしたい」「N分おきにポーリングしたい」というニーズは非常によくあります。以前は EventBridge(旧CloudWatch Events)の ルール(Rule) に cron 式を設定するのが定番でしたが、2022年に登場した EventBridge Scheduler の方が今は推奨される方法になっています。
この記事では EventBridge Scheduler を使って Lambda を定期実行する際のベストプラクティスを、架空のサンプルシステムを題材にしながら整理します。
題材にするサンプルシステム
ECサイトの運用でよくありそうな、次のようなバッチを例にします。
毎日深夜3時(JST)に、前日分の注文データを集計して日次レポートを作成し、S3に保存したうえでSlackに通知する。処理に失敗した場合は原因を追跡できるようにしておきたい。
このくらいの規模のバッチでも、素朴に「EventBridgeルール+Lambda」を組むだけでは運用時に困る点がいくつか出てきます。以下、順を追って見ていきます。
ベストプラクティス
1. EventBridge Rule ではなく Scheduler を使う
EventBridge には「ルール」と「Scheduler」という2種類のスケジュール実行の仕組みがあります。新規に定期実行を組むなら Scheduler を選ぶのが基本です。Scheduler はターゲットにできるAPIの種類が多く(Universal Target)、柔軟な実行時間枠やリトライ、デッドレターキューの設定などが標準機能として用意されており、ルールに比べてスケーラビリティも高いためです。
サンプルシステムでは、Lambda関数を直接ターゲットにした Schedule を1つ作るだけで済みます。
aws scheduler create-schedule \
--name daily-order-report \
--schedule-expression "cron(0 18 * * ? *)" \
--schedule-expression-timezone "UTC" \
--flexible-time-window '{"Mode":"OFF"}' \
--target '{
"Arn": "arn:aws:lambda:ap-northeast-1:123456789012:function:daily-order-report",
"RoleArn": "arn:aws:iam::123456789012:role/scheduler-invoke-role"
}'2. タイムゾーンは明示的に指定する
cron() 式は基本UTC基準で解釈されるため、JST 3時に動かしたい場合は ScheduleExpressionTimezone に Asia/Tokyo を指定するか、UTCに変換した時刻(18:00 UTC)を使う必要があります。夏時間のない日本では実害は少ないですが、海外拠点向けの処理を書く場合はタイムゾーン指定の方が事故が少なく安全です。
3. Flexible Time Window でスパイクを避ける
多数のスケジュールが同時刻に集中すると、Lambdaの同時実行数上限に達したり、下流のDBやAPIに瞬間的な負荷がかかったりすることがあります。厳密に「その時刻ちょうど」に実行する必要がないバッチであれば、Flexible Time Window(実行時刻に幅を持たせる機能)を設定し、実行タイミングを分散させるのが有効です。
サンプルの日次レポートも「深夜3時ちょうど」である必要はなく、3時〜3時15分の間に始まれば十分なので、15分の flexible window を設定しておくと安全です。
4. デッドレターキュー(DLQ)とリトライポリシーを必ず設定する
EventBridge Scheduler は、ターゲットの呼び出しに失敗した場合に再試行しますが、権限不足や関数が存在しないといった恒久的なエラーは再試行されずそのまま失敗になります。DLQ(SQS)を設定しておかないと、実行に失敗したことすら気づけません。
最低限、以下の設定はセットで行うことをおすすめします。
RetryPolicyのMaximumEventAgeInSecondsとMaximumRetryAttemptsDeadLetterConfigでSQSキューを指定- DLQにメッセージが積まれたらCloudWatch Alarm経由でSlackやメールに通知
サンプルシステムであれば、レポート作成に失敗した場合はDLQ経由で気づき、手動で再実行できるようにしておくイメージです。
5. Lambda関数側は冪等に作る
スケジューラのリトライや、後から手動で同じ時刻の処理を再実行するケースを考えると、Lambda関数自体は「同じ入力で複数回実行しても結果が壊れない」冪等な設計にしておくべきです。
サンプルの日次レポートの場合、
- S3への出力はレポート対象日をキーにした固定パス(例:
reports/2026-09-24.csv)に上書き保存する - Slack通知は「同じ日付のレポートを二重送信しないか」を、DynamoDBなどに実行済みフラグを記録してガードする
といった工夫で、再実行しても安全な作りにできます。
6. 実行ロールは最小権限にする
Scheduler がLambdaを呼び出すための実行ロールには、対象のLambda関数を lambda:InvokeFunction できる権限だけを与え、他の関数やリソースへの権限は持たせないようにします。Universal Target経由で他のAWSサービスAPIを呼ぶ場合も同様に、必要なAPIアクションだけを許可します。
7. Lambda側のタイムアウト・メモリ・同時実行数を見積もっておく
定期実行バッチは、想定より処理対象データが増えてタイムアウトする、というのが典型的な障害パターンです。サンプルの注文集計であれば、繁忙期のセール日などデータ量が跳ね上がるタイミングを見越して、タイムアウトとメモリに余裕を持たせておくか、処理を分割してStep Functionsで組む選択肢も検討しておくと安心です。
また、同じ関数を複数のスケジュールから呼んでいる場合は、予約済み同時実行数(Reserved Concurrency)で他の処理を圧迫しないようにする設計も有効です。
8. 監視は「動いたこと」と「終わったこと」の両方を見る
CloudWatch Logsでの実行ログ確認はもちろんですが、それに加えて
- Lambdaの
Errors/Duration/Throttlesメトリクスのアラーム化 - DLQの
ApproximateNumberOfMessagesVisibleのアラーム化 - 「そもそも今日レポートが生成されたか」をチェックする形の死活監視(例: S3に当日ファイルが存在するかを別途チェックするLambda)
まで用意しておくと、「Scheduler自体は動いたが、Lambda内部の処理が想定と違う結果になっていた」というケースにも気づけます。
9. IaCで管理する
CLIやコンソールでの作成は動作確認には便利ですが、本番運用ではCDKやTerraformなどのIaCでスケジュール定義・DLQ・IAMロールをまとめて管理するのがおすすめです。CDKの場合、aws-cdk-lib/aws-scheduler や、Lambda関数側の addEventSource 相当の仕組みを使ってコード上でスケジュールとDLQ、リトライポリシーを一箇所にまとめて定義できます。
10. 不要になったスケジュールは無効化・削除する
地味に見落としがちですが、検証用に作ったスケジュールを無効化し忘れて放置すると、無駄なLambda実行やアラート疲れの原因になります。スケジュールにはState(ENABLED/DISABLED)があるので、一時停止したい場合はまず無効化し、恒久的に不要になったら削除する運用ルールを決めておくとよいでしょう。
EventBridge Scheduler での定期実行は「スケジュールを1個登録すれば終わり」ではなく、
- Flexible Time Windowでの負荷分散
- DLQとリトライポリシーによる失敗の可視化
- Lambda側の冪等性設計
- 最小権限のIAMロール
- タイムアウト・同時実行数の見積もり
- 監視・IaC化
まで含めて設計すると安心して本番運用できるバッチになります。