Pollerプロセスの停滞 【MIRACLE ZBX 1.8, 2.0, 2.2】
Pollerプロセスはちゃんと動いている?
Zabbixの監視の肝となるプロセスがPollerプロセスです。
このプロセスは、アクティブ監視以外のほとんどの監視を受け持つプロセスとなります。
zabbix_server.confのStartPollersの値を変更後、Zabbixサーバプロセスを再起動することにより、fork()するプロセス数をコントロールすることができ、これにより監視パフォーマンスの向上を図ることができます。
しかしながら、このPollerプロセスは設計上大変危ういものとなっています。なぜかというと、時々ストールしてしまうことがあるからです。
Zabbixでのタイムアウトの実装は、alarm()をコールし、自信にSIGALRMを飛ばすことにより実現しています。
Pollerプロセスのコードは、主にsrc/zabbix_server/poller.cに記述されており、下記のようになっています。
462 static int get_value(DC_ITEM *item, AGENT_RESULT *result)
463 {
...
469 switch (item->type)
470 {
471 case ITEM_TYPE_ZABBIX:
472 alarm(CONFIG_TIMEOUT);
473 res = get_value_agent(item, result);
474 alarm(0);
475 break;
476 case ITEM_TYPE_SNMPv1:
477 case ITEM_TYPE_SNMPv2c:
478 case ITEM_TYPE_SNMPv3:
479 #ifdef HAVE_SNMP
480 alarm(CONFIG_TIMEOUT);
481 res = get_value_snmp(item, result);
482 alarm(0);
483 #else
ところが、シグナルを受け取り、割り込みが発生した後でも処理を継続してしまう
ことがあります。もし、そこがwhile()での判定であるなら無限ループから抜けること
がなくなってしまいます。(外部からはストールしているように見られます。)
さらに問題なのは、外部からこの状況を判別する手段がないことです。(2.2では多少改善されています。)
特に、1.8.xでは、Pollerプロセスの設計に問題があります。
Pollerプロセスは、処理を開始するとできうる限りの監視アイテムの確保を行います。
その後それを一つずつ処理を行っていきますが、上述のようにストール状態になってしまうと、確保した他のアイテムの処理が行われず、さらに未処理キューに入ったままとなり、Zabbixサーバを再起動しない限りそれらのアイテムの監視は行われなくなります。
(2.0.x以降では、負荷の平準化の観点やこの問題もあってか、一度に一つしか確保しないように改善されています。)
このストール状態を解決する方法はないため、少なくとも検知する方法を開発しました。
Zabbixサーバのほとんどのプロセスは、定期的にスリープを行うために、src/libs/zbxself/selfmon.c : zbx_sleep_loop() をコールします。この中の処理でupdate_selfmon_counter()をコールし、ticks値を更新します。
ですので、この値を外部から定期的にアクセスし、変更されていないことが判ればそのプロセスはストールしていると考えらます。
この値は共有メモリに保存されているため、外部からのアクセスが可能でした。ただし、共有メモリの設計は各バージョンにより変更がされていますので、使用するときにはZabbix側のコードを充分検証する必要があります。
使用方法
ソースコードを下記からダウンロードしてください。
ソースコードをコンパイルし下記のように実行します。
get_poller_status [-c config_file] [-p poller_process] -z zabbix_version
-c : Zabbixサーバの設定ファイルを指定
-p : Pollerうろセスの数を指定
-z : Zabbixのバージョンを指定
# shm_key = 0x53006ede
shmid = 393222
sem_key = 0x7a006ede
semid = 360452
Poller[0] last_ticks = 576376123
Poller[1] last_ticks = 576376129
Poller[2] last_ticks = 576376123
Poller[3] last_ticks = 576376123
Poller[4] last_ticks = 576376123
上記のコマンドをある程度時間が経過した後に実行し、値に変化がなければそのプロセスはストールしていると考えられます。
2.2での改善点
上記で、「2.2では多少改善されています。」と記述しました。2.2の動作している環境でpsコマンドでプロセス情報を取得してみます。
$ ps aux|grep zabbix_server
zabbix 1710 0.0 0.3 253080 3132 ? S 14:03 0:00 zabbix_server: poller #1 [got 0 values in 0.000004 sec, idle 1 sec]
zabbix 1711 0.0 0.3 253080 3216 ? S 14:03 0:00 zabbix_server: poller #2 [got 0 values in 0.000003 sec, idle 1 sec]
zabbix 1712 0.0 0.3 253080 3112 ? S 14:03 0:00 zabbix_server: poller #3 [got 0 values in 0.000008 sec, idle 1 sec]
zabbix 1713 0.0 0.3 253080 3228 ? S 14:03 0:00 zabbix_server: poller #4 [got 0 values in 0.000006 sec, idle 1 sec]
zabbix 1714 0.0 0.3 253080 3132 ? S 14:03 0:00 zabbix_server: poller #5 [got 1 values in 0.000579 sec, idle 1 sec]
...
2.0までとは異なり、プロセス情報を常時書き換える実装がされたため、プロセス稼働状況がわかります。
この実装は下記のようになります。
767 for (;;)
768 {
769 if (0 != sleeptime)
770 {
771 zbx_setproctitle("%s #%d [got %d values in " ZBX_FS_DBL " sec, getting values]",
772 get_process_type_string(process_type), process_num, old_processed,
773 old_total_sec);
774 }
775
776 sec = zbx_time();
777 processed += get_values(poller_type);
778 total_sec += zbx_time() - sec;
...
783 if (0 != sleeptime || STAT_INTERVAL <= time(NULL) - last_stat_time)
784 {
785 if (0 == sleeptime)
786 {
787 zbx_setproctitle("%s #%d [got %d values in " ZBX_FS_DBL " sec, getting values]",
788 get_process_type_string(process_type), process_num, processed, total_sec);
789 }
790 else
791 {
792 zbx_setproctitle("%s #%d [got %d values in " ZBX_FS_DBL " sec, idle %d sec]",
793 get_process_type_string(process_type), process_num, processed, total_sec,
794 sleeptime);
795 old_processed = processed;
796 old_total_sec = total_sec;
797 }
...
801 }
802
803 zbx_sleep_loop(sleeptime);
804 }
ただし、プロセスのストールはget_values()内で発生しますので、ストールしてしまった場合出力が変化しません。ですので定期的にpsコマンドを実行し、変化がないことを確認する必要がでてきます。