[アップデート] MackerelからAmazon EventBridgeへの連係ができるようになりました

MackerelからAmazon EventBridgeへの連係ができるようになりましたので、実際に試してみました。
2020.01.30

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

みなさん、こんにちは!
AWS事業本部の青柳@福岡オフィスです。

今回は、監視サービスMackerelのアップデートについてご紹介したいと思います。

Amazon EventBridge のイベントソースとして Mackerel を選択できるようになりました - Mackerel ブログ #mackerelio
Amazon EventBridgeにアラートを通知する - Mackerel ヘルプ

Amazon EventBridgeとは

Amazon EventBridgeとは、これまでCloudWatch Eventsと呼ばれていたサービスをベースに、機能等を拡張したサービスです。

CloudWatch EventsではイベントソースとしてAWSサービス (EC2など) のみが指定できましたが、EventBridgeは新たに「パートナーイベントソース」と呼ばれるサードパーティ製SaaSアプリケーションをイベントソースに指定することができるようになりました。
現時点で「Datadog」や「Auth0」など20種類以上のパートナーイベントソースが提供されています。

今回のアップデートで、新たに「Mackerel」のアラート発生をイベントソースに指定することができるようになりました。

MackerelからEventBridgeへの連係を試してみる

今回は、以下の処理をMackerelとEventBridgeの連係を使って実現したいと思います。

  • Mackerel上で、EC2インスタンスのCPU使用率が継続して高負荷状態に陥っていることを検知する
  • EventBridge経由でLambdaを実行し、当該EC2インスタンスを強制的に再起動する

早速、EventBridgeの画面から設定してみましょう。
パートナーイベントソースの一覧から「Mackerel」の「設定」をクリックしてみます。

設定のステップについての説明が書かれていますが・・・具体的な設定項目が無いようです。
(「詳細はこちらから」のリンクをクリックすると「https://mackerel.io」へ飛ぶようになっています)

実は、パートナーイベントソースの設定はEventBridgeから行うのではなく、各パートナーSaaSアプリケーションの画面から設定するようになっています。

それでは、Mackerel Webコンソールから設定を行っていきましょう。

Mackerel側の設定を行う

まず事前準備として、MackerelがEC2インスタンスのCPU使用率を監視してアラート発生を行えるように「監視ルール」を設定します。

CPU使用率のメトリックである「custom.ec2.cpu.used」を選択して、閾値を任意に設定します。
また、一時的にCPU使用率が高負荷になったからといって再起動されても困るので、アラートが発報するのは閾値超過が3回連続して発生した場合とします。

次に「通知チャンネル」から「Amazon EventBridge」を選択して、EventBridgeとの連係の設定を行います。

「通知チャンネル名」は、Mackerel上でのみ識別される名前ですので、分かり易い名前にすれば良いでしょう。
「AWSアカウントID」は、連係するAWSアカウントIDを指定します。
「イベント名」は、EventBridgeパートナーイベントソースの名前の一部となりますので、計画的に命名しましょう。

「作成」を行ったら、Mackerel側の設定は完了です。

※ なお、今回は通知チャンネルの設定で「デフォルト通知グループに追加する」にチェックを入れることで全ての通知をEventBridgeに連係するように設定しましたが、「通知グループ」を設定することで特定の通知だけをEventBridgeに連係するようにすることも、もちろん可能です。

EventBridgeの設定を行う

EventBridgeの画面に戻りますと、パートナーイベントソースの一覧にイベントソースが追加されていることが分かります。

イベントソースの「名前」は、以下のように命名されます。

aws.partner/mackerel.io/<Mackerelオーガニゼーション名>/<さきほど指定した「イベント名」>

なお、この時点ではステータスが「保留中」と表示されます。

イベントソースの名前をクリックします。
「パートナーイベントソースの詳細」画面が表示されます。

「イベントバスと関連付ける」をクリックします。
以下の画面が表示されます。

ここでは、オプションで「アクセス許可」の設定が行えます。
他のAWSアカウントや組織 (Organizations) からのアクセスを許可したい場合には設定を追加します。

今回は何も設定せず、そのまま「関連付ける」をクリックします。

イベントソースのステータスが「アクティブ」になり、イベントバスへの関連付けが設定されたことが確認できました。

Lambda関数の準備

EventBridgeの設定は まだ続きがありますが、流れの都合上、ここで先にLambda関数を準備します。

まずはIAMロールを作成します。

assume-role-policy.json

{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Principal": {
      "Service": "lambda.amazonaws.com"
    },
    "Action": "sts:AssumeRole"
  }
}
$ aws iam create-role \
    --role-name reboot-instance-lambda-execution-role \
    --assume-role-policy-document file://assume-role-policy.json

