この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは、虎塚です。
2016年12月1日のAWSアップデートで、AWS LambdaがDead Letter Queue (DLQ)をサポートするようになりました。キューとして使えるのは、SQSキューまたはSNSトピックです。
AWS Lambdaで、S3、SNS、IoTなどのAWSサービスに非同期メッセージを送信する機能を実装している方には嬉しいアップデートかと思いますので、ご紹介します。
※2016年12月3日現在、本機能はOhioリージョンでサポートされています。他のリージョンにも今後展開されるとのことです。
前提: Lambda関数が失敗するとどうなるか
Lambda関数は、Lambda関数のコードにエラーがある場合や、サービスまたはリソースの上限を超えた場合、実行に失敗します。失敗時の挙動は、次のとおりです。
関数とイベントの種類 | 挙動 |
---|---|
同期呼び出しのLambda関数 | 例外が返される |
非同期呼び出しのLambda関数、S3バケット通知 | 最低2回のリトライ(全3回の試行)、それでも失敗する場合はイベントが棄却される |
Kinesisストリーム、DynamoDBストリームからのイベント | データの有効期限切れまで再試行(データは24時間保持) |
Lambda関数実行の進行状況は、CloudWatchメトリックスで監視するのが一般的です。
今回のアップデートではLambdaにDead Letter Queue (DLQ)を設定できるようになりました。これまでは実行に3回失敗したイベントは捨てられていましたが、SQSキューまたはSNSトピックに送信できるようになりました。
LambdaにDead Letter Queueを設定する方法
イベントを送信したいSNSトピックまたはSQSキューのARNを、Lambda関数のDeadLetterConfigパラメータとして指定することで、DLQを設定できます。
DeadLetterConfigは、Lambda関数の作成時または更新時に指定できます。
動作確認
Dead Letter QueueとしてSNSトピックを設定し、Lambda関数の実行失敗時にメールが送信されるように設定します。
1. テスト用Lambda関数の作成
Lambda Dashboardのブループリントから、Pythonランタイムで動くhello-world-pythonのLambda関数 (dlq-test) を作成します。テスト実行して、成功することを確認しておきます。
Lambda関数を作成するこのタイミングでDead Letter Queueを指定することもできます。今回は、後から設定します。
2. Dead Letter用SNSの作成
SNSのトピック (failed-lambda) を作成します。
aws sns create-topic --name failed-lambda
{
"TopicArn": "arn:aws:sns:us-east-2:123456789012:failed-lambda"
}
作成したトピックをsubscribeします。
aws sns subscribe --topic-arn arn:aws:sns:us-east-2:123456789012:failed-lambda \
--protocol email \
--notification-endpoint test@example.com
{
"SubscriptionArn": "pending confirmation"
}
上で指定したメールアドレスに認証メールが届くので、メール本文のリンクをクリックしてsubscribeします。もしくは、メール本文中のトークン (リンクURLのパラメータ) を使って、次のコマンドを実行します。
aws sns confirm-subscription \
--topic-arn arn:aws:sns:us-east-2:123456789012:failed-lambda \
--token 1234...abcd
{
"SubscriptionArn": "arn:aws:sns:us-east-2:123456789012:failed-lambda:33333333-2222-1111-0000-444444444444"
}
3. Lambda実行ロールに権限追加
今回はDead Letter QueueとしてSNSを使うため、ステップ2で作成したトピックにAWS Lambdaがアクセスできるようにsns:Publishを許可します。なお、Dead Letter QueueとしてSQSを使う場合は、対象のキューに対するsqs:SendMeesageを許可します。
IAMポリシーを作成します。
AllowDeadLetterQueueForLambdaPolicy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sns:Publish"
],
"Resource": "arn:aws:sns:us-east-2:123456789012:failed-lambda"
}
]
}
aws iam create-policy --policy-name AllowDeadLetterQueueForLambdaPolicy \
--policy-document file://AllowDeadLetterQueueForLambdaPolicy.json
{
"Policy": {
"PolicyName": "AllowDeadLetterQueueForLambdaPolicy",
"CreateDate": "2016-12-02T23:57:26.582Z",
"AttachmentCount": 0,
"IsAttachable": true,
"PolicyId": "AAAAAAAAAAAAAAAAAAAAA",
"DefaultVersionId": "v1",
"Path": "/",
"Arn": "arn:aws:iam::123456789012:policy/AllowDeadLetterQueueForLambdaPolicy",
"UpdateDate": "2016-12-02T23:57:26.582Z"
}
}
作成したポリシーを、Lambda関数 (dlq-test) の実行ロールに追加で適用します。
aws iam attach-role-policy \
--role-name lambda_basic_execution \
--policy-arn arn:aws:iam::123456789012:policy/AllowDeadLetterQueueForLambdaPolicy
4. DeadLetterConfigの設定
Lambda関数 (dlq-test) の設定を更新して、ステップ2で作成したSNSトピック (failed-lambda) をDead Letter Queueとして指定します。
aws lambda update-function-configuration \
--function-name dlq-test \
--dead-letter-config TargetArn=arn:aws:sns:us-east-2:123456789012:failed-lambda
{
[...]
"FunctionName": "dlq-test",
"FunctionArn": "arn:aws:lambda:us-east-2:123456789012:function:dlq-test",
"Version": "$LATEST",
"Role": "arn:aws:iam::123456789012:role/lambda_basic_execution",
"Handler": "lambda_function.lambda_handler",
"DeadLetterConfig": {
"TargetArn": "arn:aws:sns:us-east-2:123456789012:failed-lambda"
},
"Runtime": "python2.7",
"Description": "A starter AWS Lambda function."
[...]
}
5. Lambda関数の実行を失敗させる
Lambda関数のコードを、エラーが出るように書き換えます。
from __future__ import print_function
import json
print('Loading function')
def lambda_handler(event, context):
#print("Received event: " + json.dumps(event, indent=2))
print("value1 = " + event['key1'])
print("value2 = " + event['key2'])
print("value3 = " + event['key3'])
return event['key1'] # Echo back the first key value
#raise Exception('Something went wrong')
上のコードの関数名 (lambda_handler) を、任意の名前 (foobar) に変更して保存します。あらかじめLambda関数に設定したhandler名と異なる名前にしたため、AWS LambdaがLambda関数を呼び出すことができず、実行時に失敗するようになります。
AWS Lambdaは、関数の実行に失敗して2回リトライした後でDead Letter Queueにイベントを送信するので、リトライが起きるようにトリガーを設定します。
その上で、Lambda関数を1分に1回実行するように設定します。
少し待つと、ステップ2でトピックをsubscribeしたメールアドレスに、「AWS Notification Message」というSubjectでメールが届きます。メールの本文は次のような内容です。
{"version":"0","id":"12345678-1234-1234-1234-210987654321","detail-type":"Scheduled Event","source":"aws.events","account":"1223456789012","time":"2016-12-03T00:11:45Z","region":"us-east-2","resources":["arn:aws:events:us-east-2:123456789012:rule/every1minute"],"detail":{}}
-- If you wish to stop receiving notifications from this topic, please click or visit the link below to unsubscribe: [...]
Lambda関数が失敗してリトライがおこなわれた後に、Dead Letter Queueとして設定したSNSにイベントが送られることを確認できました。
(放っておくと1分に1回メールが送信され続けるので、確認が完了したら、本ステップで作成したスケジュール設定は削除しておきましょう)
トラブルシュート
Dead Letter Queueを設定したLambdaを作成または更新するときに、次のようなエラーが出ることがあります。
An error occurred (AccessDeniedException) when calling the UpdateFunctionConfiguration operation: Your access has been denied by SNS, please make sure your function execution role have permission to Publish for arn:aws:sns:us-east-2:123456789012:lambda-function-name. SNS Error Code: AuthorizationError. SNS Error Message: User: arn:aws:sts::123456789012:assumed-role/lambda_basic_execution/awslambda_xxx_20161202xxxxxxxxx is not authorized to perform: SNS:Publish on resource: arn:aws:sns:us-east-2:123456789012:lambda-function-name
このエラーは、Dead Letter Queueに指定したAWSリソースへのアクセス権限がLambda関数に足りないときに発生します。ステップ3を参照して、Lambda関数の実行ロールに適切な権限を与えてください。
おわりに
失敗したLambdaのイベントがSQSやSNSに直接送信できるようになったことで、後続の例外処理を確実に実施しやすくなりました。たとえば、SQSキューに送信された未処理イベントをアプリケーション側で拾ったり、SNS経由で別のLambdaを実行したりといったことも可能なので、活用しましょう。
それでは、また。