採用情報

お問い合わせ

BLOG

Zabbix テック・ラウンジ

2017 年 10 月 20 日

Zabbix 3.2以降の新機能解説(Zabbix 4.0を見据えて) その5 - Alert Managerのキュー管理

こんにちは、MIRACLE ZBX サポートを担当している花島タケシです。 「Zabbix 3.2 以降の新機能解説(4.0 を見据えて) その 3」では、Alerter プロセスが複数起動できることを解説しました。 今回は、Alert Manager がどのようにキュー管理をしているかを解説します。

概要 - Alerter プロセスも複数起動できるようになります。(その 2)

前回「Zabbix 3.2 以降の新機能解説(4.0 を見据えて) その 3」では、Alerter プロセスが複数起動できることを解説しました。
Alert Manager プロセスがキュー管理をし、アラート配信処理を Alerter プロセスへ、UNIX ソケットを用いた通信で依頼をするという方法です。( 以下、Alert Manager, Alerter と記述します。)

今回は、Alert Manager がどのようにキュー管理をしているかを解説します。
ソースコードの解説は、src/zabbix_server/alerter/alert_manager.c にほぼ終始しています。

なお、解説に用いるコードは Zabbix 3.4.0 beta2 となります。

Alert Manager 管理領域の構成

基点となる管理領域は、zbx_am_t 構造体 ( 内部変数としては、manager が用いられます。) となり、am_init() で初期化されます。

処理を進めていくと、mediatype, alertpool, alert といった内部データが、manager にぶら下がる構成になり、Alert Manager は manager から全ての情報を辿っていくことになります。

前記の 3 つはそれぞれ、zbx_am_mediatype_t 構造体、zbx_am_alertpool_t 構造体、zbx_am_alert_t 構造体により管理されます。

メディアタイプは、ほぼ Web フロントエンドでの管理 -> メディアタイプに設定した情報となります。media_type テーブルから情報を取得します。

アラートは、Escalator プロセス等により生成されたデータです。alerts テーブルからの情報となります。
アラートプールだけはちょっと異なります。

これは内部的に持っているデータであり、対応するテーブル等はありません。これのユニーク性は、

  • イベントを生成したソース
  • イベントが由来するオブジェクト
  • イベントが由来するオブジェクト ID

により担保されます。後でも述べますが、これにより由来が同じであるアラートのグループ化を行っています。

また、事前に説明を加えておくと、zbx_am_t, zbz_am_mediatype_t, zbx_am_alertpool_t のそれぞれには、queue というメンバーが存在します。

これらは、それぞれ下位の情報をキューとしてを保持するためのものとなります。

DB からアラート情報を読み込む

以下のソースコード解説は、白紙の状態、つまりまだ何もアラートが発生していない状態を想定しています。これはソースコードの読解を容易にするためです。

ソースコードを読むにあたり、実際の動作を想定し、クリーンな状態から登録があり、処理が進み、二度目以降のループ処理と想定することは、単に上から読むよりも動きを理解できると思います。

最初に am_db_queue_alerts() をコールし、manager ツリーを構成する処理を行います。

 構造体関連図 1

ソース解説を行おうとしましたが煩雑であるため、生成されるデータ構造の図を示しました。

am_db_queue_alerts() からは、am_db_get_alerts() がコールされ、アラート情報 ( 配列 ) が構築されます。このときに、alert が所属する alertpool のための alertpoolid が、下記の 3 つを用いて生成されます。

これにより、同じ属性を持つ alert が同一の alertpool に所属することを保証します。

イベントを生成したソース
イベントが由来するオブジェクト
イベントが由来するオブジェクト ID

次に、取得したアラート情報 ( 配列 ) が保持している mediatypeid を用いて、DB からメディアタイプの情報を取得し、am_db_update_mediatypes() によりメディアタイプ情報 ( 配列 ) が構築されます。

新規に読み取ったメディアタイプに対しては、location メンバーの値は NOWHERE( 無所属 ) となります。また、manager 構造体の mediatypes に付与されます。

