AWS LambdaのデッドレターキューとSNSでリトライ構成を構築する(実践編)
前回の記事で準備したServerless Frameworkを使って、デッドレターキューを利用したAWS Lambda関数のリトライ構成を構築してみます。
なお、本記事に記載している関数のコードは下記リポジトリです。Serverless Frameworkでデプロイも可能です。
もくじ
そもそもなぜデッドレターキューか
デッドレターキューを利用したリトライ構成のメリットは以下だと思います。
- デッドレターキューにより非同期イベントがトリガーでもエラーハンドリングが用意になる
- エラー時のイベント情報を引き継いで次の関数に処理させることができる
AWS Lambda関数のデッドレターキューについてはAWS公式のドキュメントも参考になります。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/invocation-async.html
2つの関数を作成
失敗する関数
以下のような、必ずエラーになるhandler.js
を作成します。
'use strict'; module.exports.failFunc = async event => { console.log(event); throw new Error('Erorr occured!!!!!!!!!'); };
serverless.yml
に諸々の設定を記述します。
- S3のPUTイベントをトリガーにして関数を実行する想定です。すでに存在するバケットを指定するため
existing: true
としています。 - S3のバケット名やSNSのARNは
${file(./config.json):KEY}
という構文で外部ファイルから読み込んでいます。ARNを直書きしたくなかったためです。
service: failfunc provider: name: aws runtime: nodejs12.x region: ap-northeast-1 memorySize: 512 stage: dev timeout: 10 # 外部ファイルは関数に含めない package: exclude: - config.json functions: failFunc: handler: handler.failFunc name: ${self:service} onError: ${file(./config.json):SNS_ARN} events: - s3: bucket: ${file(./config.json):S3_BUCKET_NAME} event: s3:ObjectCreated:* existing: true
外部ファイルのconfig.json
は以下の様な感じです。
{ "SNS_ARN": "ARNを記載", "S3_BUCKET_NAME": "バケット名を記載" }
serverless.yml
のonError
の部分がデッドレターキューの設定です。関数がエラーで失敗したらここに記載したSNSにpublishします。 2020年4月時点ではonError
の設定はSQSは未対応とのことです。
本記事ではこれが理由でSNSでデッドレターキューを試しています。
sls deploy
でデプロイすると、下図のようにトリガーにS3、デッドレターキューにSNSトピックが設定された関数が作成されます。
成功する関数
デッドレターキューを利用して呼ばれる関数です。引数のevent
を出力しています。
event.Record[0].Sns
にSNSを介して渡されるfailFunc
のevent
の情報(S3のバケット名やファイル名)を参照できます。
'use strict'; module.exports.dlqFunc = async event => { console.log(event.Records[0].Sns); return { statusCode: 200, body: JSON.stringify( { message: 'This is dlq function!', input: event, }, null, 2 ), }; };
serverless.yml
は以下の通りです。
service: dlqfunc provider: name: aws runtime: nodejs12.x region: ap-northeast-1 memorySize: 512 stage: dev timeout: 10 # 外部ファイルは関数に含めない package: exclude: - config.json functions: dlqFunc: handler: handler.dlqFunc events: - sns: ${file(./config.json):SNS_TOPIC_ARN}
event
で外部ファイルを通してsns: SNSトピックのARN
を指定します。ARNではなくSNSトピック名を指定した場合はsls deploy
でSNSトピックが新規作成されます。
関数を実行するIAMロール
上記の2つの関数のsls deploy
が成功すると、必要なインラインポリシーがアタッチされたIAMロールが自動で作成されます。
このIAMロールはAWS Lambda関数を実行するためのロールで、デプロイのためのIAMロールとは異なります。
今回のようなお試しでは自動で作成されたロールでも構いませんが、実業務では予め専用のロールを作ってserverless.yml
に設定したり、serverless.yml
にIAMロールの詳細を記述したりします。
これら設定の詳細はServerless Frameworkの公式ドキュメントが参考になります。
例えば、予め作成したIAMロールを設定する場合は、serverless.yml
のprovider
にrole
でIAMロール名を設定します。
provider: role: myIAMRole
インフラ構築の自動化の観点からするとIAMロールの設定も全てserverless.yml
に記載するのが望ましいのかもしれません。
関数をテスト実行する
2つの関数の準備ができたら、一通りテストしてみます。
今回はS3のPUTイベントをトリガーにしてfailFunc
が実行されるので、試しにS3の対象バケットに適当なファイルをアップロードしてみます。
2つの関数のモニタリングタブからCloudWatch Logsを参照すると、failFunc
では2回リトライ後に処理が終了します。一方、dlqFunc
はfailFunc
の失敗によりSNSを介して1回実行されます。
dlwFunc
のログには、下記のようにfailFunc
のS3トリガーイベントに関係するバケット名やファイル名が出力されています("object":{"key":"AWS-Marketplace_dark-bg%404x.png","size":6024,"eTag":"dummy","sequencer":"dummy"}
部分にS3バケットに保存したファイル名が記載されている)。
ちなみに、MessageAttribues
にエラーメッセージも設定されています。
{ Type: 'Notification', MessageId: 'dummy', TopicArn: 'adummy', Subject: null, Message: '{"Records":[{"eventVersion":"2.1","eventSource":"aws:s3","awsRegion":"ap-northeast-1","eventTime":"2020-04-10T13:47:52.958Z","eventName":"ObjectCreated:Put","userIdentity":{"principalId":"dummy"},"requestParameters":{"sourceIPAddress":"dummy"},"responseElements":{"x-amz-request-id":"dummy","x-amz-id-2":"dummy"},"s3":{"s3SchemaVersion":"1.0","configurationId":"failfunc-68a737133cefb25bff959852b8f04754","bucket":{"name":"lambda-test-dummy-name","ownerIdentity":{"principalId":"dummy"},"arn":"dummy"},"object":{"key":"AWS-Marketplace_dark-bg%404x.png","size":6024,"eTag":"dummy","sequencer":"dummy"}}}]}', Timestamp: '2020-04-10T13:51:00.559Z', SignatureVersion: '1', Signature: 'dummy', SigningCertUrl: 'dummy', UnsubscribeUrl: 'dummy', MessageAttributes: { RequestID: { Type: 'String', Value: '218a0795-840c-4be9-a441-396e32db5b3b' }, ErrorCode: { Type: 'String', Value: '200' }, ErrorMessage: { Type: 'String', Value: 'Erorr occured!!!!!!!!!' } } }
上記のように、failFunc
関数がエラーで失敗した時のevent
の情報が、デッドレターキューを利用することでdlqFunc
関数で参照することができます。
これにより、エラーで失敗した時の情報を引き継いで何らかのリトライ処理(別にAWS Lambdak関数や、その他のAWSの各種サービスを利用した処理)に振り分けることができます。
Serverless Framework便利すぎる件
以上、AWS LambdaのデッドレターキューとSNSでリトライ構成を構築してみました。
まあ、厳密にはリトライ構成ではなく非同期イベントをトリガーにした場合のAWS Lambda関数のエラー時にどう対応するか、くらいしか解説していませんが・・・。
Serverless Frameworkはyamlを書くだけであとはよろしく構成管理してくれるので、非常に助かります。もっと色々できると思うので、また知識が増えたらまとめたいと思います。