Mackerel チェック機能の意外と知られていないテクニック集

この記事は [Mackerel Advent Calendar 2016](http://qiita.com/advent-calendar/2016/mackerel) の22日目の記事です。

# はじめに
Mackerel と聞くと、どうしてもサービスメトリクスに着目しがちなのですが実際には Mackerel には大きく2つ機能があります。

* サービスメトリクス(リソース情報の可視化)
* チェック機能(アラート通知)

サービスメトリクスの上限値などからアラート通知を行う事もできるのですが、何らかの事象を継起にアラート通知を発生させる事も可能です。自前でこういった物を作る場合

* 発生と復旧のステータス管理
* メール通知の再送制御

などなど、後から面倒な事が出てきて割とめんどくさい事だったりします。しかし Mackerrel のチェック機能を使えばその辺りを全て Mackerel に任せられます。今日はこの Mackerel のチェック機能を使った、意外と知られていないテクニックをご紹介しますよ、奥さん！

![大売出し](https://qiita-image-store.s3.amazonaws.com/0/665/1197a0de-607c-7739-ff15-5f1271bcd534.png)

# check-log

ログファイルに出力される特定の文言を検出します。

```
[plugin.checks.log]
command = "/path/to/check-log --file=/path/to/file --pattern=REGEXP --warning-over=N --critical-over=N"
```

ログファイル `/path/to/file` にてパターン `REGEXP` が検出されると警告や障害として扱います。文言が出現するたびにエラーになられると困る人は、`--warning-over` や `--critical-over` で出現回数 `N` を指定する事もできます。

![お得情報](https://qiita-image-store.s3.amazonaws.com/0/665/f8cd86d7-54b1-f9f9-8451-23cd83c5e1a5.png)

ここからがお得情報。実は[ちょっと前](https://mackerel.io/ja/blog/entry/weekly/20161028)に `--encoding` オプションが追加されています。Windows な人は `--encoding sjis` を付け加える事で、Shift_JIS のログファイルも扱えます。 

# check-procs

プロセスに関するあらゆる監視が可能です。プロセスのリソース値が一定以上であれば障害とする、等といった事が可能です。

```
[plugin.checks.procs]
command = "/path/to/check-procs --pattern=PROCESS_NAME --state=STATE --warning-under=N"
```

`--pattern` で正規表現が扱えるので任意のユーザが起動するコマンドも監視できますね。

![お得情報](https://qiita-image-store.s3.amazonaws.com/0/665/f8cd86d7-54b1-f9f9-8451-23cd83c5e1a5.png)

例えば、身近でこんな事は無いでしょうか。

> 何かコマンド実行を実行したまま、しばしば落とすのを忘れて帰ってしまう

コマンドが課金対象だったりすると大変ですね。こんな場合でも check-procs は使えます。

```
[plugin.checks.procs]
command = "/path/to/check-procs --pattern='ping aws-server' --critical-over 0 --esec-over 3600"
```

`--critical-over` を 0 に、`--esec-over` を許容する秒数に設定する事で、「起動していないならエラーとしないけど、起動しているなら起動時間が 3600 秒以上だとエラーにする」といった事が実現可能です。上記の例だと `ping aws-server` が実行されてから 3600 秒経過すると Mackerel 経由でアラートが送信されます。

# check-ntservice

Windows ではNTサービスも監視対象となり得ます。しかし Windows サービス名は特殊で check-procs では検出できない物があります。そこで check-ntservice を使います。

```
[plugin.checks.ntservice]
command = "/path/to/check-ntservice --service-name=SERVICE_NAME"
```

`SERVICE_NAME` で示すサービスが停止するとアラートが上がります。

![お得情報](https://qiita-image-store.s3.amazonaws.com/0/665/f8cd86d7-54b1-f9f9-8451-23cd83c5e1a5.png)

この Windows サービス名、どの値を設定するか分からないと思うかもしれません。そんな場合はプラグインが置かれているディレクトリ(`C:/Program Files (x86)/Mackerel/mackerel-agent`)で以下のコマンドを実行します。

```
check-ntservice -l
```
すると、検査可能なサービスの名前が一覧表示されます。

```
・・・略・・・
PNRPAutoReg: PNRP Machine Name Publication Service
PNRPsvc: Peer Name Resolution Protocol
PolicyAgent: IPsec Policy Agent
postgresql-x64-9.4: postgresql-x64-9.4 - PostgreSQL Server 9.4
Power: Power
ProfSvc: User Profile Service
ProtectedStorage: Protected Storage
・・・略・・・
```

この左側(例: `postgresql-x64-9.4`)を `SERVICE_NAME` に設定します。簡単ですね。

# おまけ

Windows のサーバを運用していると Windows Update に伴う脆弱性パッチのリリースを気に掛ける必要があります。気付くのが遅くて脆弱性を突かれた、なんて事が起きたら大変ですね。

![お得情報](https://qiita-image-store.s3.amazonaws.com/0/665/f8cd86d7-54b1-f9f9-8451-23cd83c5e1a5.png)


こういった場合であっても Mackerel であればチェックAPIが公開されているので以下の様なチェッカーを作る事で簡単に監視する事ができます。

```go
package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"io"
	"log"
	"net/http"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/go-ole/go-ole"
	"github.com/go-ole/go-ole/oleutil"
)

var (
	hostId  = flag.String("h", "", "hostId for mackerel")
	pattern = flag.String("p", "", "pattern to grab process")
	dryrun  = flag.Bool("d", false, "dry run")
)

type source struct {
	Type   string `json:"type"`
	HostId string `json:"hostId"`
}

type alert struct {
	Source     source `json:"source"`
	Name       string `json:"name"`
	Status     string `json:"status"`
	Message    string `json:"message"`
	OccurredAt int64  `json:"occurredAt"`
}

func sendAlert(msg string) error {
	var buf bytes.Buffer
	json.NewEncoder(&buf).Encode(&struct {
		Reports []alert `json:"reports"`
	}{
		Reports: []alert{{
			Source:     source{Type: "host", HostId: *hostId},
			Name:       "windows-update",
			Status:     "CRITICAL",
			Message:    msg,
			OccurredAt: time.Now().Unix(),
		}},
	})
	req, err := http.NewRequest("POST", "https://mackerel.io/api/v0/monitoring/checks/report", &buf)
	if err != nil {
		return err
	}
	req.Header.Set("X-Api-Key", os.Getenv("MACKEREL_API_KEY"))
	req.Header.Set("content-type", "application/json")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	io.Copy(os.Stdout, resp.Body)
	resp.Body.Close()
	return nil
}

func main() {
	flag.Parse()
	if *pattern == "" || *hostId == "" {
		flag.Usage()
		os.Exit(2)
	}
	re, err := regexp.Compile(*pattern)
	if err != nil {
		log.Fatal(err)
	}
	if os.Getenv("MACKEREL_API_KEY") == "" {
		log.Fatal("require $MACKEREL_API_KEY")
	}
	os.Setenv("GODEBUG", "http2client=0")

	ole.CoInitialize(0)
	unk, err := oleutil.CreateObject("Microsoft.Update.Session")
	if err != nil {
		log.Fatal(err)
	}
	defer unk.Release()

	updateSession := unk.MustQueryInterface(ole.IID_IDispatch)
	defer updateSession.Release()

	updateSearcher := oleutil.MustCallMethod(updateSession, "CreateUpdateSearcher").ToIDispatch()
	defer updateSearcher.Release()

	searchResult := oleutil.MustCallMethod(updateSearcher, "Search", "IsInstalled=0 and Type='Software' and AutoSelectOnWebSites=1").ToIDispatch()
	updates := oleutil.MustGetProperty(searchResult, "Updates").ToIDispatch()
	count := int(oleutil.MustGetProperty(updates, "Count").Value().(int32))

	titles := []string{}
	for i := 0; i < count; i++ {
		update := oleutil.MustGetProperty(updates, "Item", i).ToIDispatch()
		title := oleutil.MustGetProperty(update, "Title").ToString()
		log.Println(title)
		if re.MatchString(title) {
			titles = append(titles, title)
		}
	}
	if !*dryrun && len(titles) > 0 {
		title := strings.Join(titles, "\n")
		rs := []rune(title)
		if len(rs) > 1024 {
			title = string(rs[:1021]) + "..."
		}
		sendAlert(title)
	}
}
```

これをビルドして出来た実行モジュールをタスクスケジューラで毎晩起動する様にしておきます。引数は以下の通り。

```
check-widnowsupdate -h XXXX -p セキュリティ
```

※ホストID XXXX は mackerel のホスト詳細を開いた際の URL 末尾になります。
※起動には環境変数 MACKEREL_API_KEY が必要です。

これを実行するとセキュリティ関連パッチがリリースされたその夜に以下の様なアラートが上がります。

![WindowsUpdateが出たよ](https://qiita-image-store.s3.amazonaws.com/0/665/4858ebca-6696-c52e-5550-7647e7a72e90.png)

あとは Mackerel から送られてきたアラートメールを見て Windows Update すれば ok です。
※実行に時間が掛かるので mackerel-agent からは呼び出すのは推奨しません。

# まとめ

作り手が面倒だと思っている監視の大部分を Mackerel 側で行ってくれるため、多くの時間を節約する事ができて良いですね。監視はしたいけど、メール通知やステータス管理が面倒という方は Mackerel の導入を考えてみては如何でしょうか。