メディアタイプ情報は manager に関連付けられていますが、アラート情報はまだどこにも関連付けられていません。

1237 static int am_db_queue_alerts(zbx_am_t *manager, int now)
1238 {
...
1253 if (FAIL == (ret = am_db_get_alerts(&alerts, now)))
1254 goto out;
1255
1256 /* update media types for new and queued alerts */
1257
1258 zbx_vector_uint64_create(&mediatypeids);
1259
1260 for (i = 0; i < alerts.values_num; i++)
1261 {
1262 alert = (zbx_am_alert_t *)alerts.values[i];
1263 zbx_vector_uint64_append(&mediatypeids, alert->mediatypeid);
1264 }
1265
1266 zbx_hashset_iter_reset(&manager->mediatypes, &iter);
1267 while (NULL != (mediatype = (zbx_am_mediatype_t *)zbx_hashset_iter_next(&iter)))
1268 zbx_vector_uint64_append(&mediatypeids, mediatype->mediatypeid);
1269
1270 if (0 != mediatypeids.values_num)
1271 {
1272 zbx_vector_uint64_sort(&mediatypeids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
1273 zbx_vector_uint64_uniq(&mediatypeids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
1274
1275 ret = am_db_update_mediatypes(manager, mediatypeids.values, mediatypeids.values_num);
1276 }
1277
1278 zbx_vector_uint64_destroy(&mediatypeids);

 構造体関連図 2

そして、am_db_queue_alert() に戻り、alert が所属する alertpool を am_get_alertpool() により決定します。該当する alertpoold が存在しない場合には新規に作成され、manager 構造体の alertpools メンバーに付与されます。

alertpool 構造体の location メンバーは同様に NOWHERE となります。

 構造体関連図 3

最後に、alert と alertpool, alertpool と mediatype, mediatype と manager の紐付けをそれぞれ、am_push_alert(), am_push_alertpool(), am_push_mediatype() を用いて行います。

am_push_alert(), am_push_alertpool() により、alertpool の queue に alert 情報が、mediatype の queue に alertpool 情報が付与されます。
さらに、alertpool->location は QUEUE となり、キューに入れられたことになります。

しかしながら、am_push_mediatype() では、許容数以下である場合でした、manager の queue に付与されません。

402 static void am_push_mediatype(zbx_am_t *manager, zbx_am_mediatype_t *mediatype)
403 {
404 zbx_binary_heap_elem_t elem = {mediatype->mediatypeid, mediatype};
405
406 if (SUCCEED == zbx_binary_heap_empty(&mediatype->queue))
407 return;
408
409 if (ZBX_AM_LOCATION_NOWHERE == mediatype->location)
410 {
411 if (0 == mediatype->maxsessions || mediatype->alerts_num < mediatype->maxsessions)
412 {
413 zbx_binary_heap_insert(&manager->queue, &elem);
414 mediatype->location = ZBX_AM_LOCATION_QUEUE;
415 }
416 }
417 else
418 zbx_binary_heap_update_direct(&manager->queue, &elem);
419 }

 構造体関連図 4

このコードを見ると、ここまで新規登録の処理において alerts_num の加算を行っていません。それにもかかわらず無条件でコールされています。

ということは、制限を超えてしまう気がしてならないのですが ...

配信処理

キューに入れられたアラートを実際に配信する処理を追っていきます。

Alert Manager のループ処理において、am_check_queue() でキューのチェックが行われた後に、am_process_alert() がコールされ配信処理が行われます。

1956 while (SUCCEED == am_check_queue(&manager, now))
1957 {
1958 if (NULL == (alerter = zbx_queue_ptr_pop(&manager.free_alerters)))
1959 break;
1960
1961 if (FAIL == am_process_alert(&manager, alerter, am_pop_alert(&manager)))
1962 zbx_queue_ptr_push(&manager.free_alerters, alerter);
1963 }

am_check_queue() では、manager->queue から直近に処理すべき情報 (mediatype) を取り出します。( と言っても、maanger->queue に入れられる時点でソートはされています。)

さらに、mediatype->queue から alertpool を取り出し、alertpool->queue から処理すべき alert を取り出します。

am_process_alert() をコールするときに、am_pop_alert() を用いて alert を取得しています。am_check_queue() でチェックを行っているため、ここで取得しておけば良いとも思いますが ...

701 static zbx_am_alert_t *am_pop_alert(zbx_am_t *manager)
702 {
703 zbx_am_mediatype_t *mediatype;
704 zbx_am_alertpool_t *alertpool;
705 zbx_am_alert_t *alert;
706 zbx_binary_heap_elem_t *elem;
707
708 if (NULL == (mediatype = am_pop_mediatype(manager)))
709 return NULL;
710
711 alertpool = am_pop_alertpool(mediatype);
712
713 elem = zbx_binary_heap_find_min(&alertpool->queue);
714 alert = (zbx_am_alert_t *)elem->data;
715 zbx_binary_heap_remove_min(&alertpool->queue);
716
717 /* requeue media type if the number of parallel alerts has not yet reached */
718 mediatype->alerts_num++;
719 alertpool->alerts_num++;
720 if (0 == mediatype->maxsessions || mediatype->alerts_num < mediatype->maxsessions)
721 am_push_mediatype(manager, mediatype);
722
723 return alert;
724 }

manager から、mediatype, alertpool と辿って alert を取得します。最後にキューの許容数をチェックして、am_push_mediatype() をコールして、関連する mediatype に関する alert をキューに追加しています。ここでは、許容数のチェックが行われており、正しく動作していると思います。

am_process_alert() に関しては、前回解説を行ったので省略します。

アラートの破棄

最後に、アラートを割り振った Alerter プロセスから結果を取得し、アラートの破棄、リトライを行います。
正常終了やリトライ回数上限に達した場合は、am_remove_alert() により alert の破棄が行われます。

736 static void am_remove_alert(zbx_am_t *manager, zbx_am_alert_t *alert)
737 {
738 zbx_am_alertpool_t *alertpool;
739 zbx_am_mediatype_t *mediatype;
740
741 if (NULL != (mediatype = am_get_mediatype(manager, alert->mediatypeid)))
742 {
743 mediatype->alerts_num--;
744
745 if (NULL != (alertpool = am_get_alertpool(manager, alert->mediatypeid, alert->alertpoolid)))
746 {
747 alertpool->alerts_num--;
748 if (SUCCEED != am_release_alertpool(manager, alertpool))
749 am_push_alertpool(mediatype, alertpool);
750 }
751
752 if (SUCCEED != am_release_mediatype(manager, mediatype))
753 am_push_mediatype(manager, mediatype);
754 }
755
756 am_alert_free(alert);
757 }

ここで許容数チェック用の変数の減算が行われます。

同様に、am_retry_alert() ではリトライ処理が行われ、alert が再度キューに配置され、各カウントの計算が行われます。

まとめ

以上で、従来の Alerter 処理の並列化について解説を完了しました。

少し難しかったと思いますが、Alert Manager と Alerter に分離され、これらは UNIX ソケットにより IPC 通信が行われます。
Alert Manager は DB からデータを取得し、( 一応 ) 許容数内での並列処理を実現しています。

ただし、少し危うい箇所もあるとは思いますが。

関連記事

Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 1
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 2
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 3
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 4
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 5
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 6
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 7
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 8
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 9
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 10
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 11
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 12
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 13
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 14
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 15
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 16
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 17
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 18
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 19
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 20
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 21

注意事項

  • 本ドキュメントの内容は、予告なしに変更される場合があります。
  • 本ドキュメントは、限られた評価環境における検証結果をもとに作成しており、全ての環境での動作を保証するものではありません。
  • 本ドキュメントの内容に基づき、導入、設定、運用を行なったことにより損害が生じた場合でも、当社はその損害についての責任を負いません。あくまでお客さまのご判断にてご使用ください。
CentOS 7 延長サポートサービス
デジタルトランスフォーメーションのための電子認証基盤 iTrust
SSL/TLS サーバー証明書 SureServer Prime