maprobe - Mackerel のホスト情報と連携する外形監視エージェントを作った

監視を 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 を作りました。

github.com

maprobe がやること

maprobe は次のように動作します。

  1. Mackerel API を叩いてホスト情報を取得
    • Service, Role でフィルタリング
  2. 各ホストに対して、probe(pingtcp、http、command)を実行
  3. 得られた結果をホストメトリックとして Mackerel に送信
  4. 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 でアラートを上げる形は過去の遺産でしかなく、今後はなるべく使いたくないという思想によるものです)

f:id:sfujiwara:20180420175809p:plain

たとえば 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 の再起動も不要です。

どうぞご利用ください。