[アップデート]EventBridgeのデッドレターキュー(DLQ)を使ってみた

リトライポリシーも設定できるようになりましたよ
2020.10.14

先週2020/10/09、EventBridgeでデッドレターキューが使えるようになりました!

本エントリではそのEventBridgeのデッドレターキューを使ってみたいと思います。

デッドレターキュー(DLQ)ってなに

デッドレターには、配達不能の郵便物という意味があります。つまりデッドレターキューとは配達不能の郵便物を格納するキュー、ということになります。AWSでは、処理に失敗したメッセージとかイベントを格納するSQSキューのことを指します。これまでにLambda、SNS、SQSでデッドレターキューを利用することができました。例えばLambdaのデッドレターキューであれば、関数実行でエラーが発生した際に使ったイベント情報を特定のSQSキューにメッセージとして保存しておくことができます。そして後でデバッグするときに参照する、などという使い方が主なユースケースです。

EventBridgeのデッドレターキュー詳細

  • デッドレターキューはターゲットごとに設定します。
  • デッドレターキューに使用できるSQSキューはスタンダードキューのみです。FIFOキューはできません。
  • 別アカウントのSQSキューも使えます。
  • 設定するEventBridgeルールと同じリージョンのSQSキューのみ使用できます。
  • デッドレターキューに追加コストはありません。通常のSQSのコストのみかかります。

リトライポリシー

デッドレターキューと併せてリトライポリシーも設定できるようになりました。こちらもターゲットごとに設定します。

  • これまでは、ターゲットにイベントを配信できなかった場合、最大24時間にわたり再配信を試みていました。24時間経過しても配信できなかった場合、FailedInvocations メトリクスが CloudWatch で発行されました。
  • リトライポリシーの導入によりこの部分を細かく設定できるようになりました。
    • いつまで再配信を試行するか設定できます。最小60秒、最大は1日=24時間です。デフォルトは24時間です。
    • 何回再試行するか設定できます。最小は0回、最大は185回です。デフォルトは185回です。
  • 全再試行を行なっても配信できなかった場合、デッドレターキューを設定していればそちらにイベントを送ります。InvocationsSentToDlqというCloudWatchメトリックがこの送信回数を記録します。デッドレターキューへの送信が失敗した場合は、InvocationsFailedToBeSentToDlqメトリック がその回数を記録します。
  • 特定の状況では再配信を試行せずに即デッドレターキューへイベントが配信されます。アクセス許可権限不足や、ターゲットリソースが存在しない場合など、根本的な問題を解決するためのアクションが実行されるまで全再試行が失敗しうる場合、再配信を思考せず直接デッドレターキューへイベント配信が行われます。

使ってみた

EventBridgeルールを作成しました。1分間隔で実行され、SNSトピックをターゲットにしています。SNSトピックにはサブスクリプションとしてメールアドレスが紐付いています。つまり1分間隔でメールを送る構成です。これにEventBridgeのデッドレターキューを設定してみましょう。

こういうJSONが書かれたメールが毎分届きます

{
  "version": "0",
  "id": "44da9f84-53ce-af29-cfbb-db90fb431641",
  "detail-type": "Scheduled Event",
  "source": "aws.events",
  "account": "999999999999",
  "time": "2020-10-13T12:53:39Z",
  "region": "ap-northeast-1",
  "resources": [
    "arn:aws:events:ap-northeast-1:999999999999:rule/eventbridge-dlq-test"
  ],
  "detail": {}
}

デッドレターキュー用SQSキュー作成

SQSコンソールからスタンダードキューを作成します。

本番運用する際は、メッセージ保持期間を最大の14日にしておいたほうが良いかもしれません。せっかくデッドレターキューに情報を保存しても、この期間が過ぎるとメッセージが消えてしまうからです。

また、下部にデッドレターキューのオプション設定欄があります。が、こちらはデフォルトの無効のままで構いません。これはSQSのデッドレターキューについての設定項目なので、今回扱うEventBridgeのデッドレターキューとは関係がありません。

デッドレターキュー設定

EventBridgeのルール詳細ページから「編集」クリックします。 ターゲット欄下部にリトライポリシーとデッドレターキューの設定欄があります。

先程作成したSQSキューを指定します。イベント最大有効期限と再試行回数はデフォルト値のままにしました。

この設定を行なうと、裏でSQSキューのアクセスポリシーも更新されます。EventBridgeから該当SQSキューにメッセージが送れるように設定されます。非コンソールつまりTerraformなどのIaCでやる場合などはこの部分も自分で設定する必要があるので注意しましょう。

  {
    "Version": "2008-10-17",
    "Id": "__default_policy_ID",
    "Statement": [
      {
        "Sid": "__owner_statement",
        "Effect": "Allow",
        "Principal": {
          "AWS": "arn:aws:iam::999999999999:root"
        },
        "Action": "SQS:*",
        "Resource": "arn:aws:sqs:ap-northeast-1:999999999999:eventbridge-dlq-test-queue"
      },
+    {
+      "Sid": "AWSEvents_eventbridge-dlq-test_dlq_Id13d49ef3-3070-48b1-9081-e4dc2a278958",
+      "Effect": "Allow",
+      "Principal": {
+        "Service": "events.amazonaws.com"
+      },
+      "Action": "sqs:SendMessage",
+      "Resource": "arn:aws:sqs:ap-northeast-1:999999999999:eventbridge-dlq-test-queue",
+      "Condition": {
+        "ArnEquals": {
+          "aws:SourceArn": "arn:aws:events:ap-northeast-1:999999999999:rule/eventbridge-dlq-test"
+        }
+      }
+    }
    ]
  }

