Edited at

Elasticsearchを活用したサービス監視ですこしだけ幸せになる

More than 1 year has passed since last update.

エイチームライフスタイルアドベントカレンダー2017、2日目です。

本日は 株式会社エイチームライフスタイル のインフラエンジニア @ihsiek が担当します。

突然ですが皆さん、アクセスログを可視化していますか?

Elasticsearch+Kibanaでやってるよーというところもそこそこありますよね?

そこで今回は、Elasticsearch上のデータを解析してアラートを投げる ElastAlertというツールの導入手順と社内での運用方法について紹介していきます。

ちなみにこの仕組みを導入したことで、アラート本文を見るだけでサーバで起きている事象を瞬時に判断し、障害を切り分けられるようになりました。 :tada:

サーバのアクセスログどころか、Kibanaすら見なくてよくなった&経験の浅いメンバーがアラート対応に入れるようになったのが地味に幸せです。 :relaxed:


ElastAlertとは?

Elasticsearch上のIndexに対して、運用者が指定したある条件(クエリや監視ルールタイプ)に応じた通知を可能にするYelp社が開発したOSSツールです。

弊社では、このツールを使い、Elasticsearchに取り込んだアクセスログから、レスポンス遅延HTTPステータス500番台のエラーを検知する仕組みを導入しました。

当初は、指定した期間内にクエリとマッチするイベントがN件あったらアラート送信するFrequencyという監視ルールタイプを使用していました。

これではサービスごとのリクエスト数に応じて閾値を変えたりと運用が大変なので、現在は、指定した期間内にクエリとマッチするイベントがN%あったらアラートを送信するPercentage Matchを採用しています。


導入


ElastAlert


インストール

早速、ElastAlertをインストールします。

Elasticsearchは、運用しているバージョンにあわせてインストールしてください。

弊社ではElasticsearch 5系を運用しているため、以下のコマンドでインストールします。

$ sudo pip install "elasticsearch>=5.0.0" elastalert

$ elastalert -h
usage: elastalert [-h] [--config CONFIG] [--debug] [--rule RULE]
...

インストールはこれで完了です。


共通設定

次にグローバルコンフィグファイルを作成します。

--configオプションに定義するだけなので、どこに作成してもいいのですが、ほかの運用メンバーが迷わないように/etc/elastalert/config.ymlに作成します。


設定ファイル


/etc/elastalert/config.yml

# The Elasticsearch hostname for metadata writeback

# Note that every rule can have its own Elasticsearch host
es_host: elasticsearch.example.com

# The Elasticsearch port
es_port: 443

# Connect with TLS to Elasticsearch
use_ssl: True

# This is the folder that contains the rule yaml files
# Any .yaml file will be loaded as a rule
rules_folder: /var/lib/elastalert/rules

# How often ElastAlert will query Elasticsearch
# The unit can be anything from weeks to seconds
run_every:
minutes: 2

# ElastAlert will buffer results from the most recent
# period of time, in case some log sources are not in real time
buffer_time:
minutes: 5

# The index on es_host which is used for metadata storage
# This can be a unmapped index, but it is recommended that you run
# elastalert-create-index to set a mapping
writeback_index: elastalert_status

# If an alert fails for some reason, ElastAlert will retry
# sending the alert until this time period has elapsed
alert_time_limit:
days: 1



設定ファイル(解説)

キー
内容

es_host
接続するElasticsearchのエンドポイントを指定します。

es_port
接続するElasticsearchのポート番号を指定します。

use_ssl
SSL接続を選択する場合はTrueを指定します。

rules_folder
監視ルールを格納するパスを指定します。
ElastAlert実行時、このパスに格納されたルールが再帰的に評価されます。

run_every
クエリをElasticsearchに投げる間隔(監視の間隔)を指定します。

buffer_time
ログをバッファリングする期間を指定します。
1分間隔の監視で過去5分間に異常値が10回出現したらアラート送信するというケースでの利用が想定されます。

writeback_index
ElastAlertのシステム用Index名を指定します。

alert_time_limit
アラート送信に失敗した場合に再送をリトライする期間を指定します。


ElastAlertのシステム用Indexの作成

このままではElasticsearch上にElastAlertの監視ログなどを書き出せないため、以下のコマンドを実行してElasticsearchにIndexを作成します。

$ elastalert-create-index --config /etc/elastalert/config.yml


監視ルールファイル

先ほどグローバルコンフィグファイルで指定したパスに監視ルールファイルを格納していきます。

今回は監視対象Indexの更新状況・レスポンス遅延・HTTPステータスコードのエラー(以降、HTTP5XXエラー)を検知する3つのルールを設置します。

監視ルールのファイル自体は同一階層にまとめてもいいのですが、弊社では監視ルールごとに階層を分けてその下にサービス名ごとのファイルを置くことで管理しています。

$ sudo mkdir -p /var/lib/elastalert/rules/{logforward,response,status-5XX}

以下に監視ルールのサンプルを掲載しますが、詳細は公式のドキュメントを参考にしてください。


