使うならソースくらい読まないと。
今回の目標は「MISC_CHECKがどのように行われるか(timeout関連をきちっと知るため)」
ダウンロード
ここからダウンロードする。
構造
ソースを展開して構造をみる。
$ pwd /usr/local/src/keepalived-1.2.9 $ du 0 ./bin 8 ./doc/man/man1 32 ./doc/man/man5 8 ./doc/man/man8 48 ./doc/man 200 ./doc/samples 408 ./doc 224 ./genhash 424 ./keepalived/check 120 ./keepalived/core 32 ./keepalived/etc/init.d 8 ./keepalived/etc/keepalived 40 ./keepalived/etc 352 ./keepalived/include 32 ./keepalived/libipvs-2.4 120 ./keepalived/libipvs-2.6 536 ./keepalived/vrrp 1632 ./keepalived 624 ./lib
中心はkeepalivedとlib。genhashは今回は無視。
目標からすると、keepalived/core、keepalived/checkあたりを眺めれば良さげ。
軽ーく眺めただけだが、内部はマルチスレッド構成で、VRRP関連の処理や各種チェックを管理するスレッドが走る。
check関連も、詳しくみればチェックタスクをcheckers_queueに溜めて、それをスケジューラーが適宜起動するなど、本気で辿るにはそれなりの時間がかかりそうな作りになっている。が、今知りたいのは実際のMISC_CHECKがどうなっているかなので細かいことは気にしないことにする。
MISC_CHECK
MISC_CHECKの起動シーケンスをしめす。
- daley_loopで指定した周期で、misc_check_thread()@keepalived_check/check_misc.cが起動される。
- 起動したmisc_check_thread()は子プロセスをforkする。
- forkされた子プロセスはsystem_call()@lib/notify.c => システムコールsystem()でMISC_PATHのスクリプトを実行する。
- 親プロセス側=keepalived本体ではmisc_timeoutのタイマーがセットされる。
子プロセスでスクリプトを実行するので、もしもスクリプトが致命的な動作をしても、keepalived本体には影響がない(はず)。
int misc_check_thread(thread_t * thread) @ keepalived_check/check_misc.c { … 略 … thread_add_timer(thread->master, misc_check_thread, checker, checker->vs->delay_loop); /* * 子プロセスのフォーク */ pid = fork(); … 略 … /* In case of this is parent process */ if (pid) { long timeout; /* * 親プロセス側では、misc_timeoutのタイマー設定 */ timeout = (misck_checker->timeout) ? misck_checker->timeout : checker->vs->delay_loop; thread_add_child(thread->master, misc_check_child_thread, checker, pid, timeout); return 0; } /* Child part */ … 略 … /* * 子プロセス側では、misc_pathのスクリプト実行 */ status = system_call(misck_checker->path); if (status < 0 || !WIFEXITED(status)) status = 0; /* Script errors aren't server errors */ else status = WEXITSTATUS(status); exit(status); } int system_call(char *cmdline) @ lib/notify.c { int retval; retval = system(cmdline); if (retval == 127) { /* couldn't exec command */ log_message(LOG_ALERT, "Couldn't exec command: %s (ret=%d)", cmdline, retval); } else if (retval == -1) { /* other error */ log_message(LOG_ALERT, "Error exec-ing command: %s (ret=%d)", cmdline, retval); } return retval; }
ここで、misc_pathで実行したスクリプトがmisc_timeoutよりも長い時間がかかってしまった場合、misc_check_child_thread()@keepalived/check/check_misc.c スレッドが子プロセスをkillする。
int misc_check_child_thread(thread_t * thread) @ keepalived/check/check_misc.c { … 略 … if (thread->type == THREAD_CHILD_TIMEOUT) { pid_t pid; pid = THREAD_CHILD_PID(thread); if (svr_checker_up(checker->id, checker->rs)) { log_message(LOG_INFO, "Misc check to [%s] for [%s] timed out" , inet_sockaddrtos(&checker->rs->addr) , misck_checker->path); } /* * 起動した子プロセスをkill */ kill(pid, SIGTERM); thread_add_child(thread->master, misc_check_child_timeout_thread, checker, pid, 2); return 0; } … 略 …
ただし、MISC_CHECKの注意点に書かれているとおり、子プロセスが(system()で)起動したスクリプトは終了しない。
例えば、“”LVS + keepalived の設定 4で作ったmysql-check.shで”SELECT 100”を”SELECT sleep(1000)”にすると、mysql-check.shを起動した子プロセスはmisc_timeout秒後にkillされるが、mysql-check.shスクリプトは(1000秒間)動き続ける。
というわけで、misc_pathに書くヘルスチェックスクリプトは、確実に終了するように書かなければならない。
今回はここまで。気が向いたらvrrpによるMASTER electionを辿ってみるつもり。