designetwork

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

Praeco + ElastAlert2.0 + ES7.x 構成の不具合対応方法

(2020/04/24 id:naoyukisano1 様からの情報を追記)

こちらの記事で紹介したPraecoだが、Elasticsearchのバージョンアップに伴いElastAlertもバージョンが上がり、2020/2月時点ではElastAlertのバグで正常に動作しない状態となっている。

designetwork.daichi703n.com

バグ対応して、通常利用可能状態にする。

GitHub Issueの情報を元にソースコード改修等をしていますが、私はElastAlert内部を完全に理解できているわけではありません。十分に試験したうえで採用を検討ください。

Quick Fix

git clone https://github.com/daichi703n/praeco
cd praeco
export PRAECO_ELASTICSEARCH=<Elasticsearch IP>
docker-compose up

fix/elastalert2.0.1-is_enabled ブランチで修正済み。(デフォルトブランチ設定済)

※プログラミングスキルが低いため、冗長な部分が多いですがご了承ください。

あるいは、修正版Docker Image daichi703n/elastalert:0.2.1-dev を使用ください。

バグ対応

バージョン情報

項目 バージョン Docker Image Image ID Digest 備考
Praeco 1.0.1 https://hub.docker.com/r/servercentral/praeco baf1041c984e fe6140159d86
ElastAlert Server 0.0.14 https://hub.docker.com/r/servercentral/elastalert ce3c9ff6adb8 7c42b9eefe24
ElastAlert 2.0.1 https://hub.docker.com/r/servercentral/elastalert ce3c9ff6adb8 7c42b9eefe24
Elasticsearch 7.5.1 docker.elastic.co/elasticsearch/elasticsearch:7.5.1 2bd69c322e98

バグ概要

私が直面した具体的なバグは以下の通り。

バグ項目 内容 GitHub Issue 備考
Import Praecoで作成している共通設定BaseRule.configがImportされない https://github.com/Yelp/elastalert/issues/2600
Test Run Praecoでルール作成時にTest Runしてもアラート発報されない(エラー発生) https://github.com/Yelp/elastalert/issues/2556
ルールDisable PraecoでルールをDisabledにしても無効化されない https://github.com/Yelp/elastalert/pull/1683 残存バグあり
ルール削除 Praecoでルールを削除しても残存する https://github.com/Yelp/elastalert/issues/2043
ルールRe-enable ElastAlert再起動後にDisabledからEnabledにしても有効化されない
Alert log アラートログがElasticsearchに記録されない表示されない https://github.com/ServerCentral/elastalert-server/pull/3

ソースコード差し替え

ElastAlert GitHubリポジトリから、修正が含まれている版のソースコードをダウンロードする。動作担保のために特定Commitのものにしているが、基本的にmasterブランチのものを取得するといいはず。

<Praecoリポジトリをクローンしたディレクトリで作業する>
mkdir -p data/elastalert/src
wget https://raw.githubusercontent.com/Yelp/elastalert/1334b611fdd7adf39991a1b0b11689568d612690/elastalert/elastalert.py -P data/elastalert/src/
wget https://raw.githubusercontent.com/Yelp/elastalert/1334b611fdd7adf39991a1b0b11689568d612690/elastalert/test_rule.py -P data/elastalert/src/

により、ソースを手元にコピーする。

ソースコード修正 elastalert.py

ダウンロードしたelastalert.pyソースコードを修正する。

  • 未定義変数修正(必須じゃないかも)

https://github.com/Yelp/elastalert/blob/1334b611fdd7adf39991a1b0b11689568d612690/elastalert/elastalert.py#L162-L163

        self.thread_data.num_hits = 0
        self.thread_data.num_dupes = 0
        self.thread_data.alerts_sent = 0  # 追記する
        self.scheduler = BackgroundScheduler()
  • 不要パッケージ除外

他更新により追加されている部分を一旦コメントアウトする。(関連モジュールは元のDockerImageのままのため依存パッケージ不足になる)

https://github.com/Yelp/elastalert/blob/1334b611fdd7adf39991a1b0b11689568d612690/elastalert/elastalert.py#L33

from .enhancements import DropMatchException
# from .kibana_discover import generate_kibana_discover_url  #コメントアウト
from .ruletypes import FlatlineRule