監視ルール - 監視対象Indexの更新状況

ElasticsearchのIndexが更新されなくなると根本が揺らぐため、Indexが更新されているかをチェックします。


設定ファイル


/var/lib/elastalert/rules/logforward/sample.yml

es_host: elasticsearch1.example.com

es_port: 443
use_ssl: True

index: access-log-*

name: No access log on hogehoge

type: flatline
threshold: 1
timeframe:
hours: 1

timestamp_field: "time"

use_count_query: True
doc_type: access-log

alert:
- "email"
email:
- "hoge@example.com"



設定ファイル(解説)

キー
必須
説明

es_host

グローバルコンフィグを上書きする場合に指定します。

es_port

use_ssl

index

監視対象のElasticsearchのIndexを指定します。

name

全監視ルールに対して一意な名前を指定します。

type

監視ルールタイプを指定します。
flatlineはイベント数thresholdを下回った場合にアラートを送信します。

threshold

アラートのトリガとなる閾値(イベント数の下限)を指定します。
※flatlineを指定した場合、必須。

timeframe

閾値監視の期間を指定します。
1時間を指定した場合、1時間以内にイベント数がthresholdを下回った場合にアラートを送信します。

timestamp_field

timestampを保持するフィールドを指定します。

use_count_query

Count APIを使用するかどうかを指定します。
Trueを指定するとElasticsearchのCount APIを使用します。

doc_type

Indexのdoc_typeを指定します。
※use_count_queryを指定した場合、必須。

alert

アラートの通知方法を指定します。

email

アラートの通知先メールアドレスを指定します。
※alertにemailを指定した場合、必須。


監視ルール - レスポンス遅延


設定ファイル


/var/lib/elastalert/rules/response/sample.yml

es_host: elasticsearch1.example.com

es_port: 443
use_ssl: True

index: access-log-*
doc_type: access-log

name: Response was delayed on hogehoge

type: percentage_match

match_bucket_filter:
- query_string:
query: "response: >3000000 AND -path:(csv AND sitemap)"

max_percentage: 10

top_count_keys:
- "host"
- "agent"
- "path"

timeframe:
minutes: 5

timestamp_field: "time"

use_kibana4_dashboard: "https://kibana.example.com/#/discover/alert-response-hoge"

alert:
- "email"
email:
- "hoge@example.com"



設定ファイル(解説)

キー
必須
説明

match_bucket_filter

アラートとして引っ掛けたい条件を定義します。
query_stringにはKibanaのクエリと同じものが使えるため、そちらであらかじめ想定通り動作するか確認することをお勧めします。
※percentage_matchの場合、必須。

max_percentage

match_bucket_filterにマッチするイベントの出現比率の上限を指定します。
総イベント数に対する比率がこの閾値を上回った場合にアラートを送信します。
※percentage_matchの場合、min_percentage, max_percentagのいずれかが必須。

top_count_keys

Indexの任意のキーを指定します。
match_bucket_filterにマッチするイベントのうち、top_count_keysに指定したキーをそれぞれ出現率の多い順に表示します。

use_kibana4_dashboard

アラートの本文にKibanaへのリンクを挿入します。

flatlineと重複する説明は省略しています。


アラート本文

以下のようなアラートが送られるようになります。

Response was delayed on hoge.

Percentage violation, value: 10.5504587156 (min: None max : 10)

host.keyword:
xxx.yyy.zzz.aaa: 31
xxx.yyy.zzz.bbb: 13
xxx.yyy.zzz.ccc: 1
xxx.yyy.zzz.ddd: 1

