AWS IoTのLambda呼び出しアクションを検証してみた

記事タイトルとURLをコピーする
こんにちは、技術1課の中村です。
今日は、AWS IoTのRule Actionについてのお話です。
AWS IoTのRule Actionといえば、S3やSQS、DynamoDB、さらには SalesforceなどのSaaSとの連携もこなしてくれる、すごいヤツです。
今回は、AWS IoTのRule Actionの中のLambda呼び出しアクションについて検証をしてみました。

本題

今回ここで書くことは以下の3つです。
  1. AWS IoTのRule ActionによるLambda Functionの起動は、同期タイプ?非同期タイプ?
  2. AWS IoTのRule ActionでLambda Functionを指定した場合、Error Actionはどのような条件で実行される?
  3. AWS IoTからLambda Functionを起動したい時、結局の所どうすべき?(筆者の視点から)
では、早速進めていきましょう。

1. AWS IoTのRule ActionはによるLambda Functionの起動は、同期タイプ?非同期タイプ?

結論から言うと、非同期タイプでした。
まずは、Lambdaの サポートされているイベントソース のページを確認してみたのですが、AWS IoTからの実行についての記載はなかったので実際に試してみることにしました。

Lambdaは、同期タイプと非同期タイプで異常終了時の再試行の挙動が異なります。具体的には、同期タイプの呼び出しの場合は異常終了したとしても再試行は行われません。対して、非同期タイプの呼び出しの場合は異常終了した時、その後間隔を空けて2回Lambdaによって再試行が行われます。

そのため、AWS IoTから連携するLambda Functionに意図的にエラーを仕込んで、Lambdaによって再試行されるかどうかを試してみました。結果、元々の実行が1回、その後Lambdaによって2回再実行されていることが確認できたので、非同期タイプの呼び出しであると言えるでしょう。

…と、もっともらしく検証の方法を書いたのですが、シンプルにCloudTrailを見れば良いという事に後から気がつきました。
というわけでCloudTrailから確認してみました。以下は抜粋ですが、ちゃんと "invocationType":"Event" (非同期呼び出し) となっていますね。
(同期呼び出しの場合は、 "invocationType":"RequestResponse" となります)
eventtime eventsource eventname useragent requestparameters(抜粋)
2019-01-24T05:20:38Z lambda.amazonaws.com Invoke iot.amazonaws.com "functionName":"invoke_test"
"invocationType":"Event"
2019-01-24T05:20:38Z lambda.amazonaws.com InvokeExecution lambda.amazonaws.com "functionName":"invoke_test"
2019-01-24T05:21:41Z lambda.amazonaws.com InvokeExecution lambda.amazonaws.com "functionName":"invoke_test" (リトライ)
2019-01-24T05:23:38Z lambda.amazonaws.com InvokeExecution lambda.amazonaws.com "functionName":"invoke_test" (リトライ)

2. AWS IoTのRule ActionでLambda Functionを指定した場合、Error Actionはどのような条件で実行される?

これも結論から言うと、恐らく「AWS IoTからLambdaのInvoke APIの実行に失敗した時に、Error Actionが実行される」だと考えられます。
1 で記載した通り、AWS IoTからのLambda Functionの呼び出しは非同期です。そのため、Lambda Functionの呼び出しが成功したかどうかはステータスコード(202)で返りますが、Lambda Functionの中身のプログラムが正しく終了したかどうかは呼び出し時には受け取ることはできません。

こちらも検証してみました。

パターン1

前提
  • AWS IoTのRule ActionにLambda Function Aの呼び出しを、Error ActionにLambda Function Bの呼び出しを設定する
  • Lambda Function A には意図的にエラーを仕込んで、プログラムが必ず異常終了するように設定する
  • Lambda Function B は正常終了するように設定する
上記の前提で、Rule Actionで設定したTopicに、メッセージを1通投げてみます。

結果
  • Lambda Function A は計3回実行される (1回の通常実行 + 2回の自動リトライ)
  • Lambda Function B は実行されない