https://github.com/Yelp/elastalert/blob/1334b611fdd7adf39991a1b0b11689568d612690/elastalert/elastalert.py#L1506-L1509

            if kb_link:
                matches[0]['kibana_link'] = kb_link

        # コメントアウト
        # if rule.get('generate_kibana_discover_url'):
        #     kb_link = generate_kibana_discover_url(rule, matches[0])
        #     if kb_link:
        #         matches[0]['kibana_discover_url'] = kb_link

        # Enhancements were already run at match time if

修正後ソースコードマウント

docker-compose.yml にファイルのマウントを追加する。

  elastalert:
    ...
    volumes:
      ...
      - ./data/elastalert/src/elastalert.py:/opt/elastalert/elastalert/elastalert.py
      - ./data/elastalert/src/test_rule.py:/opt/elastalert/elastalert/test_rule.py

docker-compose up -d elastalert して更新を反映させる。

これにより、最低限の動作はできるようになる。私の環境では、Slack, Emailのみ動作確認済み。

Re-enable修正はこちら

こちらで各種バグ改修中。ElastAlertリポジトリはIssue・PR状況が煩雑なのでContributeは様子見中...
praeco/data/elastalert/src at fix/elastalert2.0.1-is_enabled · daichi703n/praeco · GitHub

Diff
Comparing 50c85e0e9655a8dd75ed60193451c53e963a6587...48fdb6be19d58e5cc9aee50cde812e790405033c · daichi703n/praeco · GitHub

バグ詳細

発生したエラーログなど

Importの不具合

/opt/elastalert $ python3 -m elastalert.test_rule --config ./config.yaml ./rules/infra.test.yaml --alert
Didn't get any results.
Traceback (most recent call last):
  File "/opt/elastalert/elastalert/loaders.py", line 461, in load_alerts
    alert_field = [create_alert(a, b) for a, b in alert_field]
  File "/opt/elastalert/elastalert/loaders.py", line 461, in <listcomp>
    alert_field = [create_alert(a, b) for a, b in alert_field]
  File "/opt/elastalert/elastalert/loaders.py", line 451, in create_alert
    raise EAException('Missing required option(s): %s' % (', '.join(missing_options)))
elastalert.util.EAException: Missing required option(s): slack_webhook_url
 
During handling of the above exception, another exception occurred:
 
Traceback (most recent call last):
  File "/usr/lib/python3.6/runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/opt/elastalert/elastalert/test_rule.py", line 449, in <module>
    main()
  File "/opt/elastalert/elastalert/test_rule.py", line 445, in main
    test_instance.run_rule_test()
  File "/opt/elastalert/elastalert/test_rule.py", line 437, in run_rule_test
    self.run_elastalert(rule_yaml, conf, args)
  File "/opt/elastalert/elastalert/test_rule.py", line 233, in run_elastalert
    conf['rules_loader'].load_modules(rule, load_modules_args)
  File "/opt/elastalert/elastalert/loaders.py", line 428, in load_modules
    rule['alert'] = self.load_alerts(rule, alert_field=rule['alert'])
  File "/opt/elastalert/elastalert/loaders.py", line 464, in load_alerts
    raise EAException('Error initiating alert %s: %s' % (rule['alert'], e)).with_traceback(sys.exc_info()[2])
  File "/opt/elastalert/elastalert/loaders.py", line 461, in load_alerts
    alert_field = [create_alert(a, b) for a, b in alert_field]
  File "/opt/elastalert/elastalert/loaders.py", line 461, in <listcomp>
    alert_field = [create_alert(a, b) for a, b in alert_field]
  File "/opt/elastalert/elastalert/loaders.py", line 451, in create_alert
    raise EAException('Missing required option(s): %s' % (', '.join(missing_options)))
elastalert.util.EAException: Error initiating alert ['slack', 'email']: Missing required option(s): slack_webhook_url

Issueは先に記載の通り、YAML読み込みの不具合のようで、この変更により修正される。

テスト通知の不具合

/home/node/.local/lib/python3.6/site-packages/urllib3/connectionpool.py:851: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
INFO:elastalert:Alert 'monitoring.es' sent to Slack

