【土日夜間は通知を止めたい!】 Mackerel の監視ルール・ホストをスクリプトで通知 OFF/ON する方法まとめ 〜 定期自動化目指す PoC つき #mackerelio

夜間や週末などで定期的にホストを停止しているケースでは、あわせて Mackerel の通知も止めたくなります。CLI コマンドや API を使い、スクリプトから通知を一時停止・再開する方法についてご紹介します。
2019.04.22

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

TL;DR

  • Mackerel の監視(というか通知)を自動的に停止/再開したいときってありますよね
  • それ、 mkr や API つかえば出来ます
    • ホスト単位
    • 監視ルール単位
  • 定期的な処理など、自動化することも夢じゃない

前置き

みなさん、監視してますか!(挨拶

というか、Mackerel で監視してますか!(ここまで挨拶

結論からお話ししますと、 Mackerel は API を使って監視ルールやホスト単位で通知を停止・再開させることが出来ます。つまりこれを使うことで、下記のようなものに関する通知も自動処理で停止・再開できるわけです。

  • EC2 だけでなく、 ELB など AWS インテグレーション機能で登録されたようなホスト
  • URL 外形監視みたいにホストを持たない監視ルール

どんなケースでそういったものが必要か、は後述するとして、さっそくその方法をご紹介します! やり方としては mkr コマンドを使う方法と、 curl を使って直接 API 叩く方法を紹介します。後者は応用することで Python などで実装しなおすことも難しくないでしょう。

どのやり方にしても、Write 権限を持つ API キーは必要になるので、下記の URL から用意・確認しておいてください。

なお curl で Mackerel API を叩くことに関しては、下記の記事も参考になるかと思います。

Mackerel API を利用してオーガニゼーションへ招待してみる | DevelopersIO

1. ホスト単位でミュート/ミュート解除する場合

ホストのステータスを standby に変更することで、そのホストに関する通知・警告は全て出さなくすることが可能です。この場合 maintenancepoweroff とは違い、メトリックの収集自体は続けられています。

AWS インテグレーション機能を使ってホスト登録されている ELBRDSRedshift などを一時的に監視停止したい場合はこの方法を使うといいでしょう。

mkr を使う・使わないに係わらず、実行にはホスト ID が必要です。 Mackerel Web コンソールのホスト詳細画面から調べることが可能なので、確認しメモしておいてください。

mkr コマンドを使う場合

mkr update コマンドのオプション --status (または -st)を使います。

MACKEREL_APIKEY=AAAA.....AAAA # Mackerel APIキー
HOST_ID=3ab1c2d4e5f # ホストID
NEW_STATUS=standby # 通知を止める場合。再開時には working を指定

mkr update -st ${NEW_STATUS} ${HOST_ID}

API を直叩きする場合

必要な情報は mkr を使う場合と同じです。Mackerel の API ドキュメントを参考にして実行します。

MACKEREL_APIKEY=AAAA.....AAAA # Mackerel APIキー
HOST_ID=3ab1c2d4e5f # ホストID
NEW_STATUS=standby # 通知を止める場合。再開時には working を指定

curl -s \
  -H 'X-Api-Key: '${MACKEREL_APIKEY} \
  -H 'Content-Type: application/json' \
  -d '{"status":"'${NEW_STATUS}'"}' \
  https://api.mackerelio.com/api/v0/hosts/${HOST_ID}/status

[PoC 1] 特定のホストを一括でステータス変更する

実際の運用を考えた場合、複数のホストを何らかの方法でタグ付けして一括で処理したくなると思います。Mackerel は「タグ」という概念がないので、ここでは「メモに特定の記述 AutoMute:true (タグ)が含まれているもの」を対象として、いっぺんにステータスを変更する PoC を作ってみました。

JSON 内の値を検索するために jq コマンドを使用しています。

#!/bin/bash

MACKEREL_APIKEY=AAAA.....AAAA
AUTO_MUTE_TAG_KEY=AutoMute
AUTO_MUTE_TAG_VALUE=true
NEW_STATUS=standby # 通知を止める場合。再開時には working を指定

AUTO_MUTE_TAG="${AUTO_MUTE_TAG_KEY}:${AUTO_MUTE_TAG_VALUE}"

for HOST_ID in $(
  curl -s \
    -H 'X-Api-Key: '${MACKEREL_APIKEY} \
    -H 'Content-Type: application/json' \
    https://api.mackerelio.com/api/v0/hosts |
    jq -r '.hosts[] |
      select(.memo|test("'${AUTO_MUTE_TAG}'";"i")).id'
); do
  curl -s \
    -H 'X-Api-Key: '${MACKEREL_APIKEY} \
    -H 'Content-Type: application/json' \
    -d '{"status":"'${NEW_STATUS}'"}' \
    https://api.mackerelio.com/api/v0/hosts/${HOST_ID}/status |
    jq -c '.id="'${HOST_ID}'"'
done

このコードでは API を 2回叩いています1

最初の curl でホストの一覧を取得し、その中から対象となるホストのホスト ID だけを jq で抽出するようにしています(11〜16行目)。

必要となる情報、Mackerel API や対象とするタグ情報はその前の 3〜5行目で設定しています。6行目の NEW_STATUS はここではstandbyにするようにハードコードしていますが、例えばこれをスクリプトの引数や環境変数で渡すようにすると応用が広がるでしょう。

抽出したホスト ID を for でループさせて、ひとつずつ API に POST しています(18〜22行目)。23行目の jq は、curl の出力(API の戻り値)を見やすいように成形(idの埋め込み表示)をしています。

なお、これはあくまで PoC で、実運用で使う場合は下記のようなところを考慮する必要があると思っています。

  • API キーを安全な方法で格納・参照する(Systems Manager のパラメータストアを使うなど)
  • エラー発生時の対応(戻り値をログ監視したり、mkr wrap に渡せるようにするなど)

2. 監視ルール単位でミュート/ミュート解除する場合

続いて、監視ルール単位でミュートする・しないを API でコントロールします。ホストが存在しないURL外形監視などが対象の場合に有効でしょう。

こちらではホスト ID の代わりに「モニター ID」が必要です。こちらは Mackerel のコンソールに表示されてはいないのですが、その監視ルールの詳細を表示する URL などから確認することが可能です。

さてこちらの方法ですが、mkrを使う場合も使わない場合も、手順は下記の流れになります。

  1. 現在の監視ルールの設定を取得する(JSON形式)
  2. 対象の監視ルールの isMute 要素を「True (ミュート)」または「False (ミュート解除)」に変更
  3. 変更済みの JSON を Mackerel に投入する

ホストの status とは違い、isMute だけを変更する API がないので仕方ないですね。

mkr コマンドを使う場合

mkr コマンドを使って全監視ルールを monitors.json としてダンプ(pull)し、必要な項目の isMute の値を true または false に変更して全体を push します。

monitors.json を修正するのに Ruby を使っていますが、ワンライナで書くのに書きやすかったためです。やっていることは上述(あるいはコメント)したとおりなので、得意な言語で書き直してください。

MACKEREL_APIKEY=AAAA.....AAAA
MONITOR_ID=3zy9x8w7v6u
IS_MUTE=true # or false

## 現在の監視ルールの JSON を取得する
mkr monitors pull

## 取得した JSON を修正する
ruby -rjson -e '
  # monitors.json 読み込み
  m = JSON.load(File.read("monitors.json"));
  # 特定のモニター ID に関する設定をとりだし
  i = m["monitors"].index{|i| i["id"]=="'${MONITOR_ID}'"};
  j = m["monitors"][i];
  # isMute を設定、全体の設定に戻す
  j["isMute"] = '${IS_MUTE}';
  m["monitors"][i] = j;
  # JSONフォーマットで monitors.json へ出力
  print m.to_json' > m.json && mv m.json monitors.json

## 修正した JSON を push する
mkr monitors diff
mkr monitors push

この方法は、isMute の修正の部分がうまくコード化できれば、複数の監視ルールを一括して修正するパターンにも使えます。monitors.json を CI/CD で管理している場合とは相性がよさそうに思いますし、実際に運用で利用を検討する場合は、後述する PoC 2 とはどちらの方式が良いか比較するのもよいかと思います。

API を直叩きする場合

モニター ID を指定してひとつずつ設定を取り出す・投入する API があります。アドホックに設定するにはこちらの方法を使ったほうが楽かもしれません。

こちらは jq コマンドを使って isMute の値を修正(true <-> false)しています。

MACKEREL_APIKEY=AAAA.....AAAA
MONITOR_ID=3zy9x8w7v6u
IS_MUTE=true # or false

# いまの監視ルールの内容を取得 > isMute を変更しつつ変数に
JSON=$(
  curl -s \
    -H 'X-Api-Key: '${MACKEREL_APIKEY} \
    -H 'Content-Type: application/json' \
    https://api.mackerelio.com/api/v0/monitors/${MONITOR_ID} |
  jq -c '.monitor|.isMute = '${IS_MUTE}
)

# 新しい isMute で登録し直す
curl -s \
  -H 'X-Api-Key: '${MACKEREL_APIKEY} \
  -H 'Content-Type: application/json' \
  -d "${JSON}" -X PUT \
  https://api.mackerelio.com/api/v0/monitors/${MONITOR_ID}

注意点としては、更新 API は POST HTTP メソッドではなく PUT を使う必要がある点です。明示的に指定してください。

[PoC 2] 特定の監視ルールを一括でミュート/ミュート解除したい場合

こちらも PoC を用意しました。監視ルールのメモに特定の記述 AutoMute:true (タグ)が含まれているものを対象として、一括して isMute を変更します。

#!/bin/bash

MACKEREL_APIKEY=AAAA.....AAAA
AUTO_MUTE_TAG_KEY=AutoMute
AUTO_MUTE_TAG_VALUE=true
IS_MUTE=true # or false

IFS=$'\n'
AUTO_MUTE_TAG="${AUTO_MUTE_TAG_KEY}:${AUTO_MUTE_TAG_VALUE}"

for JSON in $(
  curl -s \
    -H 'X-Api-Key: '${MACKEREL_APIKEY} \
    -H 'Content-Type: application/json' \
    https://api.mackerelio.com/api/v0/monitors |
    jq -c '.monitors[] |
        select(.memo|test("'${AUTO_MUTE_TAG}'";"i")) |
        .isMute = '${IS_MUTE}
); do
  MONITOR_ID=$(echo ${JSON} | jq -r .id)
  curl -s \
    -H 'X-Api-Key: '${MACKEREL_APIKEY} \
    -H 'Content-Type: application/json' \
    -d "${JSON}" -X PUT \
    https://api.mackerelio.com/api/v0/monitors/${MONITOR_ID} |
    jq -c '{"id":.id,"isMute":.isMute,"name":.name}'
done

おおきな流れは先の PoC 1 と同じです。ただし今度は JSON データをまるごと変数に格納する関係上、念のために 8 行目で IFS (フィールドセパレータ)を改行のみに設定しています。

20 行目でモニター ID を抽出し、そちらを指定して isMute 変更済みの JSON をそのまま PUT しています。設定する値は 6行目の IS_MUTE で設定していますので、こちらも PoC 1 と同様、スクリプトの引数や環境変数で渡すようにすると応用が広がるでしょう。

なお 26 行目の jq は単に API の戻り値を見やすくしているだけですので、処理に影響はありません。

こちらも PoC として、PoC 1 と同じ考慮点があると考えていますので、実運用に組み入れようと考えられている場合はご検討ください。

背景説明とまとめ

Mackerel を使っていくと、状況によっては「監視通知を自動的に一時停止したい」と思えてくることがあります。

例えば、AWS 上で動作させている EC2 インスタンスや RDS などのうち 24/7 でサービスしているもの以外は、夜間や週末などで定期的に止めているケースは珍しくないかと思います。ただそんな対象を Mackerel で監視していたら、されなくても良いアラートが通知されてしまうことになります。

また停止していなくても、社内システムなどは「業務時間外は通知はいらない、出社後に確認できればいい」というケースもあるでしょう(ヒアリングしたところ、うちの情シスがそうでした)。

そんなとき、対象が EC2 であれば、mackerel-agent.conf に下記のように設定をしておくことで、EC2 の stop / start にあわせてホストのステータスを変更可能です。

[host_status]
on_start = "working"
on_stop  = "poweroff"

ただし当然、これは該当のホスト (EC2) についてのみで、このホストがぶら下がっている ELB や、ホストのパブリック IP アドレスをモニターする URL 外形監視についてまで面倒を見てはくれません。

運用によっては「通知はメールや Slack のみで、夜中でも特に流しっぱなしで構わない」という場合もあるかと思いますが、通知先を電話にしていたり、クリティカルな通知は常に気にかけているような場合には要望が強くなりますし、例えそうであっても、不要な通知は所謂「オオカミ少年」問題のもとなので可能な限り避けておきたいですよね。

そんなにレアな要望ではない、と(周囲を見る限りでは)思えるので、きっと将来的なバージョンアップで Mackerel にも実装されるのではないかなぁ。。。と夢想しつつ、とりあえずのワークアラウンドとしてこういう手もあるよ、というお話でした。

脚注


  1. もちろん 2 回目のほうは mkr コマンドで代替可能です(mkr update -st ${NEW_STATUS} ${HOST_ID})。mkr コマンドが既に使われている場であれば、こちらの方がコードの見通しがよくなると思います