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

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

今回は Zabbix および MIRACLE ZBX 3.2.0で追加された「障害の手動クローズ」機能について解説を行います。

障害の手動クローズとTask Managerプロセスの導入

前回までは、Zabbix3.4.0で追加された機能をいくつか紹介したいため、今回はあえてZabbix3.2.0で追加された「障害の手動クローズ」機能について解説を行います。

解説に使用するバージョンはZabbixおよびMIRACLE ZBX3.2.7となります。

Webフロントエンドからの障害のクローズ

この機能はかねてより、当社の多くのお客さまからも要望されていたものでした。

ようやく実装されたという思いです。

Webフロントエンドの障害(ここも「イベント」から変更されています)から、障害の表示を行い、「時刻」部分をクリックして対象の障害を開きます。

Webフロントエンド(zbx-tl-017用)

ここから対象のイベントにコメント入力を行います。

障害対応コメント(zbx-tl-017用)

「障害のクローズ」にチェックを入れて「障害対応コメント」をすることによりクローズされます。

ただし、これを行うためにはトリガー設定画面にて、「手動でのクローズ許可」にチェックを入れておかなければなりません。
これにチェックを入れていない場合は、「障害対応コメント入力」において、チェックボックスにチェックを入れられないようになっています。

参考

https://www.zabbix.com/documentation/3.2/manual/introduction/whatsnew320#close_problems_manually 

その後の処理はどうなるの?

Webフロントエンドは、あくまでもデータベースとの対話的処理を行うにすぎません。

直接的には、Zabbixサーバやエージェントのプロセスにはアクセスしません。
エージェント
が取得したデータや、サーバが処理を行った結果は、あくまでもZabbixサーバが書き込んだデータベースの情報を取得して表示しているにすぎないのです。

つまり、手動でイベントをクローズ状態にした(特定のDBだけ更新した)としても、Zabbixサーバがなんらかの処理をしないと、アクションの実行まで行われません。

ちなみに、これに関するテストをするとおもしろいことがあります。
障害イベントを生成して
からZabbixサーバを止めます。
その後、手動クローズするとステータスは「クローズ」になります。「解決済」ではありません。

その後、Zabbixサーバを開始すると、ステータスは「解決済」に変ります。
ここからも、Zabbixサーバが何かしらの処理をしていることがわかります。

Task Managerプロセスの導入

この機能を実装するにあたり、Task Managerプロセスが導入されました。それに伴い、taskテーブルとtask_close_problemテーブルも導入されました。
Webフロントエンドから手動クローズを行うと、これらのテーブル等に適宜情報を挿入し、該当のイベントのステータスを変更します。

後の処理は、新しく追加したTask Managerプロセスに任せることになります。

上記を踏まえ、今回もまたデーモンの解説です。
下記がメインのループ処理です。

210 ZBX_THREAD_ENTRY(taskmanager_thread, args)
211 {
...
232   for (;;)
233   {
234     zbx_sleep_loop(sleeptime);
...
238     zbx_setproctitle("%s [processing tasks]", get_process_type_string(process_type));
239
/* ここで実際のタスク処理を行う */
240     sec1 = zbx_time();
241     tasks_num = tm_process_tasks()
242     sec2 = zbx_time();
243
/* 次回処理時刻の算出 */
ZBX_TASKMANAGER_TIMEOUT(5秒)周期の処理を行うようにしている。 
244     nextcheck = (int)sec1 - (int)sec1 % ZBX_TASKMANAGER_TIMEOUT + ZBX_TASKMANAGER_TIMEOUT;
245 
246     if (0 > (sleeptime = nextcheck - (int)sec2))
247       sleeptime = 0;
248 
249     zbx_setproctitle("%s [processed %d task(s) in " ZBX_FS_DBL " sec, idle %d sec]",
250         get_process_type_string(process_type), tasks_num, sec2 - sec1, sleeptime);
251   }
252 }

