b-shock. Fortress

Sensu監視プラグインの実装

  1. サーバ構築編
  2. クライアント構築編(FreeBSD)
  3. クライアント構築編(Ubuntu)
  4. 監視プラグイン実装編
  5. 通知プラグイン実装編

全5回予定。

少々間が空いたが、ようやく監視プラグイン(check)の実装について述べる。 まさに、これこそが本題。
実は最近、Mastodonインスタンスで、Sidekiqの監視を始めた。こいつを題材に 話を進めていく。

監視プラグインのインターフェース

Sensuでは、監視プラグインはcheckと呼ばれる様だ。
仕様上はNagiosプラグインと互換性があり、基本的に流用可能。 この利点があったからこそ、Nagiosの後継にSensuを選んだというのもある。 実際には僅かに修正が必要だったが、とはいえ基本的には同じものが動いた。この点はありがたい。

歴史あるNagiosプラグインに由来した仕様の為か、インターフェースは非常に単純。

  • エラーの深刻度に合わせた終了コード(0:OK, 1:WARNING, 2:CRITICAL)を返すこと。 0〜2以外の値を返した時は、UNKNOWNとみなす。
    はて?Nagiosでは255がUNKNOWNではなかったか?仕様が拡張された?
  • 標準出力、又は標準エラー出力にメッセージを出力すること。
    はて?Nagiosでは改行なし、しかも標準出力のみというルールがあったはずだが、 これも仕様が拡張された?

この仕様通りに実装できない言語は(シェルスクリプト含め)存在しないと思われるので、 お好みの言語を使えばよいと思う。
おれは自分ではPHPerであるというアイデンティティを持っているがw、このようなWebアプリ以外の 用途ではRubyを使用している。以降、Rubyで実装するものとして説明を続ける。

ところで、そもそもプラグインは自分で書かないでも、コミュニティプラグインなるものがある様だ。
おれ自身は「監視すべき要件を把握している自分自身が実装すべき」というポリシーだが、 そのようなポリシーを持たない(むしろ「車輪の再発明はすべきではない」)向きは、 出来合いのプラグインを積極的に使っていけばよいと思う。

Sensuは、原則的にローカルを監視する。

Sensuクライアントは原則的にローカルの監視を行い、RabbitMQを通じて、監視の結果を監視サーバに 送信する。この設計により、監視の負荷を分散しているのだと思われる。
リモートの監視は、通常は行わない。 可能ではあるのだろうが、本質的に相容れない利用法であり、 通常はやらないみたい。

HTTP監視等は特に、リモートからの外形監視である必要がある様に思われたが、ツールの 設計思想上のポリシーと割り切って諦めることにした。
外形監視でなければいけない理由は、主にネットワーク経路上の問題も検知したいから。 先日、あのGoogleがやらかしてw、日本全国の回線が広範囲に渡って麻痺したような事例も 確かにありはするが、こうした障害の検知もkeepaliveの更新を監視する(RabbitMQを通じた通信が 常時疎通しているか)ことで担保できる。
ローカル監視で異常なし、keepaliveも異常なし。この両方が正常である場合に全て問題なしと みなす…ということで概ね問題ないのではないかと考え直した。

またローカル監視ならば、ローカルからのみ接続できる類いのサービス(例えば、接続にUNIXドメインソケットを 利用するもの)の監視も容易に行うことが可能であることは、言うまでもない。

監視の要件

おれが試験的に運用しているMastodonインスタンス
実質的には、いわゆる「おひとりさまインスタンス」なので、Sidekiqがキューを貯めることは ほとんどない。「再試行」カウントはせいぜいひと桁で収まるはずで、 仮にそうでなければ何かしらの障害である可能性が高い。
そこで、監視の要件を以下の様に定義する。

  • 「再試行」の件数が5を超えたらWARNING。
  • 「再試行」の件数が10を超えたらCRITICAL。
  • そもそも件数が取得できなかったりしたらUNKNOWN。
  • 上記以外のケースは、OK。

