AWS Lambda CloudWatch Jobs
AWS LambdaとCloudWatch/EventBridgeを使用したスケジュールジョブ設定の包括的ガイド - 基礎からベストプラクティスまで
バックアップ、メール送信、データ処理などの定期的なタスクを自動化する必要がありますか?AWS LambdaとCloudWatch Events/EventBridgeの組み合わせは、従来のcronジョブを置き換えるための完璧なサーバーレスソリューションです。
Lambdaスケジュールジョブの概要#
AWS Lambdaは、サーバーを管理せずにコードを実行できるサーバーレスコンピューティングサービスです。スケジューリングサービスと組み合わせることで、以下のことが可能になります:
- タスクの自動化: バックアップ、レポート生成、データクリーンアップ
- コスト削減: コード実行時のみ支払い
- 自動スケーリング: 設定なしで負荷増加に対応
- 高可用性: AWSが可用性を保証
[!NOTE] AWSは現在、新しいスケジュールジョブに従来のCloudWatch Eventsの代わりにEventBridge Schedulerの使用を推奨しています。
方法1: EventBridge Scheduler(推奨)#
EventBridge Schedulerは、CloudWatch Eventsと比較して強化された機能を備えた最新のスケジューリングサービスです。
EventBridge Schedulerの利点#
- タイムゾーンサポート: UTCのみに制限されない
- 柔軟な時間ウィンドウ: 柔軟な時間範囲内で実行
- 組み込み再試行: 失敗時の自動再試行
- ワンタイムスケジュール: 単回実行をサポート
- デッドレターキュー: より良い失敗処理
ステップ1: Lambda関数の作成#
import json
import logging
import boto3
from datetime import datetime
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
"""スケジュールジョブのハンドラー"""
logger.info(f"Job triggered at: {datetime.utcnow()}")
logger.info(f"Event: {json.dumps(event)}")
try:
# ジョブ処理ロジック
result = process_scheduled_task(event)
return {
'statusCode': 200,
'body': json.dumps({
'message': 'Job completed successfully',
'result': result
})
}
except Exception as e:
logger.error(f"Job failed: {str(e)}")
raise
def process_scheduled_task(event):
"""メインのジョブ処理ロジック"""
# 例: データベースクリーンアップ、レポート送信など
logger.info("Processing scheduled task...")
# ビジネスロジックをここに記述
return {"status": "success", "processed_items": 10}pythonステップ2: IAMロールの作成#
Lambdaはジョブ実行とログ書き込みのために権限が必要です:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}jsonポリシー権限:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"dynamodb:*", # DynamoDBアクセスが必要な場合
"s3:*" # S3アクセスが必要な場合
],
"Resource": "*"
}
]
}jsonステップ3: EventBridge Schedulerでスケジュールを作成#
AWSコンソールを使用#
- EventBridge Schedulerコンソールに移動
- Create scheduleをクリック
- Recurring scheduleを選択
- スケジュール式を設定:
# 毎日9:00 AM UTCで実行
cron(0 9 * * ? *)
# 15分ごとに実行
rate(15 minutes)
# 毎週月曜日9:00 AMに実行
cron(0 9 ? * MON *)
# 毎月1日に実行
cron(0 0 1 * ? *)bash- Lambda functionをターゲットとして選択
- タイムゾーンを設定(必要な場合):
# 9:00 AM Eastern Time
cron(0 9 ? * MON *)
timezone: America/New_Yorkbash- 柔軟な時間ウィンドウを設定(オプション):
# スケジュール時刻から15分以内に実行
mode: FLEXIBLE
maximum_window_in_minutes: 15bash- 再試行ポリシーを設定:
maximum_retry_attempts: 3
maximum_event_age_in_seconds: 3600bashAWS CLIを使用#
aws scheduler create-schedule \
--name daily-job \
--schedule-expression 'cron(0 9 * * ? *)' \
--schedule-expression-timezone 'UTC' \
--target '{
"Arn": "arn:aws:lambda:us-east-1:123456789012:function:my-function",
"RoleArn": "arn:aws:iam::123456789012:role/scheduler-role"
}' \
--flexible-time-window '{
"Mode": "FLEXIBLE",
"MaximumWindowInMinutes": 15
}'bashTerraformを使用#
resource "aws_scheduler_schedule" "daily_job" {
name = "daily-job"
group_name = "default"
flexible_time_window {
mode = "FLEXIBLE"
maximum_window_in_minutes = 15
}
schedule_expression = "cron(0 9 * * ? *)"
schedule_expression_timezone = "UTC"
target {
arn = aws_lambda_function.my_function.arn
role_arn = aws_iam_role.scheduler_role.arn
retry_policy {
maximum_retry_attempts = 3
maximum_event_age_in_seconds = 3600
}
dead_letter_config {
arn = aws_sqs_queue.dlq.arn
}
}
}hcl方法2: CloudWatch Events(レガシー)#
CloudWatch Eventsは従来の方法で、まだサポートされていますが機能は少ないです。
ステップ1: Lambda関数の作成#
Schedulerメソッドと同じ。
ステップ2: CloudWatchルールの作成#
AWSコンソールを使用#
- CloudWatchコンソールに移動
- Events → Rulesを選択
- Create ruleをクリック
- Schedule expressionを選択
- cron式を入力:
# 毎日9:00 AM UTC
0 9 * * ? *
# 5分ごと
rate(5 minutes)bash- Lambda functionをターゲットとして選択
- 権限を設定(AWSが自動作成)
AWS CLIを使用#
aws events put-rule \
--name daily-lambda-rule \
--schedule-expression 'cron(0 9 * * ? *)'
aws events put-targets \
--rule daily-lambda-rule \
--targets '{
"Id": "1",
"Arn": "arn:aws:lambda:us-east-1:123456789012:function:my-function"
}'
aws lambda add-permission \
--function-name my-function \
--statement-id daily-lambda-rule \
--action 'lambda:InvokeFunction' \
--principal events.amazonaws.com \
--source-arn arn:aws:events:us-east-1:123456789012:rule/daily-lambda-rulebashCron式ガイド#
EventBridgeは6フィールドのcron式を使用します:
cron(Minutes Hours Day-of-month Month Day-of-week Year)textCron式の例#
# 毎日9:00 AM UTC
cron(0 9 * * ? *)
# 毎週月曜日9:00 AM
cron(0 9 ? * MON *)
# 毎月1日00:00
cron(0 0 1 * ? *)
# 15分ごと
rate(15 minutes)
# 1時間ごと
rate(1 hour)
# 月曜日から金曜日9:00 AM
cron(0 9 ? * MON-FRI *)
# 月の最終日
cron(0 0 L * ? *)
# 毎月の最初の月曜日
cron(0 0 ? * MON#1 *)bashフィールド値#
| フィールド | 値 | 特殊文字 |
|---|---|---|
| 分 | 0-59 | , - * / |
| 時 | 0-23 | , - * / |
| 日 | 1-31, L, W | , - * / ? L W |
| 月 | 1-12, JAN-DEC | , - * / |
| 曜日 | 1-7, SUN-SAT, ?, L, # | , - * / ? L # |
| 年 | 1970-2199 | , - * / |
[!WARNING] 日または曜日が制約されている場合は
?を使用し、両方のフィールドを同時に使用しないでください。
ベストプラクティス#
1. 冪等性#
安全な再試行のためにハンドラーを冪等にします:
def lambda_handler(event, context):
# ジョブが既に実行されたかチェック
if is_job_already_processed(event):
logger.info("Job already processed, skipping")
return {"status": "skipped"}
# ジョブを処理
result = process_job(event)
# ジョブを処理済みとしてマーク
mark_job_as_processed(event)
return resultpython2. デッドレターキュー(DLQ)#
失敗した呼び出しを処理するためにDLQを設定:
import boto3
import json
sqs = boto3.client('sqs')
def send_to_dlq(error_message, event):
"""失敗したイベントをDLQに送信"""
sqs.send_message(
QueueUrl='YOUR_DLQ_URL',
MessageBody=json.dumps({
'error': error_message,
'event': event,
'timestamp': str(datetime.utcnow())
})
)python3. 重複実行の防止#
DynamoDBロックで重複実行を防止:
import boto3
from datetime import datetime, timedelta
dynamodb = boto3.resource('dynamodb')
lock_table = dynamodb.Table('job-locks')
def acquire_lock(job_id):
"""重複を防ぐためにロックを取得"""
try:
lock_table.put_item(
Item={
'job_id': job_id,
'locked_at': datetime.utcnow().isoformat(),
'expires_at': (datetime.utcnow() + timedelta(minutes=10)).isoformat()
},
ConditionExpression='attribute_not_exists(job_id)'
)
return True
except Exception:
return False # ロックが既に存在
def release_lock(job_id):
"""完了後にロックを解放"""
lock_table.delete_item(Key={'job_id': job_id})python4. モニタリングとログ#
CloudWatch LogsとMetricsを使用:
import time
import logging
logger = logging.getLogger()
def lambda_handler(event, context):
start_time = time.time()
try:
# カスタムメトリクス
logger.info("JOB_STARTED")
result = process_job(event)
# 実行時間をログ
duration = time.time() - start_time
logger.info(f"JOB_COMPLETED duration={duration}")
return result
except Exception as e:
logger.error(f"JOB_FAILED error={str(e)}")
raisepython5. エラーハンドリング#
エラーを適切に処理:
def lambda_handler(event, context):
try:
result = process_job(event)
return {
'statusCode': 200,
'body': json.dumps(result)
}
except ValueError as e:
logger.error(f"Validation error: {str(e)}")
return {
'statusCode': 400,
'body': json.dumps({'error': str(e)})
}
except Exception as e:
logger.error(f"Unexpected error: {str(e)}")
# DLQに送信またはアラート
send_alert(str(e))
raisepython比較: EventBridge Scheduler vs CloudWatch Events#
| 機能 | EventBridge Scheduler | CloudWatch Events |
|---|---|---|
| スケジュールタイプ | cron + rate + one-time | cron + rate |
| タイムゾーンサポート | ✅ 任意のタイムゾーン | ❌ UTCのみ |
| 柔軟なウィンドウ | ✅ はい | ❌ いいえ |
| 組み込み再試行 | ✅ はい | ❌ いいえ(DLQが必要) |
| コスト | $1.00/M呼び出し | 無料(最初の5M/月) |
| 使用例 | 本番ジョブ | 単純な定期ジョブ |
実践例: 日次データベースクリーンアップ#
import boto3
import logging
from datetime import datetime, timedelta
logger = logging.getLogger()
dynamodb = boto3.resource('dynamodb')
def lambda_handler(event, context):
"""30日以上前のレコードをクリーンアップ"""
table = dynamodb.Table('user-activity')
cutoff_date = (datetime.utcnow() - timedelta(days=30)).isoformat()
try:
# 古いレコードをスキャンして削除
response = table.scan(
FilterExpression='created_at < :cutoff',
ExpressionAttributeValues={':cutoff': cutoff_date}
)
deleted_count = 0
with table.batch_writer() as batch:
for item in response['Items']:
batch.delete_item(Key={'id': item['id']})
deleted_count += 1
logger.info(f"Deleted {deleted_count} old records")
return {
'statusCode': 200,
'body': json.dumps({
'deleted_count': deleted_count,
'cutoff_date': cutoff_date
})
}
except Exception as e:
logger.error(f"Cleanup failed: {str(e)}")
raisepythonEventBridge Schedulerでのスケジュール:
# 毎日2:00 AM UTCで実行
cron(0 2 * * ? *)bashトラブルシューティング#
ジョブが実行されない#
- スケジュール式を確認: cron構文が正しいか確認
- IAM権限を確認: Lambdaに必要な権限があるか確認
- CloudWatch Logsを確認: エラーメッセージを確認
- ターゲットARNを確認: Lambda関数ARNが正しいか確認
ジョブは実行されるが失敗する#
- CloudWatch Logsを確認: エラーメッセージを確認
- タイムアウトを確認: Lambdaがタイムアウトしている可能性
- リソース権限を確認: データベース、S3などのアクセス権限
- ローカルでテスト: デバッグのためにLambdaをローカルで実行
重複実行#
- ロックメカニズムを実装: DynamoDBまたはRedisを使用
- 予約済み並行性を使用: 並行実行を制限
- 冪等性を追加: ハンドラーが冪等であることを確認
リソースのクリーンアップ#
不要になったらリソースをクリーンアップ:
# スケジュールを削除
aws scheduler delete-schedule --name daily-job
# CloudWatchルールを削除
aws events delete-rule --name daily-lambda-rule
# Lambda関数を削除
aws lambda delete-function --function-name my-function
# IAMロールを削除
aws iam delete-role --role-name scheduler-rolebash結論#
AWS LambdaとEventBridge Scheduler/CloudWatch Eventsの組み合わせは、スケジュールジョブのための強力なソリューションを提供します:
- EventBridge Scheduler: タイムゾーンサポート、再試行、柔軟なウィンドウを備えた本番ジョブ用
- CloudWatch Events: コスト効率の良い単純な定期ジョブ用
重要なポイント:
- 本番にはEventBridge Schedulerを使用
- 冪等性とエラーハンドリングを実装
- CloudWatchでモニタリングを設定
- 失敗した呼び出しにDLQを使用
- 重複実行を防止