🔔

ElastiCacheのイベントをDatadogやLambdaを使ってSlack通知

2021/04/08に公開

はじめに

Amazon ElastiCacheでは再起動やフェイルオーバーが発生すると、イベントとして記録されます。

マネジメントコンソールでは、ElastiCache > イベントで以下の通り、記録されたイベントを確認できます。

ElastiCache > イベント

こうしたElastiCacheに関するイベントをSlackに通知する方法について、色々と試したので記載します。

Slack通知方法

今回、試した方法は以下の4つです。

方法 コメント
DatadogのAWS Integration -> Datadog Monitor -> Slack 通知までに数分時間がかかる
ElastiCache -> SNS -> Datadog Monitor -> Slack - DatadogのAPIキーの扱いに難あり
- イベントをフィルタできない
ElastiCache -> SNS -> Chatbot -> Slack そもそも通知不可
ElastiCache -> SNS -> Lambda -> Slack - 即時に通知可
- イベントのフィルタが可能

監視全般をDatadogで行なっていることを前提とし、前半の2つの方法では極力Datadogに情報を集約しようとしています。

後半の2つの方法ではDatadog集約にこだわらずにSlack通知する方法を試しています。

1. DatadogのAWS Integration -> Datadog Monitor -> Slack

DatadogのAWS Integrationを有効にしていると、ElastiCacheのイベントは自動的にDatadogに連携されます。

以下はDatadogで、Monitors > New Monitor > Eventと選択した時の画面ですが、ElastiCacheのイベントが表示されていることがわかります。

ElastiCacheのEvent

このようなイベントを検知してSlack通知するために、Datadogで以下の新規Monitorを作成します。

  • Match events containingfailoverとする
  • fromAmazon ElastiCacheとする
  • tagsource_identifier:{ElastiCacheの名前}とする
  • 通知先をSlackとする

Monitor

Monitor作成後、検証として手動でElastiCacheのフェイルオーバーを発生させます。

以下は手動フェイルオーバー直後にマネジメントコンソールで確認したElastiCacheのイベントです。

ElastiCacheのEvent

そして、このフェイルオーバーイベントはDatadogのMonitorで検知され、DatadogからSlackには以下のように通知されました。

Slack

しかし、ElastiCacheのイベント発生時刻(23:43)とSlackの投稿日時(23:51)を比較するとわかりますが、Slack通知されたのは、フェイルオーバー発生から約8分後でした。

DatadogのAWS Integrationでは、確かにElastiCacheのイベントが自動連携されるのですが、Datadog側への反映には数分の時間がかかるようです。

そのため、DatadogのMonitorによる検知もその分だけ遅れ、結果としてSlack通知のタイミングもフェイルオーバー発生から数分経過後となってしまいました。

2. ElastiCache -> SNS -> Datadog Monitor -> Slack

ElastiCacheでは連携先のSNSトピックを指定することが可能であり、SNSを介してイベントを様々な先に通知できます。

ElastiCacheの通知ARN

ここではElastiCacheの連携先SNSの通知先をDatadogのエンドポイントとし、さらにDatadog MonitorからSlack通知してみます。

まず、スタンダードタイプのSNSトピックを作成します。

SNSトピックのアクセスポリシーは以下の通りとします。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": "SNS:Publish",
      "Resource": "{自身のSNSトピックARN}",
      "Condition": {
        "StringEquals": {
          "AWS:SourceOwner": "{自身のAWSアカウントID}"
        }
      }
    }
  ]
}

次にこのSNSトピックに対し、サブスクリプションを作成します。

サブスクリプションのプロトコルはHTTPSを選択し、エンドポイントはDatadogの公式ドキュメントに記載されている

  • https://app.datadoghq.com/intake/webhook/sns?api_key=<API KEY>

とします(DatadogのリージョンがUSの場合)。

DatadogのAPIキーは、https://app.datadoghq.com/account/settings#api より取得します。

サブスクリプション

以上により、ElastiCacheで発生したイベントがSNS経由でDatadogに反映されるようになります。

