監視を Zabbix から Mackerel に移行しています。そこで困ったことを OSS を書いて解決しようシリーズのお時間です。
ホストのダウン検知を早くしたい
Mackerel の監視は Push 型と呼ばれるもので、mackerel-agent が Mackerel サーバに対してメトリクスを送信する形態です。そのため、agent を稼働させているホストがダウンしたという事象は、「一定時間サーバに対して情報が送られてこない」ことによって、組み込みのアラート "connectivity" として検知されます。
これによって困ることとしては、以下があります。
- ホストが実際にダウンしてから7分程度経過しないとアラートが上がらない
- あまり短時間で判断してしまうと誤検知が増えるからでしょうか
- もうちょっと早く検知したいです
- "connectivity" アラートは Critical レベルしかなく、設定変更不可
- 大抵のホストは多重化してあるため、1台ダウンするのは大きな問題にならない
- アラートレベルの設定指針として、「即対応しないとサービスに影響が出るものは Critical」「翌営業日に確認・対処でいいものは Warning」としているので、Critical でない事象で Critical を上げたくない
内部リソースに対する外形監視をしたい
Mackerel の監視の基本は Push 型ですが、URL外形監視機能 というものがあり、これは Mackerel のサーバから HTTP(S) で対象にアクセスして監視する機能です。
外部に公開している HTTP のリソースについてはこれで問題はないのですが、足りないところとしては以下があります。
ということで、そのあたりを補完する外形監視エージェント、maprobe を作りました。
maprobe がやること
maprobe は次のように動作します。
- Mackerel API を叩いてホスト情報を取得
- Service, Role でフィルタリング
- 各ホストに対して、probe(ping、tcp、http、command)を実行
- 得られた結果をホストメトリックとして Mackerel に送信
- 60秒ごとに繰り返し
組み込みで ping, tcp, http の監視機能と、Mackerel agent plugin 形式で出力するコマンドを実行する機能があります。
監視対象のアドレス等は、Go の text/template 形式で、mackerel-client-go#Host の持っている値を展開することで指定します。
設定ファイルの例
probes: - service: production role: server ping: address: '{{ .Host.IPAddresses.eth0 }}' - service: production role: InternalELB http: url: 'http://{{ .Host.CustomIdentifier }}/api/healthcheck' post: POST headers: Content-Type: application/json body: '{"hello":"world"}' expect_pattern: 'ok' - service: production role: redis tcp: host: '{{ .Host.IPAddress.eth0 }}' port: 6379 send: "PING\n" expect_pattern: "PONG" quit: "QUIT\n" command: command: "mackerel-plugin-redis -host {{ .Host.IPAddress.eth0 }} -tempfile /tmp/redis-{{ .Host.ID }}"
このように、複数の Service / Role に所属しているホストに対して外形監視を行い、ホストメトリックを送信することができます。 (なお、チェック監視ではなくメトリック形式なのは、チェック監視のような 0/1/2 でアラートを上げる形は過去の遺産でしかなく、今後はなるべく使いたくないという思想によるものです)
たとえば ping であればこのようなグラフが得られるので、この値に対して適宜アラートを設定する、ということになります。
Critical がなく Warning だけのアラートも設定できますし、式機能を使えば特定の Role に所属するホストの 10% 以上に ping が疎通できなくなったらアラート、というような設定も可能になります。
コマンド実行の応用例
特定の Service / Role に存在するホスト(情報)に対して、任意のコマンドを実行できるため、「Mackerel から退役に失敗して残ってしまっているが、既に EC2 側にはインスタンスが存在しないホスト」の掃除をする例です。
probes: - service: production role: EC2 command: command: 'cleanup.sh {{.Host.ID}} {{index .Host.Meta.Cloud.MetaData "instance-id"}}'
Mackerel のホストIDと EC2 のインスタンス ID を引数にして、既に terminate されているインスタンスなら mkr retire を実行して退役処理を行う script を実行します。
#!/bin/bash set -u host_id="$1" instance_id="$2" exec 1> /dev/null # dispose stdout result=$(aws ec2 describe-instance-status --instance-id "${instance_id}" 2>&1) if [[ $? == 0 ]]; then exit elif [[ $result =~ "InvalidInstanceID.NotFound" ]]; then mkr retire --force "${host_id}" fi
なお、このような処理は毎分実行するには負荷が大きい場合があるので、maprobe once
というサブコマンドで、処理を一回だけ実行できるようにもしてあります。このような掃除だったら、1時間に1回 cron で実行する、でも十分でしょう。
コンテナ環境への対応
DockerHub にイメージを用意してあります。https://hub.docker.com/r/fujiwara/maprobe/
maprobe のように複雑な設定ファイルが必要なものをコンテナにすると、設定ファイルをコンテナ内に配置するためだけにいちいち自前のイメージを作成する必要があって面倒です。
なので、-config
引数の値はローカルファイルだけではなく、HTTP(S)と Amazon S3 の URL を処理できるようにしました。
適当なところに設定ファイルを配置し、環境変数 CONFIG
にその URL を指定すれば、公式イメージをそのまま動作させられて便利ですね。
config は更新を検知して自動で再読み込みするので、maprobe agent の再起動も不要です。