現在位置: ホーム / 製品・サービス / 統合システム監視 MIRACLE ZBX / MIRACLE ZBX - Zabbix テック・ラウンジ / Zabbix 3.2以降の新機能解説(4.0を見据えて) その5

Zabbix 3.2以降の新機能解説(4.0を見据えて) その5

前回「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(zbx-tl-014用)

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

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(zbx-tl-014用)

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

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

 構造体関連図3(zbx-tl-014-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(zbx-tl-014-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からデータを取得し、(一応)許容数内での並列処理を実現しています。

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

関連記事

注意事項

本ドキュメントの内容は、予告なしに変更される場合があります。
本ドキュメントは、限られた評価環境における検証結果をもとに作成しており、全ての環境での動作を保証するものではありません。
本ドキュメントの内容に基づき、導入、設定、運用を行なったことにより損害が生じた場合でも、当社はその損害についての責任を負いません。あくまでお客さまのご判断にてご使用ください。

 

MIRACLE ZBX 製品・サポートサービス 詳しくはこちら

MIRACLE ZBX Virtual Appliance V3.0 評価版のお申し込み

製品・サービスについてのお問い合わせ

お問い合わせフォームMIRACLE ZBX製品やサポートサービスについてのご相談やご質問は、「お問い合わせフォーム」よりお気軽にお問い合わせください。