kmuto’s blog

はてな社でMackerel CREをやっています。料理と旅行といろんなIT技術

Mackerelアラートの一定時間後クローズスクリプトを作ってみた

Mackerelでログ監視によく使われるcheck-logプラグインは、毎分ごとにログを見て追記された中で、検出対象文字列を発見したときにアラートを発報し、発見されなくなったら正常に戻ったものと見て自動クローズする、というのがデフォルトの挙動となっている。つまり、ずっと出続けるメッセージのようなものでなければ、発報のだいたい1分後にそれがクローズされることになる。

これが困るというときには、prevent_alert_auto_close = trueプラグイン設定に追加する方法がある(チェック監視項目を追加するを参照)。これは自動クローズをしないことを宣言するオプションで、クローズはユーザーの手動作業に委ねられる。

しかし、こうした手動対処すべきアラートが蓄積すると、アラートの表示が日常になって注意を払わなくなってしまう。

ということでシェル芸人として、prevent_alert_auto_close = trueが使われている環境向けに、発報から一定時間過ぎたアラートをばっさりクローズするLinuxシェルスクリプトを書いてみた。そんなに長くないので全文掲載。Gistにも置いている。

#!/bin/bash
CLOSE_AFTER_MINUTES="60"
CLOSE_CHECK_ONLY=true
DRYRUN=

if [ -z "$(mkr --version 2>/dev/null)" ]; then
  echo "ERROR: Missing mkr. Install mkr package."
  exit 1
fi
if [ ! -f "/etc/mackerel-agent/mackerel-agent.conf" -a -z "$MACKEREL_APIKEY" ]; then
  echo "ERROR: Define MACKEREL_APIKEY."
  exit 1
fi

if [ "$CLOSE_CHECK_ONLY" ]; then
  RESULT=$(mkr alerts -jq '.[] | select(.type == "check") | [.id, .openedAt] | @tsv')
else
  RESULT=$(mkr alerts -jq '.[] | [.id, .openedAt] | @tsv')
fi

echo "$RESULT" | {
  CLOSES=
  while read line; do
    # alertID openedAt
    array=($line)  # bash
    if [ "$(date +"%s")" -ge "$(expr ${array[1]} + $CLOSE_AFTER_MINUTES \* 60)" ]; then
      CLOSES="$CLOSES ${array[0]}"
    fi
  done

  if [ "$CLOSES" ]; then
    $DRYRUN mkr alerts close --reason "auto closed because more than ${CLOSE_AFTER_MINUTES} minutes passed." $CLOSES
  fi
}

頭のほうで設定用の変数定義をしているので、必要に応じて適宜変更していただきたい(格好良くコマンドラインオプションにしてもよいだろう)。

  • CLOSE_AFTER_MINUTES="60": 単位は分。アラートがオープンされて以来この分数が過ぎていたらクローズする。1日なら1440。
  • CLOSE_CHECK_ONLY=true: チェック監視のアラートだけを対象にする。「CLOSE_CHECK_ONLY=」とするとほかの監視ルールも含むすべてのアラートが対象になる。実際のところ、check-log以外のものはメトリック閾値なりチェック結果なりに問題があれば再発報されるはずなので、それでもよいのかもしれない。
  • DRYRUN=: ドライラン指定。実際にはクローズせず、実行内容を表示するだけにするには「DRYRUN=echo」とする。

実際の実行にはmkrコマンドを使っている。現在の環境のものではなく別のオーガニゼーションに対して実行するのであれば、MACKEREL_APIKEY環境変数も事前に指定しておこう。

$ ./deferclose.sh

または(別のオーガニゼーションに対して実行する場合)

$ MACKEREL_APIKEY=〜 ./deferclose.sh

棚卸しタイミングに限らず、適当にcronで回してもよいだろう。

以下は余談というかコードの説明。中身としてはだいぶシンプルで、少しシェル芸を込めている。

mkr alertsコマンドでオープン中のアラート一覧JSONを取得する。mkrコマンドはgo-jqを内包しているので、jq記法でJSONを整形できる。アラートIDと日付を取り出し(チェック監視だけの場合はtypeがcheckのもののみにフィルタする)、TSV化して出力する。

echo "$RESULT"のように""で囲んでいるのは改行を再現するため。

クローズにはmkr alerts closeを利用できるが、複数のアラートを一度でクローズするためにCLOSESという変数を作っている。パイプ(|)でつないだ先がサブシェルになるので、CLOSESを構成して利用する部分はすべてその中で完結するよう{ }でグループ化している。この挙動はわりとハマリポイントだねぇ(結局bash依存になっているので、lastpipe指定すれば済むだけだったかも)。

array=($line)でtsvから受けたタブ区切り文字列を分解している。ここがbash専用様になってしまった。TSVもCSVも微妙に扱いづらい(mkr jqのCSVには""が含まれる)。

時間は、頻出テクニックのdate +"%s"によるUnixエポック秒と、exprコマンドを使った計算結果で比較している。exprにはCLOSE_AFTER_MINUTES変数の内容をそのまま展開させているので、実は式を変数内に含めてしまうこともできる(エスケープも考えないといけないが)。

シェル芸いろいろ便利だね。