ERROR:root:Traceback (most recent call last):
  File "/opt/elastalert/elastalert/elastalert.py", line 1450, in alert
    return self.send_alert(matches, rule, alert_time=alert_time, retried=retried)
  File "/opt/elastalert/elastalert/elastalert.py", line 1549, in send_alert
    self.thread_data.alerts_sent += 1
AttributeError: '_thread._local' object has no attribute 'alerts_sent'


ERROR:root:Uncaught exception running rule monitoring.es: '_thread._local' object has no attribute 'alerts_sent'

変数未定義のエラーだが、最新のソースコードを使用するとエラーは出るものの通知は成功する。ただ、エラーを回避したいため、上記のソース改修をしておく。

ElastAlert再起動後にDisabled→Enabledにしてもルールが有効化されない

変更検知してルールを更新しようとするが、更新先Jobが見つからずエラーになる。

15:39:58.765Z ERROR elastalert-server:
    ProcessController:  ERROR:apscheduler.executors.default:Job "ElastAlerter.handle_config_change (trigger: interval[0:00:10], next run at: 2020-02-11 15:40:08 UTC)" raised an exception
    Traceback (most recent call last):
      File "/home/node/.local/lib/python3.6/site-packages/apscheduler/executors/base.py", line 125, in run_job
        retval = job.func(*job.args, **job.kwargs)
      File "/opt/elastalert/elastalert/elastalert.py", line 1275, in handle_config_change
        self.load_rule_changes()
      File "/opt/elastalert/elastalert/elastalert.py", line 1149, in load_rule_changes
        new_rule = self.init_rule(new_rule, False)
      File "/opt/elastalert/elastalert/elastalert.py", line 970, in init_rule
        self.scheduler.remove_job(job_id=new_rule['name'])
      File "/home/node/.local/lib/python3.6/site-packages/apscheduler/schedulers/base.py", line 621, in remove_job
        raise JobLookupError(job_id)
    apscheduler.jobstores.base.JobLookupError: 'No job by the id of test.article was found'

Quick Fixに記載のリポジトリで修正した。

その他 - 証明書の警告が出続ける

私の環境ではElasticsearchではSSLを有効化しており、ElastAlertからElasticsearch宛はCA証明書を指定してSSL通信するようにしている。Slack宛Webhook通信については証明書検証が無効化されているのか、SSL証明書検証の警告が出続けてしまう...

Test run error
/home/node/.local/lib/python3.6/site-packages/urllib3/connectionpool.py:851: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
INFO:elastalert:Alert 'monitoring.es' sent to Slack

解決
Slack宛通信のSSL CA証明書についてスキーマ拡張されているので、elastalert.yamlで設定する。 https://github.com/Yelp/elastalert/blob/master/elastalert/schema.yaml#L287-L288

  slack_ignore_ssl_errors: {type: boolean}
  slack_ca_certs: {type: string}

その他 - ElastAlert Server debugモードはアラートが飛ばない

デバッグの際の注意点として、ElastAlert Serverのapi.config.jsondebugtrueにしているとアラートが飛ばないので注意。

{
  "appName": "elastalert-server",
  "port": 3030,
  "wsport": 3333,
  "elastalertPath": "/opt/elastalert",
  "verbose": true,
  "es_debug": false,
  "debug": false, //trueにするとアラートが飛ばないので注意!!
  "rulesPath": {
    "relative": true,
    "path": "/rules"
  },
  "templatesPath": {
    "relative": true,
    "path": "/rule_templates"
  },
  "es_host": "elasticsearch",
  "es_port": 9200,
  "es_username": "elastic",
  "es_password": "password",
  "es_ssl": true,
  "es_ca_certs": "/opt/elastalert-server/config/ca.crt",
  "writeback_index": "praeco_elastalert_status"
}

まとめ - Praeco + ElastAlert2.0 + ES7.x 構成の不具合対応方法

ElastAlertの各種バグで、Praeco最新版の docker-compose.yml では正常稼働しない状態となっていた。(2020/02/10時点)

暫定対応として、ElastAlertのソースコードを最新化・改修することで、最低限は使用可能になる。しかし、ルールDisableなど一部機能は正常に動作しないため、ElastAlertの修正版リリースを待つ。