大規模インフラの監視システム その2

 こんにちは。グリーのmdoi(@m_doi)です。
 今回は、グリーの監視システムについて説明したいと思います。以前、こちらの記事にて、リソース監視システムの説明をさせて頂きましたが、死活監視やログ監視については語られなかったので、気になっていた方も多いと思います。ということで、今回は、グリーのインフラにおける死活監視やログ監視、アラート通知システムを紹介したいと思います。

何を使っているの?

 グリーでは、死活監視にNagiosを使用していました。監視システムの中では、かなり有名なソフトウェアですから、監視システムの構築に使用したことがある方も多いのではないでしょうか。プラグインも豊富に存在するので、様々な監視を行うことができます。死活監視は、このNagiosの機能をそのまま利用し、ログ監視は、Nagiosと独自に作成したエージェント及びログフィルタを連携させて行っていました。

 全体のシステムは、次のような構成です。


nagios

複数のNagiosを階層構造にして、
  • 一定の台数のサーバ群に対する死活監視と、そのサーバ群で動作するエージェントが送信する各種ログの受信を担当するDistributed Nagios
  • Distributed Nagios の監視結果の集約とアラート送信を担当するCentral Nagios

に分けて構成することで、大規模なサーバ群に対応していました。このようなNagiosの階層構成については、こちらで詳しく説明されています。

死活監視の流れ

 死活監視については、Nagiosの仕組みに任せています。Distributed Nagiosが、それぞれの担当する監視対象サーバ群に対して、

  • ssh
  • icmp

の死活監視を行っています。死活監視の結果は、そのままCentral Nagiosに通知されます。監視の結果が、CRITICALの場合は、そのままCentral Nagiosから、所定の宛先にアラートメールを送信します。

ログ監視の流れ

 ここでいうログ監視とは、各サーバで動作するエージェントによって収集されるPHPのエラーログや、ApacheやMySQL等のサーバプログラムのエラーログ、ロードアベレージやディスク使用量といった値を収集し、設定したルール(ログパターンや閾値)と比較してアラートレベルを決定し、アラート通知をするという処理になります。
 全てのサーバには、構築時にエージェントがインストールされます。エージェントは、NSCAクライアントも兼ねており、Nagiosに対応した形式で、上記の情報を Distributed Nagios に送ります。エージェントから送られてきた情報は、Distributed Nagios から、そのまま Central Nagios まで通知されます。Central Nagios では、通知された情報を、すぐにアラートとして送るのではなく、一旦保存します。
 Central Nagios では、定期的に保存したログ監視情報をフィルタリングするプログラムが動作しています。このフィルタによって、そのログのアラートレベルが決定されます。
 フィルタのルールには、

  • このパターンにマッチする php.log は CRITICAL にする
  • このパターンにマッチする Apache のエラーログは WARNING にする
  • ディスク使用量が、この値を超えていたら CRITICAL にする

などのルールが記載されており、ルールに従ってアラートレベルが決定されます。そして、決定したアラートレベルに基づいて、対応する宛先にアラートメールが送信されます。
 以上が、ログ監視の流れになります。

実は・・・

 と、ここまで説明してきたのですが、実は、上記のシステムはもう使用していません。何故なら、様々な問題を抱えて、大規模化に耐えられず破綻しかけていたからです。

問題その1 - 拡張性

 構成図を見ていただければ一目瞭然なのですが、このシステムはCentral Nagiosがボトルネックになります。監視対象の増加に対して、Distributed Nagiosは増やすことができますが、Central Nagiosは増やすことができません。事実、大量のサーバでアラートが発生した場合に、Central Nagiosが処理しきれなくなるケースが多発し、大変な苦労を強いられました。単純な対応として、アラートメールの集約を諦めて、Central/Distributed Nagiosのセットを複数用意するという手段が考えられますが、同内容のアラートメールの流量が増えてしまうので、最後の手段と考えていました。

問題その2 - 耐障害性

 この構成では、

  • Distributed Nagiosがダウンすると、その担当範囲のサーバの監視が行えなくなる
  • Central Nagiosがダウンすると、すべてのサーバの監視が行えなくなる

という状況が発生します。後者の発生はどうしても防ぎたかったため、後々、Central Nagiosに関しては、VRRPのような冗長化技術を用いて、複数のホストでActive/Standby構成を取ることにより、冗長性を確保しました。しかしながら、全Nagiosでそのような対応を取るには、あまりもNagiosは増えすぎていました。

問題その3 - 煩雑な設定

 Nagiosでは、監視対象のサーバリストを設定ファイルに持ちます。

  • Distributed Nagiosは、自分のサービス投入中の担当範囲のサーバをリストとして持つ
  • Central Nagiosは、サービス投入中のすべてのサーバをリストとして持つ