作成したIAMロールに「EC2インスタンス再起動する権限」を与えるポリシー、および、既定のLambda実行ポリシー (AWSLambdaBasicExecutionRole) を割り当てます。

reboot-instance-policy.json

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "ec2:RebootInstances",
      "Resource": "*"
    }
  ]
}
$ aws iam create-policy \
    --policy-name reboot-instance-policy \
    --policy-document file://reboot-instance-policy.json

$ aws iam attach-role-policy \
    --role-name reboot-instance-lambda-execution-role \
    --policy-arn arn:aws:iam::123456789012:policy/reboot-instance-policy

$ aws iam attach-role-policy \
    --role-name reboot-instance-lambda-execution-role \
    --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

続いて、Lambda関数を記述します。
(サンプルのため、エラー処理の省略やAPIキーのコード埋め込みなど改善の余地があります。ご容赦ください)

lambda_function.py

import boto3
import json
import requests

def lambda_handler(event, context):
    # アラーム発生したホストのMackerelホストIDを抽出
    host_id = event['detail']['host']['id']
    print('Host ID: ' + host_id)

    # REST APIをコールして、ホストIDをキーにホスト情報を取得
    headers = {'X-Api-Key': '<Mackerel APIキー>'}
    r = requests.get('https://mackerel.io/api/v0/hosts/'+host_id, headers=headers)
    data = r.json()

    # ホスト情報からインスタンスIDを抽出
    instance_id = data['host']['meta']['cloud']['metadata']['instance-id']
    print('Instance ID: ' + instance_id)

    # 指定したインスタンスを再起動する
    client = boto3.client('ec2')
    response = client.reboot_instances(InstanceIds=[instance_id])

    print('The instance has rebooted.')

EventBridgeからLambda関数が呼び出された時に、引数 event にMackerelのアラートに関する情報が格納されています。

イベント構造体の内容については Mackerelヘルプ に説明が掲載されています。

ここからアラート発生の対象EC2インスタンスを再起動するために「インスタンスID」を特定する必要がある訳ですが、イベント構造体には「インスタンスID」の情報が含まれていません。

そこで、代わりに「ホストID」を取り出します。(「ホストID」とはMackerelにおいて監視対象ホストに一意で付与されている11桁の英数字です)
そして、Mackerel API (REST API) を使って「ホストID」をキーにホスト情報を取得します。

Mackerel APIの仕様は Mackerel APIドキュメント を参照してください。

※ 今回使用したAPIコールは、curl で表すと以下のような内容になります。

$ curl -H "X-Api-Key: <Mackerel APIキー>" https://mackerel.io/api/v0/hosts/<ホストID>

得られたホスト情報には「インスタンスID」が含まれるため、これで「インスタンスID」を特定することができます。
あとは、boto3ライブラリを使って対象EC2インスタンスを再起動します。

以上がLambda関数の処理概要です。

必要な依存パッケージ (requests) を追加して、Lambda関数を作成します。

