keepalived ソースコードリーディング 1

使うならソースくらい読まないと。

今回の目標は「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の起動シーケンスをしめす。

  1. daley_loopで指定した周期で、misc_check_thread()@keepalived_check/check_misc.cが起動される。
  2. 起動したmisc_check_thread()は子プロセスをforkする。
  3. forkされた子プロセスはsystem_call()@lib/notify.c => システムコールsystem()でMISC_PATHのスクリプトを実行する。
  4. 親プロセス側=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を辿ってみるつもり。