実際のタスク処理を行うtm_process_tasks()は下記となっています。


177 static int  tm_process_tasks()
178 {
...
/* taskテーブルをポーリングすることになる */
184   result = DBselect("select taskid,type from task order by taskid");
185 
186   while (NULL != (row = DBfetch(result)))
187   {
188     ZBX_STR2UINT64(taskid, row[0]);
189     ZBX_STR2UCHAR(type, row[1])
190
/* typeに応じた処理。3.2.7では障害のクローズしか実装されていない。 */
191     switch (type)
192     {
193       case ZBX_TM_TASK_CLOSE_PROBLEM:
194         ret = tm_try_task_close_problem(taskid);
195         break;
196       default:
197         THIS_SHOULD_NEVER_HAPPEN;
198         ret = FAIL;
199         break;
200     }
201 
202     if (FAIL != ret)
203       processed_num++;
204   }
205   DBfree_result(result);
206 
207   return 0;
208 }
210 ZBX_THREAD_ENTRY(taskmanager_thread, args)
211 {
...
232   for (;;)
233   {
234     zbx_sleep_loop(sleeptime);
...
238     zbx_setproctitle("%s [processing tasks]"
get_process_type_string(process_type));
239
/* ここで実際のタスク処理を行う */
240     sec1 = zbx_time();
241     tasks_num = tm_process_tasks();
242     sec2 = zbx_time();
243

/* 次回処理時刻の算出 */
/* ZBX_TASKMANAGER_TIMEOUT(5秒)周期の処理を行うようにしている。 */
244     nextcheck = (int)sec1 - (int)sec1 % ZBX_TASKMANAGER_TIMEOUT + ZBX_TASKMANAGER_TIMEOUT;
245
246     if (0 > (sleeptime = nextcheck - (int)sec2))
247       sleeptime = 0;
248
249     zbx_setproctitle("%s [processed %d task(s) in " ZBX_FS_DBL " sec, idle %d sec]",
250         get_process_type_string(process_type), tasks_num, sec2 - sec1,  sleeptime);
251   }
252 }

実際のタスク処理を行うtm_process_tasks()は下記となっています。

177 static int  tm_process_tasks()
178 {
...
/* taskテーブルをポーリングすることになる */
184   result = DBselect("select taskid,type from task order by taskid");
185
186   while (NULL != (row = DBfetch(result)))
187   {
188     ZBX_STR2UINT64(taskid, row[0]);
189     ZBX_STR2UCHAR(type, row[1]);
190
/* typeに応じた処理。3.2.7では障害のクローズしか実装されていない。 */
191     switch (type)
192     {
193       case ZBX_TM_TASK_CLOSE_PROBLEM:
194         ret = tm_try_task_close_problem(taskid);
195         break;
196       default:
197         THIS_SHOULD_NEVER_HAPPEN;
198         ret = FAIL;
199         break;
200     }
201
202     if (FAIL != ret)
203       processed_num++;
204   }
205   DBfree_result(result);
206
207   return 0;
208 }

l.184からの処理でtaskテーブルから情報を全て取得し、得られた行に対して順次ループで処理を行います。

typeカラムごとの処理を行うようになっていますが、現在はZBX_TM_TASK_CLOSE_PROBLEMのみ記述されています。3.4.0では増えています。

実際のクローズ後の処理は、tm_try_task_close_problem() -> tm_execute_task_close_problem()と辿ります。

