現在位置: ホーム / みらくるブログ / Pollerプロセスの停滞 【MIRACLE ZBX 1.8, 2.0, 2.2】

Pollerプロセスの停滞 【MIRACLE ZBX 1.8, 2.0, 2.2】

Zabbixの監視の肝となるプロセス「Pollerプロセス」は、アクティブ監視以外のほとんどの監視を受け持つプロセスとなりますが、時々ストールしてしまうことがあります。 このストール状態を検知する方法を開発してソースコードを公開しました。

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

 

ソースコードをコンパイルし下記のように実行します。

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コマンドを実行し、変化がないことを確認する必要がでてきます。