AWS Lambda CloudWatch Jobs
Hướng dẫn toàn diện thiết lập job theo lịch với AWS Lambda và CloudWatch/EventBridge - từ cơ bản đến best practices
Bạn cần tự động hóa các tác vụ định kỳ như backup, gửi email, hoặc xử lý dữ liệu? AWS Lambda kết hợp với CloudWatch Events/EventBridge là giải pháp serverless hoàn hảo để thay thế cron jobs truyền thống.
Tổng quan về Lambda Scheduled Jobs#
AWS Lambda là compute service serverless cho phép chạy code mà không cần quản lý server. Khi kết hợp với scheduling service, bạn có thể:
- Tự động hóa tác vụ: Backup, report generation, data cleanup
- Tiết kiệm chi phí: Chỉ trả tiền khi code thực thi
- Scale tự động: Xử lý tăng tải mà không cần cấu hình
- High availability: AWS đảm bảo tính sẵn sàng
[!NOTE] Hiện tại AWS khuyến nghị sử dụng EventBridge Scheduler thay vì CloudWatch Events cũ cho các scheduled jobs mới.
Phương pháp 1: EventBridge Scheduler (Khuyến nghị)#
EventBridge Scheduler là service scheduling hiện đại với nhiều tính năng nâng cao so với CloudWatch Events.
Ưu điểm của EventBridge Scheduler#
- Hỗ trợ timezone: Không chỉ giới hạn UTC
- Flexible time windows: Chạy trong khoảng thời gian linh hoạt
- Built-in retry: Tự động retry khi thất bại
- One-time schedules: Hỗ trợ chạy một lần
- Dead Letter Queue: Xử lý failure tốt hơn
Bước 1: Tạo Lambda Function#
import json
import logging
import boto3
from datetime import datetime
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
"""Handler cho scheduled job"""
logger.info(f"Job triggered at: {datetime.utcnow()}")
logger.info(f"Event: {json.dumps(event)}")
try:
# Logic xử lý job của bạn
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):
"""Xử lý logic chính của job"""
# Ví dụ: cleanup database, send report, etc.
logger.info("Processing scheduled task...")
# Your business logic here
return {"status": "success", "processed_items": 10}pythonBước 2: Tạo IAM Role#
Lambda cần permission để thực thi job và ghi log:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}jsonPolicy permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"dynamodb:*", # Nếu cần access DynamoDB
"s3:*" # Nếu cần access S3
],
"Resource": "*"
}
]
}jsonBước 3: Tạo Schedule với EventBridge Scheduler#
Sử dụng AWS Console#
- Vào EventBridge Scheduler console
- Click Create schedule
- Chọn Recurring schedule
- Cấu hình schedule expression:
# Chạy hàng ngày lúc 9:00 AM UTC
cron(0 9 * * ? *)
# Chạy mỗi 15 phút
rate(15 minutes)
# Chạy hàng tuần vào Monday 9:00 AM
cron(0 9 ? * MON *)
# Chạy đầu mỗi tháng
cron(0 0 1 * ? *)bash- Select Lambda function làm target
- Cấu hình timezone (nếu cần):
# 9:00 AM Eastern Time
cron(0 9 ? * MON *)
timezone: America/New_Yorkbash- Cấu hình flexible time window (optional):
# Chạy trong 15 phút sau thời gian schedule
mode: FLEXIBLE
maximum_window_in_minutes: 15bash- Setup retry policy:
maximum_retry_attempts: 3
maximum_event_age_in_seconds: 3600bashSử dụng AWS 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
}'bashSử dụng Terraform#
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
}
}
}hclPhương pháp 2: CloudWatch Events (Legacy)#
CloudWatch Events là phương pháp truyền thống, vẫn được hỗ trợ nhưng ít tính năng hơn.
Bước 1: Tạo Lambda Function#
Tương tự như phương pháp Scheduler.
Bước 2: Tạo CloudWatch Rule#
Sử dụng AWS Console#
- Vào CloudWatch console
- Chọn Events → Rules
- Click Create rule
- Chọn Schedule expression
- Nhập cron expression:
# 9:00 AM UTC hàng ngày
0 9 * * ? *
# Mỗi 5 phút
rate(5 minutes)bash- Select Lambda function làm target
- Configure permission (AWS sẽ tự động tạo)
Sử dụng 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 Expression Guide#
EventBridge sử dụng 6-field cron expression:
cron(Minutes Hours Day-of-month Month Day-of-week Year)textVí dụ Cron Expressions#
# Hàng ngày lúc 9:00 AM UTC
cron(0 9 * * ? *)
# Hàng tuần vào Monday 9:00 AM
cron(0 9 ? * MON *)
# Hàng tháng vào ngày 1 lúc 00:00
cron(0 0 1 * ? *)
# Mỗi 15 phút
rate(15 minutes)
# Mỗi 1 giờ
rate(1 hour)
# 9:00 AM từ Monday đến Friday
cron(0 9 ? * MON-FRI *)
# Lần cuối cùng của tháng
cron(0 0 L * ? *)
# First Monday của mỗi tháng
cron(0 0 ? * MON#1 *)bashField Values#
| Field | Values | Special Characters |
|---|---|---|
| Minutes | 0-59 | , - * / |
| Hours | 0-23 | , - * / |
| Day of month | 1-31, L, W | , - * / ? L W |
| Month | 1-12, JAN-DEC | , - * / |
| Day of week | 1-7, SUN-SAT, ?, L, # | , - * / ? L # |
| Year | 1970-2199 | , - * / |
[!WARNING] Use
?khi day-of-month hoặc day-of-week bị constraint, không được dùng cả hai field cùng lúc.
Best Practices#
1. Idempotency#
Đảm bảo handler của bạn idempotent để safe khi retry:
def lambda_handler(event, context):
# Check nếu job đã chạy
if is_job_already_processed(event):
logger.info("Job already processed, skipping")
return {"status": "skipped"}
# Process job
result = process_job(event)
# Mark job as processed
mark_job_as_processed(event)
return resultpython2. Dead Letter Queue (DLQ)#
Setup DLQ để xử lý failed invocations:
import boto3
import json
sqs = boto3.client('sqs')
def send_to_dlq(error_message, event):
"""Gửi failed event đến DLQ"""
sqs.send_message(
QueueUrl='YOUR_DLQ_URL',
MessageBody=json.dumps({
'error': error_message,
'event': event,
'timestamp': str(datetime.utcnow())
})
)python3. Overlap Prevention#
Ngăn chặn overlapping executions với DynamoDB lock:
import boto3
from datetime import datetime, timedelta
dynamodb = boto3.resource('dynamodb')
lock_table = dynamodb.Table('job-locks')
def acquire_lock(job_id):
"""Acquire lock để ngăn overlap"""
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 # Lock đã tồn tại
def release_lock(job_id):
"""Release lock sau khi hoàn thành"""
lock_table.delete_item(Key={'job_id': job_id})python4. Monitoring và Logging#
Sử dụng CloudWatch Logs và Metrics:
import time
import logging
logger = logging.getLogger()
def lambda_handler(event, context):
start_time = time.time()
try:
# Custom metrics
logger.info("JOB_STARTED")
result = process_job(event)
# Log duration
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. Error Handling#
Xử lý error gracefully:
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)}")
# Send to DLQ or alert
send_alert(str(e))
raisepythonSo sánh EventBridge Scheduler vs CloudWatch Events#
| Tính năng | EventBridge Scheduler | CloudWatch Events |
|---|---|---|
| Schedule types | cron + rate + one-time | cron + rate |
| Timezone support | ✅ Any timezone | ❌ UTC only |
| Flexible windows | ✅ Yes | ❌ No |
| Built-in retry | ✅ Yes | ❌ No (need DLQ) |
| Cost | $1.00/M invocations | Free (first 5M/month) |
| Use case | Production jobs | Simple recurring jobs |
Ví dụ Thực tế: Daily Database Cleanup#
import boto3
import logging
from datetime import datetime, timedelta
logger = logging.getLogger()
dynamodb = boto3.resource('dynamodb')
def lambda_handler(event, context):
"""Cleanup records cũ hơn 30 ngày"""
table = dynamodb.Table('user-activity')
cutoff_date = (datetime.utcnow() - timedelta(days=30)).isoformat()
try:
# Scan và delete old records
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)}")
raisepythonSchedule với EventBridge Scheduler:
# Chạy hàng ngày lúc 2:00 AM UTC
cron(0 2 * * ? *)bashTroubleshooting#
Job không chạy#
- Check schedule expression: Đảm bảo cron syntax đúng
- Verify IAM permissions: Lambda có permission cần thiết
- Check CloudWatch Logs: Xem error messages
- Verify target ARN: Đảm bảo Lambda function ARN đúng
Job chạy nhưng fail#
- Review CloudWatch Logs: Tìm error messages
- Check timeout: Lambda có thể timeout
- Verify resource permissions: Database, S3, etc. access
- Test locally: Chạy Lambda locally để debug
Overlapping executions#
- Implement lock mechanism: Sử dụng DynamoDB hoặc Redis
- Use reserved concurrency: Giới hạn concurrent executions
- Add idempotency: Đảm bảo handler idempotent
Cleanup Resources#
Khi không cần nữa, xóa resources:
# Delete schedule
aws scheduler delete-schedule --name daily-job
# Delete CloudWatch rule
aws events delete-rule --name daily-lambda-rule
# Delete Lambda function
aws lambda delete-function --function-name my-function
# Delete IAM role
aws iam delete-role --role-name scheduler-rolebashKết luận#
AWS Lambda kết hợp với EventBridge Scheduler/CloudWatch Events cung cấp giải pháp mạnh mẽ cho scheduled jobs:
- EventBridge Scheduler: Cho production jobs với timezone support, retry, flexible windows
- CloudWatch Events: Cho simple recurring jobs với cost hiệu quả
Key takeaways:
- Sử dụng EventBridge Scheduler cho production
- Implement idempotency và error handling
- Setup monitoring với CloudWatch
- Use DLQ cho failed invocations
- Prevent overlapping executions