static void tm_execute_task_close_problem(zbx_uint64_t taskid, zbx_uint64_t triggerid, zbx_uint64_t eventid,
48 zbx_uint64_t userid, zbx_vector_uint64_t *locked_triggerids)
49 {
...
60 DBbegin();
61
62 result = DBselect("select null from problem where eventid=" ZBX_FS_UI64 " and r_eventid is null", eventid);
63
64 /* check if the task hasn't been already closed by another process */
65 if (NULL != DBfetch(result))
66 {
/* キャッシュからtriggeridに関する情報を取得 */
67 DCconfig_get_triggers_by_triggerids(&trigger, &triggerid, &errcode, 1);
68
69 if (SUCCEED == errcode)
70 {
71 zbx_vector_ptr_t trigger_diff;
72
73 zbx_vector_ptr_create(&trigger_diff);
74
/* トリガーの差分を作成する。 */
75 zbx_append_trigger_diff(&trigger_diff, triggerid, trigger.priority,
76 ZBX_FLAGS_TRIGGER_DIFF_RECALCULATE_PROBLEM_COUNT, trigger.value,
77 TRIGGER_STATE_NORMAL, 0, NULL);
78
79 zbx_timespec(&ts);
80
/* 解決済みのイベントを生成 */
81 close_event(eventid, EVENT_SOURCE_TRIGGERS, EVENT_OBJECT_TRIGGER, triggerid,
82 &ts, userid, 0, 0, trigger.description, trigger.expression_orig,
83 trigger.recovery_expression_orig, trigger.priority, trigger.type, NULL,
84 ZBX_TRIGGER_CORRELATION_NONE, "");
85
/*トリガー情報を更新する。 */
escalationsテーブルへ情報を挿入される。(アクション実行の起点)
86 process_trigger_events(&trigger_diff, locked_triggerids, ZBX_EVENTS_SKIP_CORRELATION);
/* キャッシュ情報を更新する。 */ 
87 DCconfig_triggers_apply_changes(&trigger_diff);
/* トリガーの変更を反映する。 */
88 zbx_save_trigger_changes(&trigger_diff);
89
90 zbx_vector_ptr_clear_ext(&trigger_diff, (zbx_clean_func_t)zbx_trigger_diff_free);
91 zbx_vector_ptr_destroy(&trigger_diff);
92 }
93
94 DCconfig_clean_triggers(&trigger, &errcode, 1);
95 }
96 DBfree_result(result);
97
98 DBexecute("delete from task where taskid=" ZBX_FS_UI64, taskid);
99
100 DBcommit();

最初にl.62でproblemテーブルから、「渡されたeventidに該当し、リカバリーがされていない情報」の有無をチェックしています。リカバリーがされているイベントは手動でクローズする必要がありません。
Webフロントエンドにて入力されてからの処理は、遅延があるため、この処理を行う前に監視が行われ、復旧していることがあるからです。

l.65以降で得られたデータに対する処理を行います。

l.67にて、渡されたtriggeridに関する情報をキャッシュから取得しています。既にこの関数がコールされている時点で、先のeventidとtriggeridに関連づけがされているので、eventidとの相関をチェックする必要はありません。
また、これ以降、渡されるtriggeridは一つだけであることも注意してください。

ll.75-77でのzbx_append_trigger_diff()にて、更新するトリガー情報を作成しておき、l.81のclose_event()で、解決済のイベントが生成されます。
ここで解決済のイベントを生成することにより、リカバリーメッセージが必要な場合には、Escalatorプロセスからアクションが実行されるようになります。

さらに、l.86のprocess_trigger_events()にてトリガー情報を更新し、l.87のDCconfig_triggers_apply_changes()にてキャッシュ情報を更新しています。そして、l.98にて、taskテーブルから該当のタスクを削除して処理は終了します。

なお、process_trigger_events()においてescalationsテーブルへ情報が挿入され、ここを起点にアクションが実行されます。このルートはDB Syncerも同様となっています。

 通常のイベントクローズパスは、 「DB Syncerがトリガー判定を行いその結果としてイベント生成を行う」ことになります。
その後、escalationsテーブルにデータを挿することにより、結果としてアクションの実行まで行われます。
Task Managerプロセスにより、この通常ルートとは別にイベント関連の処理を行い、アクションの実行までを行う仕組みを、上手に割り込むことができと思います。

 次回は、Task Managerを使用する別のサービスについて解説を行います。

 関連記事

注意事項

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

 

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

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

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

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