以下はDatadogで、Monitors > New Monitor > Eventと選択した時の画面ですが、ElastiCacheのイベントがSNS経由で反映されていることがわかります。

Datadogに反映したSNS

こうしたSNSによる通知をDatadogのMonitorで検知してSlack通知するために、Datadogにて以下の新規Monitorを作成します。

  • Match events containingは空欄とする(空欄にした理由は後述)
  • fromAmazon SNSとする
  • tagtopic:{SNSトピックのの名前}とする
  • 通知先をSlackとする

Monitor

Monitor作成後、改めて検証として手動でElastiCacheのフェイルオーバーを発生させると、Datadogはこれを検知し、Slackに以下の通知を行いました。

DatadogからのSlack通知

ただ、この方法は、SNSサブスクリプションのエンドポイント欄にDatadogのAPIキーを秘匿せずに設定することとなるので、APIキーを参照可能な人間を限定したい場合には適しません。

SNSサブスクリプションのエンドポイント欄

また、今回Datadog Monitorで以下のようなJSONから特定のイベント種類だけをフィルタする方法がわかりませんでした。

{
    "ElastiCache:イベント名": "ElastiCache名"
}

そのため、Match events containingを空欄としたのですが、結果としてあらゆるElastiCacheイベントがSlack通知されることとなっており、実用性に欠けたものとなってしまっていました。

3. ElastiCache -> SNS -> Chatbot -> Slack

この経路でのSlack通知はできませんでした。

AWS Chatbotは、ElastiCacheのイベントには対応していないようで、Chatbotの部分は後述するLambdaを使う必要があります。

(もし仮にChatbotがイベントのフィルタリングも含めてElastiCacheに対応していたとすれば、Lambdaのソースコードを管理せずに済むという利点が考えられます)

4. ElastiCache -> SNS -> Lambda -> Slack

この経路では、Lambdaのソースコードを管理する必要はあるものの、特に問題なくSlack通知することができます。

以下はPythonによるLambdaのコード例です。今回、イベント種類の絞り込み処理は割愛していますが、そうしたコードを追加すれば任意のイベントのみを通知可能かと思います。

なお、SlackのWebhook URLはあらかじめパラメータストアに格納しておき、そのパラメータストアのパスは環境変数から取り出すようにしています。

import boto3
import json
import os
import urllib.request

def lambda_handler(event, context):
    ssm = boto3.client('ssm')

    ssm_res = ssm.get_parameter(
        Name = os.environ['PARAMETER_STORE_PATH_WEBHOOK_URL'],
        WithDecryption = True
        )
    url = ssm_res['Parameter']['Value']

    data = {
        'text': event['Records'][0]['Sns']['Message']
    }

    headers = {
        'Content-Type': 'application/json',
    }
    
    req = urllib.request.Request(url, json.dumps(data).encode(), headers)
    
    with urllib.request.urlopen(req) as res:
        res_body = res.read().decode()
    
    print(res_body)

パラメータストアを参照するので、Lambdaに割り当てられたIAMロールでは以下の権限を追加で付けるようにします。

// 略
        {
            "Effect": "Allow",
            "Action": [
                "sts:AssumeRole",
                "ssm:GetParameter"
            ],
            "Resource": "*"
        }
// 略

このコードをLambdaにデプロイ後、手動でElastiCacheのフェイルオーバーを発生させると、SNS経由で起動したLambdaはSlackに対して以下の通知を行いました。

(ひとつの通知にまとまっているように見えますが、実際は3回に分けて通知されます)

Slack

ElastiCacheのイベント欄で表示されるイベント発生時刻と比較しても、即時に通知できていることがわかります。

ElastiCacheのイベント欄

終わりに

以上、ElastiCacheのイベントをSlackに通知する方法複数の検証でした。

実用性の面では、最後に紹介したElastiCache -> SNS -> Lambda -> Slackが一番良さそうです。

本記事が参考になれば幸いです。

参考

Discussion