BLOG
2017 年 11 月 29 日
Zabbix 3.2以降の新機能解説(Zabbix 4.0を見据えて) その6 - 「障害の手動クローズ」機能
こんにちは、MIRACLE ZBX サポートを担当している花島タケシです。 今回は Zabbix 3.2.0 で追加された「障害の手動クローズ」機能について解説を行います。
障害の手動クローズと Task Manager プロセスの導入
前回までは、Zabbix 3.4.0 で追加された機能をいくつか紹介したいため、今回はあえて Zabbix 3.2.0 で追加された「障害の手動クローズ」機能について解説を行います。
解説に使用するバージョンは Zabbix 3.2.7 となります。
Web フロントエンドからの障害のクローズ
この機能はかねてより、当社の多くのお客さまからも要望されていたものでした。
ようやく実装されたという思いです。
Web フロントエンドの障害 ( ここも「イベント」から変更されています ) から、障害の表示を行い、「時刻」部分をクリックして対象の障害を開きます。
ここから対象のイベントにコメント入力を行います。
「障害のクローズ」にチェックを入れて「障害対応コメント」をすることによりクローズされます。
ただし、これを行うためにはトリガー設定画面にて、「手動でのクローズ許可」にチェックを入れておかなければなりません。
これにチェックを入れていない場合は、「障害対応コメント入力」において、チェックボックスにチェックを入れられないようになっています。
参考
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 を使用する別のサービスについて解説を行います。
関連記事
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 1
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 2
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 3
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 4
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 5
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 6
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 7
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 8
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 9
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 10
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 11
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 12
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 13
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 14
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 15
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 16
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 17
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 18
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 19
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 20
Zabbix 3.2 以降の新機能解説(Zabbix 4.0 を見据えて) その 21
注意事項
- 本ドキュメントの内容は、予告なしに変更される場合があります。
- 本ドキュメントは、限られた評価環境における検証結果をもとに作成しており、全ての環境での動作を保証するものではありません。
- 本ドキュメントの内容に基づき、導入、設定、運用を行なったことにより損害が生じた場合でも、当社はその損害についての責任を負いません。あくまでお客さまのご判断にてご使用ください。