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

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

こんにちは、MIRACLE ZBXサポートを担当している花島タケシです。 今回はTask Managerプロセスを利用した最後のサービスについての後編の解説となります。

プロキシでのリモートコマンドの実行(その2)

今回はTask Managerプロセスを利用した最後のサービスについての後編の解説となります。

参考

Remote command support through proxies

前回は、Escalatorプロセスが通常のアラート処理と異なり、alertテーブルへ情報を挿入せずに、リモートコマンドをプロキシで実行するためにtask_remote_commandテーブルへと挿入しているところまで解説を行いました。

リモートコマンド(zbx-tl-020用)

今回はそのデータをどのようにプロキシに渡し、プロキシがどうやって実行しているか?ということを解説してきます。

アクティブプロキシとパッシブプロキシ

Zabbixにおいてプロキシはアクティブプロキシとパッシブプロキシの二種類が存在します。

それらの違いは、名前からも分かるように主体性の違いとなります。アクティブプロキシは、プロキシが起動した直後からサーバへ問い合わせを行い、サーバからそのプロキシが受け持つべきホストの監視設定を受け取ります。

監視設定は刻一刻と変化するため、プロキシは定期的にサーバへ問い合わせを行います。
それとは逆に、パッシブプロキシは自ら問い合わせを行うことはなく、Zabbixサーバ起動後にプロキシに対して監視設定を送信します。当然こちらも定期的に送信することになります。

リモートコマンド情報はどうやって送り込む?

前回の説明でリモートコマンド情報はtask_remote_commandテーブルへと一旦格納されていることを説明しました。実際にはtaskテーブルへの挿入も行いますが、コマンド実行の直接的な情報ではありません。

Task Managerプロセスは、taskテーブルをポーリングして処理を行うプロセスです。

taskテーブルを参照して、関連する情報をtask_remote_commandテーブルから参照してもパッシブプロキシにデータを送信することはできますが、アクティブプロキシに、強制的にでも、データを送信することはできません。

では、どうしているのか?というと、アクティブプロキシに対しての任務は、Trapperプロセスが受け持ちます。というよりも、サーバが主体的に行えない通信のほとんどはTrapperプロセスが受け持つこととなります。

Trapperプロセスによる受送信

Trapperプロセスは、なんらかの受信を行うとprocess_trap()をコールして、受信したデータの解析を行い、それに準じた処理をし、返信を行います。
これは、src/zabbix_server/trapper/trapper.c を眺めていただければと思います。

アクティブプロキシは、サーバから監視設定を受け取るために、定期的にサーバ要求を送信します。

と書きましたが、実は正確ではありません。「監視等によって得られたデータを送信し、そのときにサーバは監視設定を送信する」が正しいです。

このときに、ZBX_PROTO_VALUE_PROXY_DATAタグを付けて送信を行います。

src/zabbix_proxy/datasender/datasender.c