$ mkdir package
$ pip3 install --system --target ./package requests
$ cd package
$ zip -r9 ../function.zip .
$ cd ..
$ zip -g function.zip lambda_function.py
$ aws lambda create-function \
    --function-name reboot-instance \
    --runtime python3.8 \
    --role arn:aws:iam::123456789012:role/reboot-instance-lambda-execution-role \
    --handler lambda_function.lambda_handler \
    --zip-file fileb://function.zip

EventBridgeの設定 (続き)

EventBridgeの画面に戻り、「イベント」→「ルール」を開きます。

イベントバスは通常「default」が選択状態になっています。
これを、作成した「カスタムイベントバス」に変更します。

「ルールを作成」をクリックします。
ここから、ルールの設定を行っていきます。

「名前と説明」は、分かり易いものを任意に設定してください。

「パターンを定義」では、まず「イベントパターン」を選択します。

Mackerelイベントソースの設定を行うには、以下のように選択します。

  • イベント一致パターン: 「サービスごとの事前定義パターン」
  • サービスプロバイダー: 「サービスパートナー」
  • サービス名: 「Hatena Co., Ltd.」

イベントソースがAWSサービスの場合には、各サービスの種類に応じて「イベントの種類」などの条件を指定して設定を行うことができます。
しかし、パートナーイベントソースの場合にはGUI上で細かな条件を設定することはできません。

そこで、イベント一致パターンより「カスタムパターン」を選択して、イベントパターンを直接編集する方法を取ります。

「イベントパターン」に下記のような内容を入力します。
(※ 入力後に「保存」するのを忘れないようにしてください)

{
  "account": [
    "123456789012"
  ],
  "source": [
    "aws.partner/mackerel.io/<Mackerelオーガニゼーション名>/<イベント名>"
  ],
  "detail-type": [
    "alert"
  ],
  "detail": {
    "alert": {
      "monitorName": [
        "EC2 CPU Utilization"
      ],
      "status": [
        "critical"
      ]
    }
  }
}

accountsourcedetail-type の内容は固定です。

detail に、フィルタリングしたい条件を指定します。

今回は、監視ルールが「EC2 CPU Utilization」であり、かつ、ステータスが「Critical」であるアラートのみをイベント連係の対象とするように指定しました。

※ 「ステータス」の指定は重要です。Mackerelでは障害状態からの復旧時にステータス「OK」のアラートが通知されるため、障害復旧のタイミングで予期しないイベント連係が実施されてしまう可能性があります。適宜、条件を指定するようにしましょう。

「イベントバスを選択」は、すでにカスタムイベントバスが選択されているはずですので、変更する必要はありません。

「ターゲットを選択」では、作成したLambda関数を指定します。

その他のオプションは今回は変更する必要はありません。

「タグ」は必要に応じて設定します。

最後に「作成」をクリックします。

ルールが作成されました。

これで、Mackerelのアラート発生がEventBridgeによりLambdaに連係されるようになりました。

動作確認

対象EC2インスタンス上で stress コマンドなどを使用してCPUに負荷を与えて、動きを見てみましょう。

ちなみに、Amazon Linux 2 の場合に stress コマンドを使えるようにするには以下のようにします。

$ sudo amazon-linux-extras install epel -y
$ sudo yum -y install stress

今回はt3.microインスタンス (2 vCPU) を使用しましたので、以下のようにしてCPU使用率を100%にします。

$ stress -c 2 -q &

top コマンドでCPU使用率が100%近くに上昇することを確認して、しばらく待ちます。

しばらくすると、Mackerel上で「EC2 CPU Utilization」のCriticalアラートが発生します。

ほどなくして、対象EC2インスタンスが強制的に再起動させられます。

MackerelでCPUメトリックのグラフを確認すると、CPU使用率の高負荷状態が続いたあと、再起動が行われたためにメトリックが収集できていない期間があることが分かります。

おわりに

EventBridgeがMackerelに対応したことにより、Mackerelのアラート発生を契機に、様々な処理や通知を連係して行うことができるようになりました。

今回試してみたもの以外にも、監視項目やイベントターゲットをいろいろ組み合わせて試してみたいですね!