という、設定を行う必要があるため、サーバをサービスイン/アウトするたびに、各Nagiosの設定を生成し、設定を配布してNagiosを再起動する必要があります。この作業は、ツールを開発して自動で行っていましたが、サーバ数の増大に伴い、この手順が大きなコストになってきていました。
 また、Nagiosの設定ファイルを見たことがある方ならわかると思いますが、Nagiosの設定ファイルは大量のサーバを見やすく記述できるような形式になっておらず、人の目で見てわかる状態ではありませんでした。
 さらに、Distributed Nagiosが特定のサーバ群を監視するということは、監視される側のサーバは、どのDistributed Nagios にエージェントのメッセージを投げるべきか知っておく必要があるということになります。そこを間違えると、監視されるべきものが監視されないという事態を招き、大きな問題になります。

次世代監視システム AWACS

 上記以外にも、機能的に、あるいは運用上、様々な問題を抱えていました。特に大量のアラートを処理できないという問題から、上記システムは破綻しかかっていたので、これらの問題を解決しつつ、さらにより良いものを目指して、独自の監視システムを作ることにしました。
 新しい物を作る上で名前は重要です。というわけで、名前から先に決めました。AWACSと言います。早期警戒管制機(Airborne Warning And Control System)ではなく、Alert Warning And Control Systemです。
 ちなみに、本物のAWACSの役割は以下の通りです。

  • 索敵
  • 情報整理及び分析
  • 情報共有
  • 指揮管制

 実は監視システムも、やることは同じなのです。この役割を踏まえた上で、次世代監視システムAWACSの構成について紹介します。

AWACSの構成

メッセージキュー

 構成の説明に先立って、メッセージキューについて説明します。これまで述べてきたように、監視システムは、大量のメッセージを処理できる必要があります。今回、より堅牢でスケーラブルなシステムを目指して、監視システムのメッセージングインフラにメッセージキューを採用しました。
 今回使用するメッセージキューのミドルウェア及びプロトコルの選定にあたって、

  • PubSubなメッセージ配送が可能である
  • 大量のメッセージも確実に処理できる安定性
  • 多くの言語で利用できるライブラリが入手可能である

という要件を設けました。色々と評価をした結果、この要件を満たしているミドルウェアとしてActiveMQ、メッセージングのプロトコルとしてSTOMP、を使用することにしました。
PubSubとかSTOMPとか何よ、と思われるかもしれませんが、ざっくり次のようなメッセージ配送モデルが利用可能と考えて下さい。

Queue

 Publisherが生成したメッセージは、Queueに接続しているSubscriberに、順に振り分けられる。

Topic

 Publisherが生成したメッセージは、Topicに接続しているSubscriberすべてに振り分けられる。

 これらの配送モデルが、システムにおいて重要な役割を果たしています。
今回のシステムでは、上記の配送モデルを利用して

  • Queue -> 負荷分散装置
  • Topic -> メッセージのレプリケーション

としての働きを行うことを意図して使用しています。では、監視システムの構成について説明します。

Passive監視

 Passive監視とは、前述のシステムにおけるログ監視にあたるものです。Passive監視は、論理的に次のような構成となります。

Agent

 Agentは、これまでのシステムと同じで、エラーログの回収や、リソースの値の取得、サーバプログラムの監視等を行います。その結果を、メッセージキューのQueueに送信します。

Filter

 Filterは、送信されてきたメッセージのアラートレベルを、ルールに基づいて決定し、アラートレベルや、マッチしたルールの情報、発生元サーバの情報を付与したメッセージをTopicに送信します。この時、アラートレベルに応じたTopicを用意しておき、メッセージを送信します。
 また、弊社ではサーバ管理システムで、サービスの投入状態を管理しているので、この時点で、サーバ管理システムと連携して、サービスに投入しているホストからのメッセージのみ取り扱うように、メッセージを取捨選択します。

Summarizer

 Summarizerは、高優先度のアラート(緊急度の高いアラート)を、適切に蓄積、集約してQueueに送信します。ここで受け取るアラートには、

  • アラートレベル
  • マッチしたルールのユニークID
  • 発生元サーバの詳細
  • アラート対象のログ本文

等の情報が含まれるので、それらの情報に基づいた集約を行います。
 ただ、ここに到着するアラートは緊急度が高いので、初回のメッセージは、集約を待たず即時送信し、同じルールにマッチしている2通目以降を一定時間蓄積して集約する、などの工夫をしています。
 また、この時点でメールの宛先は決定されていませんので、上記の情報と、設定したルールに基づいて宛先を決定します。(宛先が決まっていないと、集約できません)

reporting system

reporting systemは、優先度の低いアラートを受信して蓄積します。これらのアラートは優先度が低いので、一定時間に一度、集計してまとめたものを所定の宛先にメールで送信するような仕組みになっています。

Critical Sender

