ForgeVision Engineer Blog

フォージビジョン エンジニア ブログ

RDS イベントサブスクリプションが EventBridge でフィルタリングして飛ばせました

こんにちは、AWS グループの尾谷です。

ずいぶん前のことですが、2021 年に構築させていただいた AWS 環境に Amazon RDS がありました。
当時、RDS のイベントを SNS 通知するように設定したところ「毎日以下のバックアップの通知が届くのが煩わしい。」という話になり、

  • RDS-EVENT-0001 : Backing up DB instance
  • RDS-EVENT-0002: Finished DB Instance backup

でも「上記は通知しないで欲しいが、RDS-EVENT-0086 (だったと思います。) は通知したい、その他の通知も原則一度は通知を飛ばして、不要だと判断したら通知を止めたい。」といったご要望をいただきました。

RDS イベントサブスクリプションは、以下のスクリーンショットのように当時から特定のイベントカテゴリーのみ通知させる機能があったのですが、イベント ID を指定して通知を止めるような機能はありませんでした。

そこで、Lambda を組んで以下のようにフィルタリングして通知する仕組みを実装しました。

  1. RDS のイベントが発生
  2. RDS イベントサブスクリプションで送信先に設定した Amazon Notification Service (SNS) のトピックが立つ
  3. SNS サブリクリプションした Lambda 関数がトリガー
  4. Lambda 関数が event からイベント ID を取得
  5. Lambda 関数は、不要なイベントをフィルタリング
  6. フィルターされなかった通知が必要なイベントは、SNS トピック (2) へ
  7. SNS トピック (2) をサブスクリプションした AWS Chatbot がトリガー
  8. ChatBot が Slack に通知

以下のような event データが届くので、

{
    "Records": [
        {
            "EventSource": "aws:sns",
            "EventVersion": "1.0",
            "EventSubscriptionArn": "arn:aws:sns:ap-northeast-1:000000000000:otani-test-rds-event-subscription:1111111111111-1111-1111-111111111111",
            "Sns": {
                "Type": "Notification",
                "MessageId": "11111111-1111-1111-1111-111111111111",
                "TopicArn": "arn:aws:sns:ap-northeast-1:000000000000:otani-test-rds-event-subscription",
                "Subject": "RDS Notification Message",
                "Message": "{\"Event Source\":\"db-instance\",\"Event Time\":\"2024-05-06 12:41:56.674\",\"Identifier Link\":\"https://console.aws.amazon.com/rds/home?region=ap-northeast-1#dbinstance:id=database-1-instance-1\",\"Source ID\":\"database-1-instance-1\",\"Source ARN\":\"arn:aws:rds:ap-northeast-1:000000000000:db:database-1-instance-1\",\"Event ID\":\"http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Events.Messages.html#RDS-EVENT-0006\",\"Event Message\":\"DB instance restarted\",\"Tags\":{}}",
                "Timestamp": "2024-05-06T12:42:24.568Z",
                "SignatureVersion": "1",
                "Signature": "XXXXXX",
                "SigningCertUrl": "https://sns.ap-northeast-1.amazonaws.com/SimpleNotificationService-XXXXX.pem",
                "UnsubscribeUrl": "https://sns.ap-northeast-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:ap-northeast-1:000000000000:otani-test-rds-event-subscription:11111111-1111-1111-1111-111111111111",
                "MessageAttributes": {
                    "Resource": {
                        "Type": "String",
                        "Value": "arn:aws:rds:ap-northeast-1:000000000000:db:database-1-instance-1"
                    },
                    "EventID": {
                        "Type": "String",
                        "Value": "RDS-EVENT-0006"
                    }
                }
            }
        }
    ]
}

Lambda は以下のようなイメージでコーディングしました。

def lambda_handler(event, context):
    try:
        records = event.get('Records')
        sns = records[0].get('Sns')
        message_attributes = sns.get('MessageAttributes')
        event_id = message_attributes.get('EventID').get('Value')
    except:
        print('error occurred')
        return
  
    if event_id == 'RDS-EVENT-0001':
        print(event_id + ' need not notice.')
        return
  
    if event_id == 'RDS-EVENT-0002':
        print(event_id + ' need not notice.')
        return

現在は Amazon EventBridge がある

この話を同僚にしたところ、今は Lambda を使わなくても Amazon EventBridge が進化していて、自由にフィルタリングできるようになっていると教えてもらったので、検証してみました。

まず「EventBridge ルールを作成」から、イベントパターンを持つルールを選択して [次へ] ボタンをクリックしました。

サンプルイベント - オプション では、 RDS DB Instance Event が選択でき、フェイルオーバーイベントの JSON サンプルが表示されました。これを使って、フィルタリングテストをしてみます。

下部のイベントパターンで、イベントソースに RDS と入力すると、Relational Database Service (RDS) が候補として表示されました。
これを選択し、

イベントタイプを RDS DB Instance Event にしました。

すると、イベントパターンが自動生成されました。
自分でコーディングしなくても、コードが生成されるのは便利です。

ただ、特定の EVENT ID でフィルタリングしないといけないので「パターンフォームを使用する」から「カスタムパターン (JSON エディタ)」に変更しました。これで、編集できなかった JSON が自由にコーディングできるようになりました。

しかも、この JSON エディタにはドロップダウンメニューが備わっており、ドロップダウンメニューから

「以外マッチング」を選択して、「挿入」ボタンをクリックすると

以下のようにコードが自動生成されました。AWS さん、これは手厚すぎます。。

「RDS-EVENT-0049」を除外してみようと思います。イベントパターンを以下のように書き換えて、

{
  "source": ["aws.rds"],
  "detail-type": ["RDS DB Instance Event"],
  "detail.EventID": [ { "anything-but": [ "RDS-EVENT-0049" ] } ]
}

「テストパターン」ボタンをクリックしてテストを実行したところ、条件一致せず

「RDS-EVENT-0049」を「RDS-EVENT-0050」に書き換えて再度テストしたところ、今度は条件一致しました。
正常にフィルタリングができているようです。

複合判定もできました。「RDS-EVENT-0001」と「RDS-EVENT-0002」を除外するには、以下のようなルールを記述しました。

{
  "source": ["aws.rds"],
  "detail-type": ["RDS DB Instance Event"],
  "detail.EventID": [ { 
    "anything-but": [ "RDS-EVENT-0001", "RDS-EVENT-0002" ] } ]
}

まとめ

いかがでしたでしょうか。
つい力技で Lambda を使いがちですが、Lambda で利用する Python や Node.js は使用期限があります。
弊社では、数年前にコーディングした Python 3.8 が間も無くサポート外になるということで、その更新作業に追われています。

EventBridge ルールを使いこなせれば、メンテナンス問題から解放されそうです。

以上です。
最後までお読みくださりありがとうございました。