Sidekiqの「再試行」数を取得する

シェルから実行するなら、Mastodonがインストールされたディレクトリで、以下実行。

1
bundle exec rails runner 'puts Sidekiq::Stats.new.retry_size'

オーバーヘッドを嫌い、Ruby内で完結させるなら、以下の通り。

1
2
require 'sidekiq/api'
Sidekiq::Stats.new.retry_size

個人的にはどっちでもいいと思うが、状況に合わせて。

監視プラグインの実装

では実装していこう。

こいつに、/usr/local/bin/sensu-sidekiq等の名前をつけて保存。
工夫も何もないベタ書きのコードで恐縮だがw、サンプルゆえ勘弁して欲しい。 ちなみに、おれが普段使ってるのはこちら。 (他のインクルードファイルがいくつかないと動かないので、サンプルとしては不適切と思った)

https://github.com/pooza/ginseng/blob/master/bin/sensu/check/sensu-sidekiq.rb

今回はサンプルゆえ、件数しきい値のパラメータをハードコードしているが(手を抜いてすみません)、 実際には外部から設定できる様にするのが普通と思う。
この例ではそうでもないが、セキュリティに関わる設定項目(はっきり 言うと、要するにパスワードとか)は、コマンドラインから設定できるべきではないと考える。詳しくは後述。

サーバ側でのcheckの登録

Sensuサーバを設置したホストで、 /usr/local/etc/sensu/conf.d/checks/sidekiq.json に以下設置。

handlers は、handler(通知プラグイン)の配列。
subscribers は、対象となるsubscription。

保存後に、sudo service sensu_server restart

被監視側での登録

Sensuクライアントを設置した被監視側のホストでは、 /usr/local/etc/sensu/conf.d/client.json に以下の様な 設定を行う。

IPアドレスもホスト名も実在のものだけど、設定の内容は実際のものとは違うのであしからず。
ここまで手順通りに行っていれば、クライアント側にclient.jsonは設置済みと思う。 client.jsonのsubscriptionsに設定した名前と、サーバ側でsidekiq.jsonに設定した名前を一致させる。

保存後に、sudo service sensu_client restart。(FreeBSD/Ubuntu共)

checkにパラメータを渡すことも可能だが…

サーバ側のsidekiq.jsonで、commandで指定する文字列に /usr/local/bin/sensu-sidekiq -w:::waring|5::: -c :::critical|10::: 等と指定し、クライアント側のclient.jsonで

などと属性を追加する(13,14行目に注目)と、criticalに20、warningに10というパラメータをcheckの コマンドラインに渡すことが可能な様だ。(実際に試した)
省略した場合は、|で区切った右側がデフォルト値(criticalが10、warningが5)となる。

一見、checkの振る舞いはこの方法で制御できるように見えるが。
個人的に、この方法はごく限定的な用途に留めるべきと考える。特にパスワード等、セキュリティに関わる パラメータを指定する方法としては不適切だ。何故ならこれらの情報は、RabbitMQを通じてサーバとの間で 平文でやりとりされる可能性がなくもないから。
今の時点でRabbitMQが暗号化されていないのは、確かにおれの自業自得だけど、たとえ経路が暗号化されていても、 これらの情報が不用意に送信される状況は望ましくない。

被監視側のローカルにYAMLかJSONで設定ファイルを置き、設定関連がローカルで完結する実装にするべきと考える。
実際の運用では、client.jsonの書き換えはChef等で行うことが推奨される。同様に checkがローカルで参照する設定ファイルも、Chefに管理させればよいと思う。(2017/10/13追記)

次回

今回は、コミュニティのmailerプラグインを通知に使用したが、独自の通知プラグイン(handler)の実装について 考えていきたい。
まぁ実際には、メール以外の通知が必要な状況ってあんまり思い浮かばない気もするんだが、この様な実装経験も 引き出しになるので。
次回最終回、よろしく。