Critical Senderは、Summarizerによって宛先が決定され、集約されたアラートを、ひたすらメールで送信します。

 以上が、各要素の動作になります。実は、一点説明不足なところがあります。それはAgentです。Agentは、上述のFilterとは別に独自のFilter(Local Filterと呼んでいます。)を持っており、自身でアラートの優先度を判断することができるようになっています。ここで高優先度と判断されたものは、そのまま上述のフローでQueueに投入されますが、低優先度と判断されたアラートはローカルに書き出すのみで、低優先度のアラートがあるというメッセージだけQueueに投入します。こうすることにより、大量の低優先度アラート発生による、高優先度アラートの処理の遅延を防ぐことができます。ここで、疑問が残ると思います。

  • 低優先度と判断されて、ローカルに書き出したログは誰が回収するのか?
  • Local Filterのルール設定はどうやってメンテするのか?

この部分の処理については、Active監視で解決されるので、後回しにします。

 上記の論理構成を、以下のような物理構成で構築しました。角丸の四角がサーバになります。

Agent

 各Agentは複数のMQ Serverの中から、ランダムに選択してメッセージの送信を行います。Agentからのメッセージは、MQのQueue(raw alert)のうち、どれかに存在することになります。

Filter

 各Filterは、すべてのMQ ServerのQueue(raw alert)をsubscribeしておきます。Agentからのメッセージは、MQ ServerのQueue(raw alert)のいずれかに蓄積されているので、Filter ServerのFilterのどれかに取り出されます。また、Queueなので、Filter群に対して、適度に分散されてメッセージが配送されます。これによりQueueを使用したFilterの負荷分散を行っています。Filterはそれぞれ処理を行い結果を、MQ ServerのTopic(filtered alert)のいずれかに送信します。

Summarizer

 Summarizerは、すべてのMQ ServerのTopic(filtered alert)をsubscribeしておきます。但し、これはTopicなので、すべてのFilter ServerのSummarizerがSubscribeしてしまうと、同じメッセージが、各Summarizerに処理されてしまい重複した集約アラートが生成されてしまいます。それを避けるため、どれか一つのSummarizerがActiveとなり、その他のSummarizerがStandbyとなるような仕組みを備えました。詳細は割愛しますが、各SummarizerがMQのTopicを介して通信を行い、単一のMasterを選出するような仕組みを実装しています。これにより、単一のSummarizerのみが、集約処理を行い、ActiveなSummarizerのプロセスやサーバが落ちたとしても、処理は引き継がれます。Summarizerの集約結果は、Queue(summarized alert)に蓄積されます。

Critical Sender

 各Critical Senderは、すべてのMQ ServerのQueue(summarized alert)をsubscribeしておきます。Queueなので、各Critical Senderに適切に分散されて集約メッセージが処理されます。つまり、ここでもQueueを使用して負荷分散を行っているわけです。

 このように、要所要所のメッセージ配送にQueueとTopicを使い分けることで、負荷分散と冗長化を実現できるように設計をしてみました。
 仮に上記の構成で、filter serverを追加したくなった場合は、次のような構成になります。

 どうでしょうか?MQがすべて停止したり、同じ役割のプロセスがすべて停止しなければ、処理が継続される様に、相当しぶとく生き残る構成になっていると思いませんか?また、MQ Serverや、Filter Serverのスケールアウトは、サーバを追加するだけで実現でき、非常に運用しやすい構成になっています。唯一ボトルネックとなりそうなのは、ActiveなSummarizerの処理の限界ですが、Summarizerに到着するメッセージは、高優先度のアラートであり流量はそれほど多くありません、従って、通常、問題になることはありません。
 これで、旧システムで問題となっていた、拡張性、耐障害性については解決できました。また、独自開発の強みを活かして、自社サーバ管理システムとの連携も容易にできるため、監視対象の設定の煩雑さも解消できました。また、filter後のアラートをTopicに蓄積することで、アラートを別のことに使用したい場合も、このTopicをsubscribeすれば、いつでもアラートを取得できるようになりました。これにより

  • アラートをトリガに何かを走らせる
  • アラートをメール以外の手段で通知する
    • skypeやircのbotにアラートから通知
    • WebSocketを介して接続したWebブラウザに対して、アラートをpush通知

などの拡張が行い易くなり、柔軟性の高いシステムになりました。
 次に、Active監視ですが、分量が多くなってしまったので、次回に回したいと思います。

まとめ

 今回は

  • Nagiosの分散構成と、独自のアラートフィルタを使用した監視の仕組み
  • 上記の構成の問題点と、それを解決する新しい監視システムAWACS(Passive監視のみ)
    • PubSubなメッセージキューを活用した、冗長性の確保と負荷分散

について、紹介しました。現在、弊社の監視は、このAWACSを用いて行なっております。
今回、説明できなかった、Active監視については、次回説明したいと思います。