designetwork

ネットワークを軸としたIT技術メモ

Praeco (ElastAlert GUI)でElasticsearchログアラートする

f:id:daichi703n:20190324171403p:plain

Elasticsearchのデータを元にアラート通知できるElastAlertは、X-Pack(Watcher Alert)を導入しない環境において、ログ監視を実装する有用な方法として広く使用されている。

しかし、ElastAlertはコマンド・設定ファイル(YAML)ベースでの動作のみをサポートしており、アラートルール編集・抑止等は(CIなどでの操作は可能だが)サーバ操作が必要となる。アラートを受け取りたいのはKibanaのみを操作する一般ユーザであるケースが多く、ElastAlertのルール運用はやや面倒だ。ElastAlertをKibanaのタブとして組み込める ElastAlert Kibana Pluginはあるが、シンプルなアラート作成のみが可能...

そこで、ElastAlertの機能一式をサポートしているPraecoを使用する。

Praecoでは、ElastAlert Kibana Pluginの開発元のElastAlert Serverを使用している。

Note:
Praecoは2019/3/23時点で Beta 4 - "Italica" とベータリリースとなっています。また、ElastAlert, ElastAlert Server, Praecoと、三者が関連するため、開発・アップデートサイクルにやや不安があります。採用の際は注意して検討ください。

Open Distro for Elasticsearch

AWSがOpen Distro for Elasticsearchをリリースし、X-Pack相当の機能がオープンソースで公開されるようになったが、Elasticでは以下のようなニュースを出していたり、賛否両論あるため様子見している。

Praecoをインストールする

今回はDocker-ComposeでPraecoを起動する。Dockerを使用しない場合はnpmで起動する方法がオフィシャルで案内されている。

Praecoダウンロード

[root@h-cent-doc01 docker-compose]# git clone https://github.com/ServerCentral/praeco
Cloning into 'praeco'...
remote: Enumerating objects: 52, done.
...
Resolving deltas: 100% (1567/1567), done.

[root@h-cent-doc01 docker-compose]# cd praeco/

通知先設定

Praeco起動前にElastAlertの通知先を設定する。今回はSlackに通知する。

vi rules/BaseRule.config

slack_webhook_url: 'https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX'
smtp_host: ''
smtp_port: 25
slack_emoji_override: ':postal_horn:'

Slack通知のリンク先はpublic/praeco.config.jsonで設定されている。

vi public/praeco.config.json

{
  "appUrl": "http://localhost:8080",
  "errorLoggerUrl": "",
  "hidePreconfiguredFields": []
}

Praeco起動

[root@h-cent-doc01 praeco]# export PRAECO_ELASTICSEARCH=192.168.1.76

