NRIネットコム Blog

NRIネットコム社員が様々な視点で、日々の気づきやナレッジを発信するメディアです

Lambdaの関数URLを使ったSlackボットで無限ループに焦った件

こんにちは、梅原です。
今日はLambdaを使ったオウム返しボットで無限ループして焦った話とその対策について話します。

今回作成したボットと起こった事象

今回作ったボットは、そのボットへのメンションをトリガーにLambdaをキックし、そのままオウム返ししてくれるSlack上で動くボットです。
こちらが実装した構成図です。

SlackからLambdaを実行する方法はいくつかあります。今回はLambdaの関数URLから呼び出しをしています。
関数URLを使用することで、以前のようにAPI Gatewayでエンドポイントを作成する必要がなくなったので、簡単にHTTPS経由Lambdaを呼び出せるようになりました。

関数URLについての詳細は以下ブログを参照ください。 tech.nri-net.com

またメンションを検知して関数URLを叩く方法は、Slack AppのEvent Subscriptionsで可能です。 Event SubscriptionsはSlack AppへのメンションといったEvent typesをトリガーに、登録しておいたエンドポイントにPOSTリクエストできる機能です。
このEvent Subscriptionsにメンションをトリガーに関数URLを叩くように設定しました。

メッセージ送信の部分はLambdaの中で、受けったイベントからメッセージを取得し、Incoming WebhooksでSlackに送信する処理を書いています。
Slackが受け取るeventの例はこちらです。

{
    "token": "ZZZZZZWSxiZZZ2yIvs3peJ",
    "team_id": "T123ABC456",
    "api_app_id": "A123ABC456",
    "event": {
        "type": "app_mention",
        "user": "U123ABC456",
        "text": "What is the hour of the pearl, <@U0LAN0Z89>?",
        "ts": "1515449522.000016",
        "channel": "C123ABC456",
        "event_ts": "1515449522000016"
    },
    "type": "event_callback",
    "event_id": "Ev123ABC456",
    "event_time": 1515449522000016,
    "authed_users": [
        "U0LAN0Z89"
    ]
}

https://api.slack.com/events/app_mentionより引用

eventの中のtextの「What is the hour of the pearl, <@U0LAN0Z89>?」が送られてきたメッセージです。
前半の「What is the hour of the pearl,」 がメンションとともに送られてきたメッセージで、「<@U0LAN0Z89>」がメンションです。

それをなんの加工もなしにそのままSlackに送っていました。

つまり、

メンションが含まれるメッセージをそっくりそのままSlackに送信していました。

はい、、それはダメですね。。。

構成図の右側の部分のLambdaの実行条件とLambdaの処理内容がおなじになっています。これで無限ループの出来上がりです。

構成図から無限ループしてますね。 宮川大輔でなくても「アカーン!」って言うくらいです。

Slack Appにメンションした瞬間、ボットからの1答目は成功したかのように見えました。
ですが、当然そのメッセージにメンションが含まれているので、次のメッセージもやってきます。もちろんその次もです。
どんどん増えていくメッセージに対して、当時の自分は寝耳に水状態で「やばい、無限ループやん」と焦りました。かなり。

焦った自分は止め方もわからず、とっさに思いついた関数自体を削除でなんとか耐え凌ぎました。

10秒くらいの出来事でしたが結果、10回ほどLambdaが実行されていました。
今回はすぐ形に見えるもので即座に気がつきましたのが良かったですが、気付かずループして高額請求されたらと考えるとゾッとします。 パワープレイでなんとか止めましたが、そもそも無限ループを起こさないことが大切です。 考えられる対策を書いておくのでぜひ二の舞いにならないように参考してください。

対策

①無限ループがあることを知ろう!

自分自身無限ループしてうん百万円請求された系の話は聞いたことがあったのですが、完全に忘れていました。
何かをトリガーにLambdaをキックするときは必ず無限ループしないか必ず確認しましょう。

②メトリクスの監視をする

Lambda関数のinvocationや、EventBridgeを使用している場合はtriggeredruleのメトリクスを監視しておき、必要以上に実行していたら通知されるようにしましょう。

③コストアラームをいれる

Lambdaに限らずAWSを使うにあたって必要ですが、コストアラームも入れておきましょう。ただコストはタイムラグがあるのでその点は注意です。
また万が一の万が一無限ループしても高額請求されないために、Lambdaには必要以上メモリは割り当てないようにしておきましょう。

④止め方を知っておく

いざ目の前で無限ループしてしまったら焦ってしまいます。そんなときのためにでも止め方を知っておくのは大事です。 今回は焦って関数を削除するというパワープレイをしましたが、そんな事する必要はありません。
Lambda関数の一番上にスロットリングのボタンがあります。これを押すことで意図的にスロットリングさせることができます。 焦ってパニックになるとこのボタンも忘れてしまうので、わからなくなったらパワープレイしましょう

⑤Lambda の再帰ループ検出を使用する

Lambda関数やSQSSNSを使用している場合は、AWSの機能として再帰ループの検出が可能です。 docs.aws.amazon.com

最後に

今回は無限ループにすぐ対処でき高額請求は免れたので、軽く話す事ができていますが、うん百万請求と紙一重と考えると笑えません。これを教訓として以降は考慮漏れしないように気をつけます。 みなさんも高額請求にならないように気をつけながらAWSを触りましょう。

執筆者: 梅原 航 インフラエンジニア。 主にAWSを使ってます。