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

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

こんにちは、MIRACLE ZBXサポートを担当している花島タケシです。 今回は、IPMI監視対象ホストとIPMI Pollerのキャッシュによるバインディングについて解説します。

IPMI監視対象ホストとIPMI Pollerのキャッシュによるバインディング

今回は、IPMI監視対象ホストとIPMI Pollerのキャッシュによるバインディングについて解説します。

IPMIマネージャーの導入

以前のバージョンでも、IPMI監視のための複数のIPMI Pollerプロセスを立ち上げることができました。
しかしながら、下記で報告されているようにこれによる弊害もありました。

  https://support.zabbix.com/browse/ZBXNEXT-3386

3.2.10のコードを見ると、IPMI Pollerプロセスは、ipmi_con_s構造体の *start_con() メンバー関数をコールして接続を行っていますが、*close_connection() メンバー関数をコールしておらずコネクションを閉じていません。(3.2.10の src/zabbix_server/poller/checks_ipmi.c や OpenIPMI-develパッケージで提供される /usr/include/OpenIPMI/ipmi_conn.h を参考としてください。)

つまり、上記で報告されているように、IPMP Pollerプロセス数分のコネクションを監視対象も保持してしまいます。(これにはパフォーマンスとのトレードオフが考えられます。)

コネクションを閉じないことによる監視対象の負荷という問題を解消するために、コネクション情報を保持するキャッシュが導入され、このキャッシュ情報も含めた管理のためにIPMIマネージャーも導入されました。

IPMIマネージャーとIPMI Pollerの関係

IPMIマネージャーとIPMI Pollerの関係をソースコードを追っていくことで解説していきます。

両者間の通信はUNIXソケットを使用しており、Poller側から登録依頼を行い、マネージャーが対象として登録するというところは以前にも紹介したので割愛します。

