Speee DEVELOPER BLOG

Speee開発陣による技術情報発信ブログです。 メディア開発・運用、スマートフォンアプリ開発、Webマーケティング、アドテクなどで培った技術ノウハウを発信していきます!

Zabbix環境でなるべく手軽にアプリケーション監視を行う方法

SpeeeのDX事業本部にて、業務委託のエンジニアとしてお手伝いしている id:ayemos です。今回はSpeeeにおいて実践したアプリケーション監視の話をします。

アプリケーション監視とは

メトリクスの収集による監視とは例えば、特定のEC2インスタンスのルートデータボリュームの空き容量が20%以下になったらアラートを上げる などの仕組みです。これにより、システムの異常を素早く検知、あるいは未然に防ぐことができます。

あるメトリクスがリリースなどの 変更に連動して変化 する可能性があり、そしてその 変化が異常と認められる ような場合、そのメトリクスは監視の対象とすべき有力な候補となります。例えば、ユーザーによるフォーム入力のバリデーション失敗数やスパムメール検知フィルタの検知数などのメトリクスはログやレコードには残らない場合があります。

この意味で、ホストやロードバランサ、nginxから発せられるメトリクスやログを収集することでシステムを安定して稼働させるのに必要なすべての監視を行えるとは限りません。このような場合は、アプリケーションの中、つまりサーバーやフロントエンドの実装から直接必要な情報を発信し、監視する必要があります。ここではこのような監視方法を アプリケーション監視 と呼びます。

ログとメトリクスによるアプリケーション監視

アプリケーション監視を行う方法としては、ログを残す、メトリクスを送出するという2つの方法が考えられます。例えばスパムフィルタの実行ごとにログ (例: {app: ‘spam_filter’, message_id: '1a2b3c', is_spam:true}) を残す、あるいはスパムフィルタの実行数 、成功数、スパムとして検知された数、されなかった数などをメトリクスとして収集するということになります。

ログを用いたアプリケーション監視は、メトリクスよりも多くの情報を扱えるという点で、数値のみを扱うメトリクスによる監視に対して優位です。例えば、ユーザー属性ごとのフォーム入力失敗率を監視するような場合にはメトリクスよりもログを利用したほうがいいでしょう。 1

一方でメトリクスを用いたアプリケーション監視は、メトリクス名と数値というシンプルなデータであることから、ログにおけるスキーマの混同や集計における困難などを避けることができます。またメトリクスごとにサンプリングを行えるという利点もあります。

このようにログとメトリクスはアプリケーション監視という目的においてそれぞれに長所と短所を持ちます。監視の目的に応じて適切な方法を選択するのがいいでしょう。

StatsDを利用したアプリケーション監視

運営しているWebサービスにおいて、複数のシステムが連携して動作する重要なトランザクションの動作を監視するために、それぞれのシステムからメトリクスを発信し、StatsDによる収集、Zabbixによる監視を行いました。以降ではその方法を紹介します。

StatsDとは

StatsDの中心となるのは以下に示すメトリクス収集プロトコルです。Counter(回数)やGauge(数値)、Histogramなどの数値をやり取りするためのプロトコルであり、StatsDのシステム、あるいはプロトコルに対応したシステムとの間で利用されます。

<metricname>:<value>|<type>

StatsDのシステム自体はサーバー(daemon)とバックエンドにより構成されます。各アプリケーションから サーバーに対して上記のプロトコルに従ってメトリクスを送信し、サーバーが集計、サンプリングを行いバックエンドへと受け流します。 バックエンドは基本的にメトリクスの永続化を行い、バックエンドの種類によっては同時に可視化やアラートを行います。

従ってStatsDを利用したメトリクス監視に必要な作業は以下のようになります。

  • (初回のみ) メトリクスを収集するStatsDサーバーを用意しバックエンドへ接続する
  • 各種アプリケーションからStatsDサーバーへ必要なメトリクスを送出する
  • バックエンドに必要な設定をする(監視)

StatsDと外部サービスのインテグレーション

StatsDサーバーへのメトリクスの送信には各種言語向けに実装されたサードパーティのクライアントライブラリを利用できます。クライアントが行う必要があるのは、上記のプロトコルに従ったメッセージを送信することだけなので、ライブラリに頼らずともメトリクスを送信するのは難しくはないでしょう。

