BLOG
2018 年 01 月 25 日
Zabbix 3.2以降の新機能解説(Zabbix 4.0を見据えて) その9 - プロキシでのリモートコマンドの実行 (その2)
こんにちは、MIRACLE ZBX サポートを担当している花島タケシです。 今回は Task Manager プロセスを利用した最後のサービスについての後編の解説となります。
プロキシでのリモートコマンドの実行(その 2)
今回は Task Manager プロセスを利用した最後のサービスについての後編の解説となります。
参考
Remote command support through proxies
前回は、Escalator プロセスが通常のアラート処理と異なり、alert テーブルへ情報を挿入せずに、リモートコマンドをプロキシで実行するために task_remote_command テーブルへと挿入しているところまで解説を行いました。
今回はそのデータをどのようにプロキシに渡し、プロキシがどうやって実行しているか?ということを解説してきます。
アクティブプロキシとパッシブプロキシ
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 に頼った実装となっていることが理解できたと思います。
関連記事
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
注意事項
- 本ドキュメントの内容は、予告なしに変更される場合があります。
- 本ドキュメントは、限られた評価環境における検証結果をもとに作成しており、全ての環境での動作を保証するものではありません。
- 本ドキュメントの内容に基づき、導入、設定、運用を行なったことにより損害が生じた場合でも、当社はその損害についての責任を負いません。あくまでお客さまのご判断にてご使用ください。