IPMIマネージャーに関するコードは、src/zabbix_server/ipmi/ipmi_manager.c に記述されています。

 

 952 ZBX_THREAD_ENTRY(ipmi_manager_thread, args)
 953 {
...
1001   for (;;)
1002   {
...
1020     scheduled_num += ipmi_manager_schedule_requests(&ipmi_manager, now, &nextcheck);
...
1031     ret = zbx_ipc_service_recv(&ipmi_service, timeout, &client, &message);
...
1046     if (NULL != message)
1047     {
1048       switch (message->code)
1049       {
...
1057         case ZBX_IPC_IPMI_VALUE_RESULT:
1058           ipmi_manager_process_value_result(&ipmi_manager, client, message, now);
1059           polled_num++;
1060           break;
1061         case ZBX_IPC_IPMI_SCRIPT_REQUEST:
1062           ipmi_manager_process_script_request(&ipmi_manager, client, message, now);
1063           break;
1064         case ZBX_IPC_IPMI_COMMAND_RESULT:
1065           ipmi_manager_process_command_result(&ipmi_manager, client, message, now);
1066       }
1067
1068       zbx_ipc_message_free(message);
1069     }
1070
1071     if (NULL != client)
1072       zbx_ipc_client_release(client);
1073
1074     if (now >= nextcleanup)
1075     {
1076       ipmi_manager_host_cleanup(&ipmi_manager, now);
1077       nextcleanup = now + ZBX_IPMI_MANAGER_CLEANUP_DELAY;
1078     }
1079   }

IPMIマネージャーは、デーモン処理のループが始まると、l.1020の ipmi_manager_schedule_requests() をコールして、監視スケジュールの生成を行います。 

 849 static int ipmi_manager_schedule_requests(zbx_ipmi_manager_t *manager, int now, int *nextcheck)
 850 {
...
 856   num = DCconfig_get_ipmi_poller_items(now, items, MAX_POLLER_ITEMS, nextcheck);
 857
 858   for (i = 0; i < num; i++)
859   {
860     if (FAIL == zbx_ipmi_port_expand_macros(items[i].host.hostid, items[i].interface.port_orig,
861         &items[i].interface.port, &error))
862     {
...
872     }
873
874     request = ipmi_request_create(items[i].host.hostid);
875     request->itemid = items[i].itemid;
876     request->item_state = items[i].state;
877     ipmi_manager_serialize_request(&items[i], 0, &request->message);
878     ipmi_manager_schedule_request(manager, items[i].host.hostid, request, now);
879   }
880
881   zbx_preprocessor_flush();
882   DCconfig_clean_items(items, NULL, num);
883
884   return num;
885 }

l.856の DCconfig_get_ipmi_poller_items() により、次回監視予定時刻が到来している監視アイテムをConfigurationキャッシュから取得します。

当然、IPMIタイプの監視アイテムのみとなります。

従来は、マネージャーが存在していなかったため、各IPMI Pollerプロセスが監視対象となるアイテムを取得するという処理を行っていました。
監視情報を取得するのはマネージャーだけとなっています。

その後、ll.858-879のループ処理において、各々のアイテムに対しての処理を行います。

l.877の ipmi_manager_serialize_request() は、マネージャー - Poller間の通信の無駄を省くための処理で、流れを見ていくという点では重要ではありません。
ただし、メッセージに ZBX_IPC_IPMI_VALUE_REQUEST タグを付与しています。

l.878の ipmi_manager_schedule_request() において、リクエストの処理を行います。

 299 static void ipmi_poller_schedule_request(zbx_ipmi_poller_t *poller, zbx_ipmi_request_t *request)
 300 {
 301   if (NULL == poller->request && NULL != poller->client)
 302     ipmi_poller_send_request(poller, request);
 303   else
 304     ipmi_poller_push_request(poller, request);
 305 }
...
 825 static void ipmi_manager_schedule_request(zbx_ipmi_manager_t *manager, zbx_uint64_t hostid,
 826      zbx_ipmi_request_t *request, int now)
 827 {
 828   zbx_ipmi_manager_host_t *host;
 829
 830   host = ipmi_manager_cache_host(manager, hostid, now);
 831   ipmi_poller_schedule_request(host->poller, request);
 832 }

l.830の ipmi_manager_cache_host() が、キャッシュからhostidに紐づいたホスト情報を取得する関数です。
hostには、関連するPoller情報が格納されており、l.831にてこのPollerに対してリクエストの依頼を行います。

ipmi_poller_schedule_request()では、Pollerがリクエストを処理していない状態ならば直接リクエストを送信し、そうでないならキューへ挿入します。

ここまでで、マネージャーからPollerへリクエストを依頼することが行えました。

Pollerキューとホストキャッシュの関係

処理すべきリクエストはPollerの処理能力に関係なく、アイテムの更新間隔により生成されます。
Pollerが何も処理しなくなったらリクエストを生成しプッシュするという方式でも問題ありませんが、これだと無駄な時間が発生していまいます。

そのため、Poller毎にキューがあります。

本来の目的である「監視対象ホストに対するコネクションの接続」から、あるホストへの接続は特定のPollerからのみと限定します。
そのため、マネージャーがキャッシュするホスト情報にはバインドするPoller情報を保持します。

 Pollerキューとホストキャッシュ1(zbx-tl-023用)

マネージャーが管理するPoller情報のキャッシングはあまり難しい処理はしていません。

l.830の ipmi_manager_cache_host() において、マネージャーが管理しているホスト情報(リスト)にホストがない(キャッシュされていない)場合、リストに載せた後に ipmi_manager_get_host_poller() をコールします。

ipmi_manager_get_host_poller() では、ホストが紐付けされれている数が最も少ないPollerが選出されます。
これにより、ホストに紐づいたPoller情報がキャッシュされます。
キャッシュ情報に載っているときは、その情報を返すだけです。

なお、キャッシュの有効期限は24時間となります。

Pollerキューとホストキャッシュ2(zbx-tl-023用)

IPMI Pollerによるリクエストの処理

Pollerプロセスに関する記述は、src/zabbix_server/ipmi/ipmi_poller.cにされています。 

 212 for (;;)
 213 {
...
 231   if (SUCCEED != zbx_ipc_socket_read(&ipmi_socket, &message))
 232   {
 233     zabbix_log(LOG_LEVEL_CRIT, "cannot read IPMI service request");
 234     exit(EXIT_FAILURE);
 235   }
...
 251   switch (message.code)
 252   {
 253     case ZBX_IPC_IPMI_VALUE_REQUEST:
 254       ipmi_poller_process_value_request(&ipmi_socket, &message);
 255       polled_num++;
 256       break;
 257     case ZBX_IPC_IPMI_COMMAND_REQUEST:
 258       ipmi_poller_process_command_request(&ipmi_socket, &message);
 259       break;
 260     case ZBX_IPC_IPMI_CLEANUP_REQUEST:
 261       zbx_delete_inactive_ipmi_hosts(time(NULL));
 262       break;
 263     }
 264
 265     zbx_ipc_message_clean(&message);
 266 }

Poller側の処理はそれほど複雑ではありません。ソケットからマネージャーが発したメッセージを読み込み、それに応じた処理を行うだけです。

マネージャーからは、ZBX_IPC_IPMI_VALUE_REQUESTタグが付与されたリクエストを発したので、 ipmi_poller_process_value_request() を見ていきます。

  92 static void ipmi_poller_process_value_request(zbx_ipc_socket_t *socket, zbx_ipc_message_t *message)
  93 {
...
 102 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
 103
 104 zbx_ipmi_deserialize_request(message->data, &itemid, &addr, &port, &authtype,
 105 &privilege, &username, &password, &sensor, &command);
...
 111 errcode = get_value_ipmi(itemid, addr, port, authtype, privilege, username, password, sensor, &value);
 112 ipmi_poller_send_result(socket, ZBX_IPC_IPMI_VALUE_RESULT, errcode, value);
...
 121 }

マネージャー側でシリアライズ(パッケージ化)されたメッセージを zbx_ipmi_deserialize_request() で分解します。こちらもあまり重要ではありません。

その後、get_value_ipmi()により監視対象に対して監視を行います。この実装は以前のものと大差ありません。

最後に監視結果を ipmi_poller_send_result() にてZBX_IPC_IPMI_VALUE_REQUESTタグを付与して、マネージャーに送ります。

 

監視結果の受け取り

再度マネージャー側の処理に戻ります。

マネージャーは、ipmi_manager_schedule_requests() をコールしてPoller群にリクエストを割り振った後に、l.1031の zbx_ipc_service_recv() にてメッセージの読み取りを行います。

その後、ll.1046-1069のブロックにて受け取ったメッセージに対する処理を行います。ZBX_IPC_IPMI_SCRIPT_REQUEST は、Poller群からのメッセージではありません。

実は、IPMIマネージャーはPoller群からのみメッセージを受け付けるわけではありません。

Escalator等からのスクリプト実行メッセージも受け付けます。以前のバージョンからIPMIタイプのスクリプトを設定することができます。
そういった類のスクリプトに対する処理となります。

なお、実行はPoller群にて行われ、その返信は ZBX_IPC_IPMI_COMMAND_RESULT タグが付与されます。ZBX_IPC_IPMI_COMMAND_RESULT タグの処理は、後述する ZBX_IPC_IPMI_VALUE_RESULT と大差ありません。

ZBX_IPC_IPMI_SCRIPT_REQUEST は、通常監視に対する監視結果の処理となります。

ipmi_manager_process_value_result() が対応する関数となります。 

 717 static void ipmi_manager_process_value_result(zbx_ipmi_manager_t *manager, zbx_ipc_client_t *client,
 718 zbx_ipc_message_t *message, int now)
 719 {
...
 728   if (NULL == (poller = ipmi_manager_get_poller_by_client(manager, client)))
 729   {
 730     THIS_SHOULD_NEVER_HAPPEN;
 731     return;
 732   }
 733   itemid = poller->request->itemid;
 734
 735   zbx_ipmi_deserialize_result(message->data, &ts, &errcode, &value);
 736
 737   /* update host availability */
...
 755   /* add received data to history cache */
...
 783   /* put back the item in configuration cache IPMI poller queue */
 784   DCrequeue_items(&itemid, &state, &ts.sec, &errcode, 1);
 785
 786   ipmi_poller_free_request(poller);
 787   ipmi_manager_process_poller_queue(manager, poller, now);
 788 }

impi_manager_process_value_result() では初め(l.728)に、メッセージ発信元(メッセージ読み込み時に確定します)に関する情報を取得します。

一つのPollerには同時に複数のリクエストを投げることができないため、リクエストに関するアイテム等が一意に確定します。

l.735ではPollerからのメッセージを分解しています。この時点で監視が成功したかがわかります。

ソースコードは端折りましたが、l.737から監視結果によりホストのステータスを変更し、l.755にてプロプロセッサーに監視データを渡します。
l.784にて次回監視時刻を計算しConfigurationキャッシュ内のキューの再配置を行います。
この辺りは通常のPoller等と変わりません。

l.786にて、リクエスト情報を消去します。
この時点で、メッセージを送ってきたPollerは処理を行っておらず、フリーな状態です。

したがって、l.787の ipmi_manager_process_poller_queue() により、当該Pollerへのリクエストの割り当てを行います。

以上で、IMPIマネージャーとPoller群の処理の説明を行いました。

これらと処理の概略図を示して終わりにします。

 IPMIマネージャーとPoller群(zbx-tl-023用)

関連記事

注意事項

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

 

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

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

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

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