また、 バックエンドとしては GraphiteZabbixの他、MySQL などを利用することができます。また未検証ですが、Prometheusstatsd_exporter を利用してPrometheusと連携し、Telegraf を経由して InfluxDBにメトリクスを格納することも可能です。

StatsDを利用したアプリケーション監視の構築方法

各サービスにはすでにZabbixによる監視の体制が敷かれていたため、Zabbixをバックエンドとして利用し、新たに作成するリソースを最小限にしました。構築した監視システムの全体図は以下のようになります。

f:id:ayemos:20191009131645p:plain

Step1. 各種アプリケーションから必要なメトリクスを送出する

Speeeにおけるケースでは、RailsとJetty(Java)で実装された2つのWebサーバーが連携する動作を扱う必要があったため、それぞれ Shopify/statsd-instrumenttim-group/java-statsd-client を利用して、以下のようにメトリクスを送信しました。

StatsD.increment('statsd.user-filter.filter-request')

result = request_user_filter(user)  # Jettyサーバーへのリクエスト

if result[:success]
  StatsD.increment('statsd.user-filter.filter-success')
  …
else
  StatsD.increment('statsd.user-filter.filter-fail')
  …
end
private static final StatsDClient statsd = new NonBlockingStatsDClient("statsd.user-filter", STATSD_HOST, STATSD_PORT);

...
statsd.incrementCounter("filter-start");
            
FilterResult filterResult = run_filiter(user)
if (filterResult.isOk()) {
    statsd.incrementCounter("filter-ok");
    …
} else {
    statsd.incrementCounter("filter-ng");   
    …
}

これにより、特定のフィルタ処理のリクエスト数、処理開始数、フィルタ通過数、フィルタ非通過数がサーバーに送信されます。収集/監視するメトリクスの選定は、リクエストしたフィルタが確かに処理開始されているか、処理開始数とフィルタ通過数/非通過数の合計が等しいかどうか、フィルタ通過率が異常値を出していないかどうかを監視するという意図に基づいています。

Step2. メトリクスを収集するサーバーを用意する

StatsDサーバーの詳細なインストール方法の説明は省略しますが、Zabbixへの接続を行う際は parkerd/statsd-zabbix-backend など、サードパーティのバックエンドの設定が必要です。各種アプリケーションからUDP通信可能な場所に用意します。

Step3. アラートを設定する

Zabbixは通常、Zabbixから監視対象ホストへデータを要求する、いわゆるPull型の方法でデータを集めてきます。StatsDを用いた監視は逆にサーバーからメトリクスを送信する形になります。これはZabbix trapperというアイテムを利用することで実現できます。

f:id:ayemos:20191009122235j:plain
(設定例)

上記の statsd-zabbix-backend ではメトリクスの種類に応じてZabbixに届くメトリクス名が細かく異なる点に注意する必要があります。例えば Counting タイプのメトリクス(例: filter-success:1|c)を送出した際に最終的にZabbixにはflush区間中の合計値、平均値(/s)が届き、メトリクス名はそれぞれ filter-success[total] filter-success[avg] となります。

ここで設定したアイテムに対してトリガーを設定することで、アプリケーションから届いた数値とアラートを対応させることができます。

結果とまとめ

今回対象としたアプリケーションの特定の動作について、以前はRedashのクエリ定期実行機能を利用したレコード数ベースの監視をしていましたが、処理の開始数はレコードに残っていないなど不完全な点がありました。今回メトリクスベースの監視に切り替えたことで、監視に必要な情報を余すことなく収集し、アラートにつなげることができました。

また今回構築した仕組みは、異なるメトリクスやバックエンドに対して横展開、拡張が可能です。アプリケーションレイヤで得られる様々な数値を手軽に監視に繋げられる基盤を用意することで、サービスの正常な動作を必要に応じて可視化し、安定稼働に繋げられるようになることが今後の希望となります。


  1. ログの場合は {app: 'user_registration', user_segment_id: 1, success: true} のようなログを残し、ユーザーセグメントごとの集計などを行うことが出来ますが、同様のことをメトリクスで行う場合には、registration_success_segument_1 などのメトリクスをセグメントごとに定義する必要があり、これはセグメント数が大きい場合には不都合でしょう。