[root@h-cent-doc01 praeco]# docker-compose up
Creating network "praeco_default" with the default driver
Pulling webapp (servercentral/praeco:latest)...
latest: Pulling from servercentral/praeco
22dbe790f715: Pull complete
...
Digest: sha256:89f4c18f3e336ec216e9eed3e0a21211ad4d7205dd0ea1d4c5332de6911c4291
Status: Downloaded newer image for servercentral/praeco:latest
Pulling elastalert (servercentral/elastalert:latest)...
latest: Pulling from servercentral/elastalert
8e402f1a9c57: Pull complete
...
Digest: sha256:707e813613f66ba9628801ed0cd847e0e374d613aae893e59d821d64360be370
Status: Downloaded newer image for servercentral/elastalert:latest
Creating praeco_webapp_1 ...
Creating praeco_elastalert_1 ...
Creating praeco_webapp_1
Creating praeco_webapp_1 ... done
Attaching to praeco_elastalert_1, praeco_webapp_1
elastalert_1  |
elastalert_1  | > @bitsensor/elastalert@0.0.14 start /opt/elastalert-server
elastalert_1  | > sh ./scripts/start.sh
elastalert_1  |
elastalert_1  | 05:47:03.951Z  INFO elastalert-server: Config:  No config.dev.json file was found in /opt/elastalert-server/config/config.dev.json.
elastalert_1  | 05:47:03.954Z  INFO elastalert-server: Config:  Proceeding to look for normal config file.
elastalert_1  | 05:47:03.960Z  INFO elastalert-server: Config:  A config file was found in /opt/elastalert-server/config/config.json. Using that config.
elastalert_1  | 05:47:03.991Z  INFO elastalert-server: Router:  Listening for GET request on /.
...
elastalert_1  | 05:47:03.996Z  INFO elastalert-server: Router:  Listening for GET request on /config.
elastalert_1  | 05:47:04.001Z  INFO elastalert-server: ProcessController:  Starting ElastAlert
elastalert_1  | 05:47:04.001Z  INFO elastalert-server: ProcessController:  Creating index
elastalert_1  | 05:47:09.697Z  INFO elastalert-server:
elastalert_1  |     ProcessController:  Elastic Version:6
elastalert_1  |     Mapping used for string:{'type': 'keyword'}
elastalert_1  |     New index praeco_elastalert_status created
elastalert_1  |     Done!
elastalert_1  |
elastalert_1  | 05:47:09.697Z  INFO elastalert-server: ProcessController:  Index create exited with code 0
elastalert_1  | 05:47:09.697Z  INFO elastalert-server: ProcessController:  Starting elastalert with arguments [none]
elastalert_1  | 05:47:09.704Z  INFO elastalert-server: ProcessController:  Started Elastalert (PID: 47)
elastalert_1  | 05:47:09.706Z  INFO elastalert-server: Server:  Server listening on port 3030
elastalert_1  | 05:47:09.707Z  INFO elastalert-server: Server:  Websocket listening on port 3333
elastalert_1  | 05:47:09.709Z  INFO elastalert-server: Server:  Server started

Praecoが起動すると、指定したElasticserchにElastAlert用のindexが作成される。prefixはpraecoとして作成される。

GET _cat/indices?v&s=index

health status index                            uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   .kibana_1                        XppKrqwlQ0WCJuD_ZA75uA   1   0        345            0    286.8kb        286.8kb
yellow open   elastiflow-3.4.1-2019.03.22      QKhlIUdCTpmeqDox_nvjkw   3   1      28050            0     10.6mb         10.6mb
yellow open   elastiflow-3.4.1-2019.03.23      B2f_KpS0RxCGYHl7UbhJuQ   3   1      22340            0      8.6mb          8.6mb
green  open   praeco_elastalert_status         clr-u5uKSLqZEsrVVhNmhQ   1   0          0            0       230b           230b
green  open   praeco_elastalert_status_error   T4-SRO3SQxyrg5Xv4x2v8w   1   0          0            0       230b           230b
green  open   praeco_elastalert_status_past    xMMWoEtgSNG7YtAT7SVmGQ   1   0          0            0       230b           230b
green  open   praeco_elastalert_status_silence 2_2o8DcvSBOoU3UXRirmxg   1   0          0            0       230b           230b
green  open   praeco_elastalert_status_status  9qnRPpPpR4GjELOMdSMgzQ   1   0          0            0       230b           230b

Praecoでアラート設定する

Praeco Web GUIを使用して、Elasticsearchのログからアラート通知できるよう設定する。

Praeco Web GUIにアクセスする

http://<docker-host>:8080

f:id:daichi703n:20190323150800p:plain

ElastAlert APIには3030番ポートでアクセスできる。

http://<docker-host>:3030

f:id:daichi703n:20190323145052p:plain

アラートルールを作成する

Elasticsearchを参照しているため、Indexをプルダウンから選択する。Indexを選択したら、クエリを編集する。

f:id:daichi703n:20190323183914p:plain

クエリはリアルタイムにマッチ状況を確認しながら編集できる。

f:id:daichi703n:20190323184246p:plain

ISの項目でしきい値を調整できる。クエリが正しく設定されていると、しきい値のラインが表示される。分かりやすい。

f:id:daichi703n:20190323184727p:plain

Aggregationを有効化すると、アラート単位を編集できる。(ElastAlert標準機能の通り) ここでも、Indexの情報が引き継がれているため、プルダウンから選択することができる。

f:id:daichi703n:20190323184504p:plain

通知の設定も分かりやすい。

f:id:daichi703n:20190323185156p:plain

f:id:daichi703n:20190323185258p:plain

