【新機能】AWS LambdaがDead Letter Queueをサポートしました #reinvent
こんにちは、虎塚です。
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 [email protected] { "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ポリシーを作成します。
{ "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を実行したりといったことも可能なので、活用しましょう。
それでは、また。