59 static int proxy_data_sender(int *more, int now)
60 {
...
78  zbx_json_init(&j, 16 * ZBX_KIBIBYTE);
79
80  zbx_json_addstring(&j, ZBX_PROTO_TAG_REQUEST, ZBX_PROTO_VALUE_PROXY_DATA, ZBX_JSON_TYPE_STRING);
...
203 ZBX_THREAD_ENTRY(datasender_thread, args)
204 {
...
235     records += proxy_data_sender(&more, (int)time_now);

一方、これを受け取ったサーバのTrapperプロセスはこのタグに対する処理を行います。

920 static int process_trap(zbx_socket_t *sock, char *s, zbx_timespec_t *ts)
921 {
...
979     else if (0 == strcmp(value, ZBX_PROTO_VALUE_PROXY_DATA))
980     {
981      if (0 != (program_type & ZBX_PROGRAM_TYPE_SERVER))
982       zbx_recv_proxy_data(sock, &jp, ts);
983      else if (0 != (program_type & ZBX_PROGRAM_TYPE_PROXY_PASSIVE))
984       zbx_send_proxy_data(sock, ts);
985     }

l.981は稼働しているデーモンがZabbixサーバであるかを判定しています。
l.983はパッシブプロキシ(Zabbixサーバからの受信)の判定となります。

今回はZabbixサーバでの処理に着目しているため、src/zabbix_server/trapper/proxydata.c :zbx_recv_proxy_data()を見ていきます。

この関数から、返信用のデータを生成するzbx_send_proxy_data_respose()がコールされます。
ここから、zbx_tm_get_remote_tasks()がコールされ、件のtask_remote_commandテーブルを参照することになります。

プロキシでのリモートコマンド実行

説明を省いてしまいますが、サーバからの返信を受け取ったプロキシはリモートコマンドの実行データをzbx_tm_save_tasks()をコールして、プロキシ側のtaskテーブルとtask_remote_commandテーブルに挿入します。

プロキシ側のTask Managerプロセスは、サーバ側と同様にtaskテーブルをポーリングします。

src/zabbix_proxy/taskmanager/taskmanager.c

205 ZBX_THREAD_ENTRY(taskmanager_thread, args)
206 {
...
228   for (;;)
229   {
...
238     tasks_num = tm_process_tasks((int)sec1);

ただし、サーバ側とは異なり現在のところ、リモートコマンドの実行に対する処理しか実装されていません。 

149 static int tm_process_tasks(int now)
150 {
...
157   result = DBselect("select taskid,type,clock,ttl"
158      " from task"
159      " where status=%d"
160      " and type=%d"
161      " order by taskid",
162     ZBX_TM_STATUS_NEW, ZBX_TM_TASK_REMOTE_COMMAND);
163
164   while (NULL != (row = DBfetch(result)))
165   {
166    ZBX_STR2UINT64(taskid, row[0]);
167    ZBX_STR2UCHAR(type, row[1]);
168    clock = atoi(row[2]);
169    ttl = atoi(row[3]);
170
171    switch (type)
172    {
173     case ZBX_TM_TASK_REMOTE_COMMAND:
174      ret = tm_execute_remote_command(taskid, clock, ttl, now);
175      break;
176     default:
177      THIS_SHOULD_NEVER_HAPPEN;
178      ret = FAIL;
179      break;
180    }

tm_execute_remote_command()の処理の概要を説明します。 

  51 static int tm_execute_remote_command(zbx_uint64_t taskid, int clock, int ttl, int now)
  52 {
...
  71  task = zbx_tm_task_create(0, ZBX_TM_TASK_REMOTE_COMMAND_RESULT, ZBX_TM_STATUS_NEW, time(NULL), 0, 0);
...
116   if (SUCCEED != (ret = zbx_script_execute(&script, &host, &info, error, sizeof(error))))
117    task->data = zbx_tm_remote_command_result_create(parent_taskid, ret, error);
118   else
119    task->data = zbx_tm_remote_command_result_create(parent_taskid, ret, info);
...
125   DBbegin();
126
127   if (NULL != task)
128   {
129    zbx_tm_save_task(task);
130    zbx_tm_task_free(task);
131   }
132
133   DBexecute("update task set status=%d where taskid=" ZBX_FS_UI64, ZBX_TM_STATUS_DONE, taskid);
134
135   DBcommit();
136
137   return ret;
138 }

l.71のzbx_tm_task_create()により初期データを作成します。二番目のtype引数にZBX_TM_TASK_REMOTE_COMMAND_RESULTを指定しています。

これは、l.129でコールされるzbx_tm_save_task()にて効果を持ちます。

l.116にて実装にコマンドを実行した後に、zbx_tm_save_task()をコールします。ここで上記のtypeにより、結果がtask_remote_command_resultテーブルへ挿入されることになります。

最後に、taskテーブルの情報をアップデートして終了します。

task_remote_command_resultテーブルへ収められたコマンド結果はどうなるか?というと、最初のプロキシのproxy_data_sender()に帰着します。
ここで、zbx_tm_get_remote_tasks()からデータを取得し、サーバへ返送されます。

 まとめ

以上で、二回に渡りリモートコマンドがプロキシで実行する実装の解説を行いました。
taskテーブルは使用していますが、サーバ側ではTask Managerがあまり関与せず、プロキシ側ではTask Managerに頼った実装となっていることが理解できたと思います。 リモートコマンド(zbx-tl-021用)

 

関連記事

注意事項

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

 

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

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

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

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