eventtime eventsource eventname useragent requestparameters(抜粋)
2019-01-24T05:20:38Z lambda.amazonaws.com Invoke iot.amazonaws.com "functionName":"invoke_test" (A)
"invocationType":"Event"
2019-01-24T05:20:38Z lambda.amazonaws.com InvokeExecution lambda.amazonaws.com "functionName":"invoke_test" (A)
2019-01-24T05:21:41Z lambda.amazonaws.com InvokeExecution lambda.amazonaws.com "functionName":"invoke_test" (Aのリトライ)
2019-01-24T05:23:38Z lambda.amazonaws.com InvokeExecution lambda.amazonaws.com "functionName":"invoke_test" (Aのリトライ)

パターン2

前提
  • AWS IoTのRule ActionにLambda Function Aの呼び出しを、Error ActionにLambda Function Bの呼び出しを設定する
  • Lambda Function A を削除する
  • Lambda Function B は正常終了するように設定する
上記の前提で、Rule Actionで設定したTopicに、メッセージを1通投げてみます。

結果
  • Lambda Function A 実行されない (そもそも存在しない)
  • Lambda Function B が1回実行される
eventtime eventsource eventname useragent requestparameters(抜粋)
2019-01-24T01:44:47Z lambda.amazonaws.com Invoke iot.amazonaws.com "functionName":"invoke_failed_test" (B)
"invocationType":"Event"
2019-01-24T01:44:47Z lambda.amazonaws.com InvokeExecution lambda.amazonaws.com "functionName":"invoke_failed_test" (B)
(Lambda Function Aの実行ログはCloudTrailには記録されていませんでした)
上記の結果から、「AWS IoTからLambdaのInvoke APIの実行に失敗した時に、Error Actionが実行される」で間違いなさそうです。
パターン1はプログラムのバグによりLambda Function Aが異常終了するケース、パターン2はLambda Functionが存在しないことにより、そもそものLambda Functionの実行( invoke() )に失敗するケースです。

つまり、AWS IoTからRule Actionを使ってLambdaに連携する場合には、連携するLambda Functionにバグがあって異常終了したとしてもRuleに設定したError Actionは実行されないのです。そのため、Lambda Functionの異常終了を拾いたい場合は、Lambdaのデッドレターキューを使ったり、ログ監視をしたり、プログラム内に例外発生時の通知を実装するなり、工夫する必要がありそうですね。

3. AWS IoTからLambda Functionを起動したい時、結局の所どうすべき?(筆者の視点から)

これも結論から書くと、私はAWS IoTとLambdaの間にKinesis StreamsやSQS(SNS)を挟む構成を検討することが多いです。(ここでは、エラーアクションについては割愛します)
AWS IoTのRule Actionを使ってLambdaと直接連携する方法は、データ処理効率の点から個人的にはあまりオススメできません。
一般的にIoTのシステムは継続的に大量のメッセージが流れ込みますが、AWS IoTとLambdaを直で連携させる場合、以下のような懸念点がでてきます。 そもそものデータ流入量が少ないなどで、上記懸念点が気にならないのであれば問題ありませんが、少しでも気になる場合は、Kinesis StreamsやSQSを挟むと少し緩和できます。
これらのサービスを挟むことによる得られるメリットは以下の通りです。 …というわけで、AWS IoTからLambda Functionを起動したい時は、システムの規模・要件に応じて組み合わせるサービスを選べばよいのではないでしょうか。
(AWS IoTとLambdaの直連携も、シンプルでいいですよ!)

まとめ

この記事では、AWS IoTとLambdaの関係について、以下の3つを記載しました。
  1. AWS IoTのRule ActionはによるLambda Functionの起動は、同期タイプ?非同期タイプ?
    -> 非同期タイプ
  2. AWS IoTのRule ActionでLambda Functionを指定した場合、Error Actionはどのような条件で実行される?
    -> invoke() の実行に失敗した時。 invoke() 自体が成功した後にプログラムでコケた場合にはError Actionが呼ばれないので注意
  3. AWS IoTからLambda Functionを起動したい時、結局の所どうすべき?(筆者の視点から)
    -> 規模に応じて、Kinesis StreamsやSQSを挟むと幸せになれる。データ流入量が少ないのであれば直連携でも良さそう
AWSの構成は各種サービスの特徴を理解しながら組み立てていきましょう!