agent.keyword:
Mozilla/5.0 (iPhone; CPU iPhone OS 10_0 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Mobile/14A346 Safari/602.1: 31
Mozilla/5.0 (iPhone; CPU iPhone OS 11_1_2 like Mac OS X) AppleWebKit/604.3.5 (KHTML, like Gecko) Mobile/15B202 YJApp-IOS jp.co.yahoo.ipn.appli/4.8.9: 13
Googlebot-Image/1.0: 1
Mozilla/5.0 (iPhone; CPU iPhone OS 11_1_2 like Mac OS X) AppleWebKit/604.3.5 (KHTML, like Gecko) Version/11.0 Mobile/15B202 Safari/604.1: 1

path.keyword:
/sample1.jpg: 2
/sample2.jpg: 2
/sample3.jpg: 2
/sample4.jpg: 2
/sample5.jpg: 2

kibana_link: https://kibana.example.com/#/discover/alert-response-hoge?_g=%28refreshInterval%3A%28display%3AOff%2Csection%3A0%2Cvalue%3A0%29%2Ctime%3A%28from%3A%272017-11-24T16%3A27%3A28.717549Z%27%2Cmode%3Aabsolute%2Cto%3A%272017-11-24T16%3A37%3A28.717549Z%27%29%29
num_hits: 436
num_matches: 1
percentage: 10.5504587156
time: 2017-11-24T16:32:28.717549Z


監視ルール - HTTP5XXエラー


/var/lib/elastalert/rules/status-5XX/sample.yml

es_host: elasticsearch1.example.com

es_port: 443
use_ssl: True

index: access-hoge-*
doc_type: access-log

name: HTTP status 5XX on hoge

type: percentage_match

match_bucket_filter:
- query_string:
query: "code: [500 TO 599]"

max_percentage: 1

top_count_keys:
- "host"
- "agent"
- "path"

timeframe:
minutes: 5

timestamp_field: "time"

use_kibana4_dashboard: "https://kibana.example.com/#/discover/alert-5xx-hoge"
alert:
- "email"
email:
- "hoge@example.com"



クエリ以外は、レスポンスの監視とほぼ同じなので説明は割愛します。


検証

監視ルールの準備が完了したので検証してみます。

検証にはelastalert-test-ruleを使用します。

$ elastalert-test-rule --config /etc/elastalert/config.yml

実際にアラートを飛ばしてみたいときは、--alertをつけて先ほどのコマンドを実行してください。

検証でエラーが出なければ、下準備は完了です。


運用

ここからは実際に本番環境で運用する際に行ったことと、本番運用を開始してトラブったことを少しだけ紹介していきます。


デーモン化

ElastAlert自体にデーモン化する機能がないため、Supervisorを使ってDaemon化してしまいます。


Supervisor


インストール

まずはSupervisorをインストールします。

$ sudo pip install supervisor


設定ファイルの作成

次に設定ファイルを用意します。

設定ファイルは、ElastAlertプロジェクトに用意されているサンプルも参考にしてみてください。


/etc/supervisord.conf

[unix_http_server]

file=/var/run/supervisor.sock

[supervisord]
logfile=/var/log/supervisord.log
logfile_maxbytes=1MB
logfile_backups=2
loglevel=warn
nodaemon=false
directory=%(here)s

[inet_http_server]
port = 127.0.0.1:9001

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///var/run/supervisor.sock

[program:elastalert]
command=elastalert --config /etc/elastalert/config.yml --verbose
process_name=elastalert
autorestart=true
startsecs=15
stopsignal=TERM
stopasgroup=true
killasgroup=true
redirect_stderr=false
stderr_logfile=/var/log/supervisord/elastalert_stderr.log
stderr_logfile_maxbytes=1MB
stderr_logfile_backups=5



Supervisorの起動

Supervisorの本体を起動します。

$ supervisord -c /etc/supervisord.conf
SupervisorからElastAlertをデーモンとして起動します。
$ supervisorctl start elastalert


supervisorのinitスクリプト設置

サーバーが再起動されても大丈夫なように、こちらの記事を参考にinitスクリプトを設置します。

$ sudo curl -o /etc/rc.d/init.d/supervisord https://raw.githubusercontent.com/Supervisor/initscripts/master/redhat-init-equeffelec

$ sudo chmod 755 /etc/rc.d/init.d/supervisord
$ sudo chkconfig --add supervisord

これでElastAlertのデーモン化が完了しました。

とりあえずの運用はここまででも大丈夫かと思います。


ElastAlert導入の注意点


ElasticsearchのCPUリソースに気を付けましょう

当初のElasticsearchの運用では、障害分析などのタイミングでログを見るために使うくらいしか想定していなかったため、Amazon Elasticsearch Service(ES)のt2.microを使っていました。

ElasticAlertを導入し毎分クエリが投げられるようになったことで、CPUリソースを食い潰す事象が発生したため、ESのスケールアップとクエリの実行間隔(run_every)の見直しをしています。

どの程度リソースを消費するのか見えにくいと思うので、導入初期はCPUリソースの監視をしっかり行ったほうがいいと思います。


top_count_keysのサマリが実は・・・。

監視期間の全数からサマリが作られてしまうため、「監視期間中、HTTPステータスコード5XXになった」イベントの上位5件を見ているつもりが、監視期間の全数に対する上位5件が表示されてしまい、まったく見当はずれな情報になっています。

なので、「監視期間中、HTTPステータスコード5XXになった」イベントのURIを確認したいという場合、監視ルールをカスタマイズする必要があります。

この監視ルールをカスタマイズしたスクリプトも用意していますが、今回の記事が相当なボリュームになってしまったので、次の機会に紹介します。

==2017/12/23 追記==

ElastAlertの監視ルールをカスタマイズする方法を別記事にまとめました。

サンプルコードも記載しているので、参考にしてみてください。

ElastAlertの監視ルールを拡張してすこしだけ幸せになる


参考


最後に

エイチームライフスタイルアドベントカレンダー2017の2日目、いかがでしたでしょうか。

明日は株式会社エイチームライフスタイルでフロントエンドからインフラまで幅広く担当している @maa0984 さんが ReactNative に関する記事を書いてくれるらしいので、お楽しみに。


株式会社エイチームライフスタイルでは、一緒に働けるチャレンジ精神旺盛な仲間を募集しています。興味を持たれた方はぜひエイチームグループ採用サイトを御覧ください。

http://www.a-tm.co.jp/recruit/