この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
SNS→SQS→Lambdaの組み合わせを使っている方は多いと思います。 このとき、LambdaがErrorになってもSQSにデータが残り続けるため、任意時間後に自動で再実行してくれます。便利ですね。
しかし、何らかの理由によってDLQを設定するとき、LambdaのDLQやDestinationを設定しても、動作しませんでした。 理由はSQSトリガーで動作するLambdaは非同期呼び出しではないからです。DLQを使いたい場合は、SQSのDLQを使いましょう。
実際に試してみました。
おすすめの方
- SQSトリガーで起動するLambdaをAWS SAMで作りたい方
- SQSにDLQを設定したい方
- SNS -> SQSの構成を作りたい方
まずは、LambdaのDLQやDestinationが動作しないことを確認する
sam init
sam init \
--runtime python3.8 \
--name Lambda-Sqs-Dlq-Test-Sample \
--app-template hello-world \
--package-type Zip
SAMテンプレート
下記を作成しています。
- SNS
- SQS
- SNS→SQSのポリシー
- Lambda
template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Lambda-Sqs-Dlq-Test-Sample
Resources:
# LambdaのDLQ用のSNSトピック
HelloWorldFunctionDlqTopic:
Type: AWS::SNS::Topic
Properties:
Subscription:
- Protocol: email
Endpoint: sample@example.com
SampleTopic:
Type: AWS::SNS::Topic
Properties:
Subscription:
- Protocol: sqs
Endpoint: !GetAtt SampleQueue.Arn
SampleQueue:
Type: AWS::SQS::Queue
Properties:
VisibilityTimeout: 120
QueuePolicy:
Type: AWS::SQS::QueuePolicy
Properties:
Queues:
- !Ref SampleQueue
PolicyDocument:
Statement:
- Action:
- sqs:SendMessage
Effect: Allow
Resource: "*"
Principal:
Service: sns.amazonaws.com
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello_world/
Handler: app.lambda_handler
Runtime: python3.8
Timeout: 5
DeadLetterQueue:
Type: SNS
TargetArn: !Ref HelloWorldFunctionDlqTopic
Policies:
- arn:aws:iam::aws:policy/AmazonSNSFullAccess
EventInvokeConfig:
MaximumEventAgeInSeconds: 60
MaximumRetryAttempts: 1
DestinationConfig:
OnFailure:
Type: SNS
Destination: !Ref HelloWorldFunctionDlqTopic
Events:
SQS:
Type: SQS
Properties:
BatchSize: 5
Queue: !GetAtt SampleQueue.Arn
HelloWorldFunctionLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/lambda/${HelloWorldFunction}
Lambdaコード
強制的にErrorを発生させています。
app.py
def lambda_handler(event, context):
raise NotImplementedError()
デプロイ
sam build
sam package \
--output-template-file packaged.yaml \
--s3-bucket cm-fujii.genki-deploy
sam deploy \
--template-file packaged.yaml \
--stack-name Lambda-Sqs-Dlq-Tests-Sample-Stack \
--s3-bucket cm-fujii.genki-deploy \
--capabilities CAPABILITY_NAMED_IAM \
--no-fail-on-empty-changeset
SNSにメッセージを発行して、DLQの動きを確認する
SNSにメッセージを発行します。
しばらく待っても、DLQ用のSNSトピックを経由したメールは届きません。
なお、CloudWatch LogsでLambdaのログを確認すると、VisibilityTimeout:120
ごとにLambdaが実行されています。
SQSは、「処理中のメッセージ」にキューが溜まっています。
SQSのDLQを設定する
SAMテンプレート
下記を追加します。
- DLQ用のSQS
- DLQ用のSQSを試しに購読するLambda(ログを出すだけ)
- DLQ用のSQSトリガーで起動し、eventをログ出力するLambda
template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Lambda-Sqs-Dlq-Test-Sample
Resources:
# LambdaのDLQ用のSNSトピック
HelloWorldFunctionDlqTopic:
Type: AWS::SNS::Topic
Properties:
Subscription:
- Protocol: email
Endpoint: sample@example.com
SampleTopic:
Type: AWS::SNS::Topic
Properties:
Subscription:
- Protocol: sqs
Endpoint: !GetAtt SampleQueue.Arn
SampleQueue:
Type: AWS::SQS::Queue
Properties:
VisibilityTimeout: 120
RedrivePolicy:
maxReceiveCount: 2
deadLetterTargetArn: !GetAtt SampleDeadLatterQueue.Arn
# DLQ用のSQS
SampleDeadLatterQueue:
Type: AWS::SQS::Queue
Properties:
VisibilityTimeout: 60
QueuePolicy:
Type: AWS::SQS::QueuePolicy
Properties:
Queues:
- !Ref SampleQueue
PolicyDocument:
Statement:
- Action:
- sqs:SendMessage
Effect: Allow
Resource: "*"
Principal:
Service: sns.amazonaws.com
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello_world/
Handler: app.lambda_handler
Runtime: python3.8
Timeout: 5
DeadLetterQueue:
Type: SNS
TargetArn: !Ref HelloWorldFunctionDlqTopic
Policies:
- arn:aws:iam::aws:policy/AmazonSNSFullAccess
EventInvokeConfig:
MaximumEventAgeInSeconds: 60
MaximumRetryAttempts: 1
DestinationConfig:
OnFailure:
Type: SNS
Destination: !Ref HelloWorldFunctionDlqTopic
Events:
SQS:
Type: SQS
Properties:
BatchSize: 5
Queue: !GetAtt SampleQueue.Arn
HelloWorldFunctionLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/lambda/${HelloWorldFunction}
DumpSnsFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello_world/
Handler: app2.lambda_handler
Runtime: python3.8
Timeout: 5
Events:
SQS:
Type: SNS
Properties:
Topic: !Ref HelloWorldFunctionDlqTopic
DumpSnsFunctionLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/lambda/${DumpSnsFunction}
DLQ用のSQSを試しに購読するLambda
event
をログ出力します。
app2.py
def lambda_handler(event, context):
print(event)
デプロイ
sam build
sam package \
--output-template-file packaged.yaml \
--s3-bucket cm-fujii.genki-deploy
sam deploy \
--template-file packaged.yaml \
--stack-name Lambda-Sqs-Dlq-Tests-Sample-Stack \
--s3-bucket cm-fujii.genki-deploy \
--capabilities CAPABILITY_NAMED_IAM \
--no-fail-on-empty-changeset
DLQ用のSQSを試しに購読するLambdaのログ
「SNS -> SQS -> Lambda」でLambdaがErrorになり続けていましたが、SQSに設定したDLQ(SQS)にキューが移動します。このDLQ(SQS)をトリガーに動くLambdaのログを見ると、「This is a pen.」を受け取れています。
「SNS -> SQS -> Lambda」にあったSQSのキューも処理されています。
さいごに
SQSトリガーで動作するLambdaは、非同期呼び出しではありません。DLQを使いたい場合は、SQSのDLQを使いましょう。
これに気づくまで半日以上かかりました……。どなたかの参考になれば幸いです。