SNSトピックのアクセスポリシーを変更

では、デッドレター(不着イベント)を発生させてみましょう。 EventBridgeのターゲットであるSNSトピックのアクセスポリシーを変更して、EventBridgeからSNSトピックへのメッセージの送信をできなくします。

SNSトピックアクセスポリシー

  {
    "Version": "2008-10-17",
    "Id": "__default_policy_ID",
    "Statement": [
      {
        "Sid": "__default_statement_ID",
        "Effect": "Allow",
        "Principal": {
          "AWS": "*"
        },
        "Action": [
          "SNS:GetTopicAttributes",
          "SNS:SetTopicAttributes",
          "SNS:AddPermission",
          "SNS:RemovePermission",
          "SNS:DeleteTopic",
          "SNS:Subscribe",
          "SNS:ListSubscriptionsByTopic",
          "SNS:Publish",
          "SNS:Receive"
        ],
        "Resource": "arn:aws:sns:ap-northeast-1:047429787746:eventbridge-dlq-test-topic",
        "Condition": {
          "StringEquals": {
            "AWS:SourceOwner": "047429787746"
          }
        }
-  },
-  {
-    "Sid": "AWSEvents_eventbridge-dlq-test_Id13d49ef3-3070-48b1-9081-e4dc2a278958",
-    "Effect": "Allow",
-    "Principal": {
-      "Service": "events.amazonaws.com"
-    },
-    "Action": "sns:Publish",
-    "Resource": "arn:aws:sns:ap-northeast-1:047429787746:eventbridge-dlq-test-topic"
    }
    ]
  }

デッドレターキュー確認

デッドレターキューにメッセージが溜まり始めました。リトライポリシーをデフォルト値にしていたので、イベント最大有効期限と再試行回数は24時間と185回のはずですが、それらの設定は無視され即デッドレターキューに送られています。今回のケースが前述のとおり、アクセス許可権限不足という「根本的な問題を解決するためのアクションが実行されるまで全再試行が失敗しうる場合」に合致するからですね。

メッセージ本文です。発行されたイベントがそのまま格納されていますね。

{
  "version": "0",
  "id": "6e66938b-fdb8-d7f5-12b1-bd1fb82cb4cf",
  "detail-type": "Scheduled Event",
  "source": "aws.events",
  "account": "047429787746",
  "time": "2020-10-14T07:43:02Z",
  "region": "ap-northeast-1",
  "resources": [
    "arn:aws:events:ap-northeast-1:999999999999:rule/eventbridge-dlq-test"
  ],
  "detail": {}
}

メッセージの属性欄で、エラーの詳細について確認できます。

InvocationsSentToDlq CloudWatchメトリック

メトリックも確認できました。ちなみにメトリクスの名前空間は「イベント〜」です。

SQSキューのアクセスポリシーを変更

今度はデッドレターキューにも配信できなくしてみましょう。先程のSQSキューアクセスポリシーを削除します。

  {
    "Version": "2008-10-17",
    "Id": "__default_policy_ID",
    "Statement": [
      {
        "Sid": "__owner_statement",
        "Effect": "Allow",
        "Principal": {
          "AWS": "arn:aws:iam::999999999999:root"
        },
        "Action": "SQS:*",
        "Resource": "arn:aws:sqs:ap-northeast-1:999999999999:eventbridge-dlq-test-queue"
-    },
-    {
-      "Sid": "AWSEvents_eventbridge-dlq-test_dlq_Id13d49ef3-3070-48b1-9081-e4dc2a278958",
-      "Effect": "Allow",
-      "Principal": {
-        "Service": "events.amazonaws.com"
-      },
-      "Action": "sqs:SendMessage",
-      "Resource": "arn:aws:sqs:ap-northeast-1:999999999999:eventbridge-dlq-test-queue",
-      "Condition": {
-        "ArnEquals": {
-          "aws:SourceArn": "arn:aws:events:ap-northeast-1:999999999999:rule/eventbridge-dlq-test"
-        }
-      }
      }
    ]
  }

設定変更後、新たなメッセージが受信できなくなりました。以下はキューに追加されたメッセージの数(NumberOfMessagesSent)のメトリックです。1分毎にイベント発行されたので毎分1でメッセージ受信していましたが、アクセスポリシー変更後ゼロになりました。

InvocationsFailedToBeSentToDlqCloudWatchメトリック

記録されていることが確認できました。

ちなみにFailedInvocationsメトリックの値は、InvocationsSentToDlqが記録されている際、InvocationsFailedToBeSentToDlqが記録されている際両方とも記録されていました。まあそうですよね。デッドレターキューへの配信が成功していようと失敗していようと元々の配信は失敗していますもんね。

まとめ

EventBridgeのデッドレターキューを使ってみました。EventBridgeを使う際「配信失敗してもオッケー」というケースはあまりないかなと思います。ですのでこれからは基本このデッドレターキューありきで考えていきたいと思います。

参考情報