クエリのTestも可能。

f:id:daichi703n:20190323185327p:plain

ルールはDisable状態で作成されているため、Enableする。

f:id:daichi703n:20190323185523p:plain

Confirm.

f:id:daichi703n:20190323185600p:plain

アラートルールはrules配下に生成される。

[root@h-cent-doc01 praeco]# ls -la rules/
合計 12
drwxr-xr-x.  2 root root   52  3月 23 17:29 .
drwxr-xr-x. 10 root root 4096  3月 23 14:46 ..
-rw-r--r--.  1 root root  165  3月 23 17:14 BaseRule.config
-rw-r--r--.  1 root root 1014  3月 23 17:23 ElastiFlow.yaml

アラートと各種ログ

ログのしきい値超過を検知したら、設定した通り通知を受け取れる。

f:id:daichi703n:20190324124101p:plain

アラート履歴

f:id:daichi703n:20190324124437p:plain

クエリ履歴

f:id:daichi703n:20190324124557p:plain

YAML

f:id:daichi703n:20190324124701p:plain

Silence設定

アラートルールは一定期間Silence(無効化)することができる。5 minutes, 1 hour, 1 daysのボタンはエラーになって使えなかった...

f:id:daichi703n:20190324125320p:plain

無効化中はNoticeが表示される。

f:id:daichi703n:20190324130106p:plain

Silence解除(Unsilence) ※動作検証途中

現状、GUIではSilence設定を解除する項目がない。ElastAlert自身に該当するコマンド等がないため納得。記載の方法でSilence解除は可能だが、運用を考慮して要検討。

Silence設定はpraeco_elastalert_status_silenceのindexに保存されている。

GET praeco_elastalert_status_silence/_search
{
  "query": {
    "range" : {
      "until" : {
        "gt" : "now"
      }
    }
  }
}

---

{
  "hits" : {
    "total" : 1,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "praeco_elastalert_status_silence",
        "_type" : "silence",
        "_id" : "vcKqqWkBYbvXPV6quKBd",
        "_score" : 1.0,
        "_source" : {
          "rule_name" : "ElastiFlow._silence",
          "@timestamp" : "2019-03-23T08:28:51.674337Z",
          "exponent" : 0,
          "until" : "2019-03-23T10:28:51.674328Z"
        }
      }
    ]
  }
}

untilの内容を現在時刻で更新し、Praecoを再起動することで、Silenceを実質解除できる。ドキュメントIDを検索・使用するため運用に難あり...。また、ElastAlert単体で--silenceを使用した際は再起動不要でSilence解除できたが、Praecoとの組み合わせ時は再起動が必要だった。(詳細動作仕様は未調査)

POST praeco_elastalert_status_silence/silence/<id>/_update
{
  "script" : {
    "source": "DateFormat df = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'\");\ndf.setTimeZone(TimeZone.getTimeZone(\"UTC\"));\nDate date = new Date();\nctx._source.until = df.format(date);",
    "lang": "painless"
  }
}

---

{
  "_index" : "praeco_elastalert_status_silence",
  "_type" : "silence",
  "_id" : "vcKqqWkBYbvXPV6quKBd",
  "_version" : 2,
  "result" : "updated",
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 2,
  "_primary_term" : 1
}
GET praeco_elastalert_status_silence/_search

{
...
          "rule_name" : "ElastiFlow._silence",
          "@timestamp" : "2019-03-23T08:28:51.674337Z",
          "exponent" : 0,
          "until" : "2019-03-23T09:11:32.912Z"
...
}

(非推奨)rule_name指定でドキュメントを削除してもよいが、Silence履歴も消えてしまうため注意。

POST praeco_elastalert_status_silence/_delete_by_query
{
  "query":{
    "match": {
      "rule_name": "ElastiFlow._silence"
    }
  }
}

まとめ - Praeco (ElastAlert GUI)でElasticsearchログアラートする

Praecoを使用することでGUIベースでElastAlertを操作することができるようになった。ルール作成画面は非常に洗練されており、Kibana本家に搭載されているWatcher(X-Pack)よりも使い勝手が良いように思う。

引き続きテンプレート機能なども動作確認をし、効率的な運用方法を検討する。