HEARTBEATS

Apache HTTP Serverのgraceful stop/restartを理解する

   

こんにちは、去年の8月に入社しましたMSP事業部エンジニアリンググループの鈴木です。

本記事は、Apache HTTP Server(以降「Apache」と略す)のgraceful stop/restart(以降、2つを指す場合「graceful」と略す)について調査・理解したことをまとめたものになります。具体的には以下について調査しました[※1]。

  • gracefulの概要、通常のstop/restartとの違いおよびユースケース
  • systemd(systemctl)でgracefulを実行する方法
  • gracefulを実行する3つのコマンドの動作や関係性
  • gracefulを実行するコマンドの動作確認(ドキュメントの裏取り)
  • 付録:graceful関連のソースコード解析(理解できている範囲のみ)

なお、今回は私個人の学びの一環としてgracefulの基本的なことから調査しましたので、内容としてはApache初学者向けになりました。ただし、Linuxの知識や経験はある前提で書いていますので、Linuxについて基本的なことは説明していません。

[※1]:調査・検証環境には、AlmaLinux8.7(カーネル4.18.0-425.3.1.el8.x86_64)およびApache2.4.37(MPM:event)を使用します。

gracefulとは

以降、gracefulの概要、通常のstop/restartとの違いおよびユースケースについて記載します。

graceful stop(緩やかな停止)の概要

公式ドキュメントによると次のとおりです。

WINCH や graceful-stop シグナルを受け取ると、 親プロセスは子プロセスに現在処理中のリクエストの後に終了する (あるいは処理中のものが何もなければ直ちに終了する) ようにアドバイスします。 その後親プロセスは PidFile を削除し、ポートでの Listen を全て停止します。 親プロセスはどの子プロセスがリクエスト処理中かを監視し続けています。 全ての子プロセスが終了するか GracefulShutdownTimeout で設定した時間が過ぎると、親プロセスも終了します。 タイムアウトに達した場合、残りの子プロセスには TERM シグナルが送信され強制的に終了されます。

引用元:公式ドキュメント - Apache HTTP Server の停止と再起動(緩やかな停止)

graceful restart(緩やかな再起動)の概要

公式ドキュメントによると次のとおりです。

親プロセスは USR1 あるいは graceful シグナルを受け取ると、子プロセスに現在のリクエストの処理の後に終了する (あるいは何もしていなければすぐに終了する) ように助言します。 親プロセスは設定ファイルを再読込して、ログファイルを開き直します。 子プロセスが徐々になくなるに従って、 新しい世代の設定による子プロセスに置き換えていきます。 そして、これらが新たなリクエストに即座に応答し始めます。

引用元:公式ドキュメント - Apache HTTP Server の停止と再起動(緩やかな再起動)

通常のstop/restartとの違い

gracefulと通常のstop/restartの主な違いは次のとおりです。最も重要な違いは子プロセスの状態を考慮するかどうかです。

  • 実行コマンド
  • 処理のトリガーとなるシグナル
  • 子プロセスの状態を考慮するかどうか

これらの違いを表にまとめます。

 対応コマンド  トリガーのシグナル  子プロセスの状態を考慮するかどうか 
 graceful stop  apachectl -k graceful-stop  SIGWINCH  する 
 graceful restart  apachectl -k graceful  SIGUSR1  する 
 通常のstop  apachectl -k stop  SIGTERM  しない 
 通常のrestart  apachectl -k restart  SIGHUP  しない 

なお、公式ドキュメントにはgraceful-stopシグナル、gracefulシグナルなどの文言が登場しますが、記載を省略しています(以降でも省略)。このようなシグナルはLinux標準シグナルやリアルタイムシグナルには存在せず、混乱を避けるためです。おそらくApache特有の表現だと考えます。

参考:公式ドキュメント - Apache HTTP Server の停止と再起動(急な停止・急な再起動)

ユースケース

以上を踏まえたうえで、gracefulと通常のstop/restartのユースケースを記載します。

 ユースケース  補足 
 graceful stop/restart  基本はこちらを使用  gracefulはリクエスト処理中にApacheプロセスの停止・再起動をしないため、ユーザ影響を最小限に抑えられる。基本はこちらを使用するのが望ましい 
 通常のstop/restart  gracefulを実行したが長時間処理され終わらない場合や、サーバリソースが枯渇傾向にあるなど、即座にApacheプロセスの停止や再起動が必要な場合はこちらを使用  通常のstop/restartではリクエスト処理中でも即座にApacheプロセスの停止や再起動が行われ、ユーザに影響を及ぼす可能性があるため基本は使用を避けるべき。しかし、リソースの大量消費によりApacheプロセス以外の処理に影響を与える可能性やサーバダウンの恐れがある状況では使用した方が良いケースもある(使用した方が良いかどうかは一概に言えず状況を見て判断する) 

systemdでgracefulを実行する方法

ここでは、systemd(systemctl)でgracefulを実行する方法および調査内容について記載します。

公式ドキュメントでは、gracefulの実行コマンドとしてapachectlが使用されていますが、Red Hat Enterprise Linux 8および互換ディストリビューション(本検証環境)ではサービス停止や再起動にsystemctlを使用するのが一般的です。

結論

次のコマンドから、gracefulを実行できます。特別な設定やオプションは不要です。

  • graceful stopを実行する:systemctl stop httpd.service
  • graceful restartを実行する:systemctl reload httpd.service

また、systemctlで通常のstop/restartを実行する場合は次のとおりです。

  • 通常のstopを実行する:systemctl kill --kill-who=main httpd.service
  • 通常のrestartを実行する:systemctl kill --kill-who=main --signal=HUP httpd.service

調査1:systemdのユニットファイル(httpd.service)

systemdのユニットファイル(httpd.service)の関係箇所を記載します。

まずはExecReloadの行です。

# systemctl cat httpd.service
--- 略 ---
ExecReload=/usr/sbin/httpd $OPTIONS -k graceful
--- 略 ---

上記からはsystemctl reload httpd.serviceを実行するとgraceful restartが実行されると解釈できます。ExecReloadはsystemdのreloadに関する定義で、引数部分はreloadで実行されるコマンドです。後述しますが引数のhttpd $OPTIONS -k gracefulはgraceful restartを実行するコマンドです。

続いてKillSignalの行です。

--- 略 ---
# Send SIGWINCH for graceful stop
KillSignal=SIGWINCH
--- 略 ---

上記からはsystemctl stop httpd.serviceで、graceful stopが実行されると解釈できます。ExecStopの定義がない場合(上記のユニットファイルには定義なし)、プロセスはKillSignalのシグナルにより終了させられます。先述のとおり、SIGWINCHはgraceful stopを実行するトリガーです。

参考:systemd.service - Man Page(ExecStop=)

調査2:HTTPD.SERVICE(8)

systemctlとgracefulとの関連性については、HTTPD.SERVICE(8)にも記載されています。

When running systemctl reload httpd.service, a graceful restart is used, which sends a signal to the httpd parent process to reload the configuration and re-open log files.

--- 略 ---

Similarly, a graceful stop is used when systemctl stop httpd.service is run, which terminates the server only once active connections have been processed.

また、systemctlで通常のstop/restartを行う場合についても記載されています。

To "ungracefully" stop the server without waiting for requests to complete, use systemctl kill --kill-who=main httpd; similarly to "ungracefully" reload the configuration, use systemctl kill --kill-who=main --signal=HUP httpd.

引用元:httpd.service - Man Page(Reloading and stopping the service)

apachectl/systemctl/httpdコマンドの関係性

3つのコマンドは動作が似ており、混乱するため、次のように各コマンドの動作や関係性を整理します。

  • apachectl [-k] graceful-stop | gracefulの動作:systemctlhttpdを実行するラッパー[※2]
  • systemctl stop | reload httpd.serviceの動作:httpdを実行するか、SIGWINCHを送信する
  • httpd -k graceful-stop | gracefulの動作:SIGWINCHおよびSIGUSR1を送信する

これらの関係性を図示します。

cmd_rel_2.png

[※2]:apachectlはシェルスクリプトですので、ソースコードは直接読めます。実行結果のみ確認する場合は、bash -x apachectl [-k] xxxxxが便利です。

gracefulの動作確認(シグナル送信)

ここまでの情報をもとに、実際の動作を確認します。調査・理解したことが正しいかどうかを知る目的もありますが、期待した結果にならないこと(バグ、ドキュメントの不備など)もあります。

しかし動作確認と言っても範囲が広いため、ここではコマンド実行時のシグナル送信に着目します。確認対象のコマンドはhttpdsystemctlの2つだけとします。前述の図にあるように、apachectlhttpdsystemctlを実行するラッパーのようなもので、確認する意味はないと考えます。

httpdの確認

シグナル送信を確認するため、コマンド実行時にstraceでkillシステムコールをトレース(-e kill)します。

killシステムコールはシグナルを送信するのに使用されるため、コマンド実行時にシグナルが送信された場合、killシステムコールの引数に送信先のPIDとシグナル名が出力されます。

# strace -e kill httpd -k graceful
kill(35229, 0)                          = 0
kill(35229, SIGUSR1)                    = 0
--- 略 ---

# strace -e kill httpd -k graceful-stop
kill(35229, 0)                          = 0
kill(35229, SIGWINCH)                   = 0

上記から、Apacheの親プロセス(35229)への当該シグナル送信が確認できます。

systemctlの確認

httpdと同様に確認します。

ただし、systemctlはsystemd本体を制御するコマンドのため、systemctl自体をトレースしても期待する結果は得られません。実際の処理はsystemd本体が行いますので、straceでsystemd本体(PID1)にアタッチ(-p 1)した状態で別ターミナルにてsystemctlを実行します。

また、reloadの場合はsystemd本体からの子プロセスとしてhttpdが実行されるので、子プロセスをトレース(-f)します。子プロセスを生成するcloneシステムコール、その子プロセスを特定のプログラム(今回はhttpd)に置き換えるexecveシステムコールもトレース(-e clone,execve)します。

以上を踏まえ、それぞれ確認します。まずは次のコマンド実行後、別ターミナルにてsystemctl reload httpd.serviceを実行します。

# strace -f -e clone,execve,kill -p 1
--- 略 ---
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f03a14542d0) = 8090
strace: Process 8090 attached
[pid  8090] execve("/usr/sbin/httpd", ["/usr/sbin/httpd", "-k", "graceful"], 0x5572a0f906a0 /* 5 vars */) = 0
[pid  8090] kill(7869, 0)               = 0
[pid  8090] kill(7869, SIGUSR1)         = 0

reload時は子プロセス(PID 8090)のhttpd経由でSIGUSR1が送信されていることを確認できます。 同様に、systemctl stop httpd.serviceも確認します。

# strace -e kill -p 1
--- 略 ---
kill(7869, SIGWINCH)                    = 0

stop時は子プロセスをトレース(-f)していないため、systemd本体から直接SIGWINCHが送信されたことを確認できます。

gracefulの動作確認(シグナル受信)

シグナル送信と同様に受信も確認します。

シグナル受信を確認するには、straceでApacheの親PIDにアタッチ(-p xxxx)し、その状態で別ターミナル(termB)からコマンドを実行します[※3]。

確認観点は2つです。

  1. --- SIGXXXXに出力されるシグナル名(受信したシグナルを示す)
  2. si_pid=xxxxxに出力されるPID(シグナル送信元のPIDを示す)

[※3]:混乱を避けるため(またシグナル受信確認には不要なため)、straceの絞り込み(-e xxx)をしていませんが、このような状態でApacheにアタッチすると大量に出力されます。

httpdの確認

上述のように、次のコマンド実行後、別ターミナル(termB)にて、httpd -k gracefulを実行します。

[termA]# strace -p 8613
--- 略 ---
--- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_USER, si_pid=23752, si_uid=0} ---

同様に、httpd -k graceful-stopも確認します。

[termA]# strace -p 8613
--- 略 ---
--- SIGWINCH {si_signo=SIGWINCH, si_code=SI_USER, si_pid=23968, si_uid=0} ---

以上から、各コマンド実行時に当該シグナルの受信を確認できます。また、次の別ターミナル(termB)からコマンド実行時のPIDと上記のsi_pidが一致していることも確認できます。

[termB]# httpd -k graceful &
[1] 23752

[termB]# httpd -k graceful-stop &
[2] 23968

systemctlの確認

httpdと同様に確認します。次のコマンド実行後、別ターミナル(termB)にてsystemctl reload httpdを実行します。

[termA]# strace -e kill -p 24802
--- 略 ---
--- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_USER, si_pid=25252, si_uid=0} ---

同様にsystemctl stop httpdも確認します。

[termA]# strace -e kill -p 24802
--- 略 ---
--- SIGWINCH {si_signo=SIGWINCH, si_code=SI_USER, si_pid=1, si_uid=0} ---

以上からコマンド実行時に当該シグナルの受信を確認できます。また、si_pidに着目すると、それぞれ252521であることが確認できます。次のとおり、前者はsystemd本体から実行されたhttpdのPIDであることが確認できます(systemd本体にアタッチし、別ターミナル(termB)でsystemctl reload httpdを実行した結果)。後者はPID1なので、systemd本体からのシグナル送信です。

[termC]# strace -f -e kill,clone,execve -p 1
--- 略 ---
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLDstrace: Process 25252 attached
, child_tidptr=0x7ff7cea642d0) = 25252
[pid 25252] execve("/usr/sbin/httpd", ["/usr/sbin/httpd", "-k", "graceful"], 0x55ef1f02fd30 /* 5 vars */) = 0
--- 略 ---
[pid 25252] kill(24802, SIGUSR1)        = 0

gracefulの動作確認(ログ確認・検証)

シグナル送受信とは別の観点で確認・検証した内容について記載します。

ログの確認

gracefulの痕跡は、Apacheのエラーログへnoticeレベルで記録されます。

たとえば、graceful restartおよびgraceful stopを実行すると次のようになります。受信したシグナル名や、それによって処理されたgraceful処理について記録されます。

# cat /var/log/httpd/error_log (日付部分削除)
--- 略 ---
[mpm_event:notice] [pid 2229:tid 140214461652416] AH00493: SIGUSR1 received.  Doing graceful restart
--- 略 ---
[mpm_event:notice] [pid 4175:tid 140585775152576] AH00492: caught SIGWINCH, shutting down gracefully

検証:シグナルをkillコマンドで直接送信する

次のとおり、公式ドキュメントにはシグナルを直接送信してgracefulを実行しても問題ない旨の記載があります。

Apache HTTP Server を停止したり再起動したりするためには、実行されている httpd プロセスにシグナルを送る必要があります。 シグナルを送るには二つの方法があります。 一つ目はプロセスに直接シグナルを送る unix の kill コマンドを使用する方法です。 システムを見ればたくさんの httpd が 実行されているのに気が付くでしょうが、シグナルを送るのは 親プロセスだけで、それ以外の個々のプロセスには シグナルを送らないで下さい。その親プロセスの pid は PidFile に書かれています。これはつまり、親以外のプロセスに シグナルを送る必要すらない、ということです。 親プロセスに送ることができる 4 種類のシグナルがあります: TERM, HUP, USR1, WINCH です。これらの説明については続きをご覧下さい。

引用元:公式ドキュメント - Apache HTTP Server の停止と再起動(イントロダクション)

以降、検証としてApacheの親プロセスへ、graceful restartのシグナル(SIGUSR1)をkill経由で送信します。細かい動作確認には、ソースコード解析やデバッグが必要ですが、ここでは次の2つの観点で確認します。

  1. graceful restartに対応するログが出力されるかどうか
  2. restartすると子プロセス(PID)が入れ替わるので、それがされるかどうか

まずは、既存ApacheプロセスのPIDを確認します。

# ps -eo pid,ppid,args | grep httpd
    879       1 /usr/sbin/httpd -DFOREGROUND
   2173     879 /usr/sbin/httpd -DFOREGROUND
   2174     879 /usr/sbin/httpd -DFOREGROUND
   2175     879 /usr/sbin/httpd -DFOREGROUND
   2176     879 /usr/sbin/httpd -DFOREGROUND

続いて、killでgraceful restartのシグナルをApacheの親プロセス(879)へ送信します。

# kill -USR1 879

graceful restartのログを確認します(日付部分削除)。

# cat /var/log/httpd/error_log
--- 略 ---
[mpm_event:notice] [pid 879:tid 140559072816576] AH00493: SIGUSR1 received.  Doing graceful restart
--- 略 ---
[core:notice] [pid 879:tid 140559072816576] AH00094: Command line: '/usr/sbin/httpd -D FOREGROUND'

正常に記録されています。最後に子プロセス(PID)が入れ替わっているかどうか確認します。

# ps -eo pid,ppid,args | grep httpd
    879       1 /usr/sbin/httpd -DFOREGROUND
   2467     879 /usr/sbin/httpd -DFOREGROUND
   2468     879 /usr/sbin/httpd -DFOREGROUND
   2469     879 /usr/sbin/httpd -DFOREGROUND
   2470     879 /usr/sbin/httpd -DFOREGROUND

子プロセスの入れ替わりも確認できます。ドキュメントのとおり、特に問題はないようです。

要点まとめ

ここまでの内容を要点に絞ってまとめます。

graceful全般について

gracefulは、子プロセスの状態を考慮(リクエスト処理を監視)してApacheプロセスの停止および再起動をします。通常のstop/restartよりもユーザへの影響を最小限に抑えられるため、基本はgracefulを使用した方がよいです。

gracefulを実行するコマンドについて

gracefulを実行するには3つ(apachectl/systemctl/httpd)のコマンドのうち、いずれかを使用します。3つのコマンドは密接に関係しており、調査・検証した範囲内では、いずれも最終的な結果は同じになります。

gracefulの仕組みについて

上記のコマンドは、いずれも処理の過程でApache(親プロセス)へシグナルを送信します。Apache(親プロセス)がシグナル(SIGWINCHおよびSIGUSR1)を受信するとgracefulの処理が始まり、その痕跡はApacheのエラーログにnoticeレベルで記録されます。

また、このような仕組みであるため、killにてApache(親プロセス)へ直接シグナル送信してもgracefulは動作します(公式ドキュメントにもその旨記載されています)。

最後に

以上の調査で、gracefulの基本は理解できました。

さらなる調査として、Apacheソースコードの解析やGDBでのデバッグも行いましたが、時間の都合上、gracefulの具体的な処理内容を理解するまでには至りませんでした。

しかし、現時点でも何らかの調査の起点として使えそうなため、理解できていることを中心に付録として記載します。

  • 理解できていること:gracefulが処理されるまでの流れ
  • 理解できていないこと:gracefulの具体的な処理内容(細かい処理までは追いきれていない)

付録:Apacheソースコード解析

以降のソースコード解析では、本検証環境と同じApache2.4.37を使用します。 また、ソースコードの記載は、読み進める上で重要な箇所に限定します。

各種シグナルハンドラー

シグナルの受信をトリガーにgracefulが実行されるということは、対応するシグナルハンドラーがあります。Apacheではapr_signal()を使い、ハンドラーを登録します。第一引数に受信シグナル、第二引数にハンドラーが指定できます。

Apacheのソースコードを対象に検索すると次の箇所がヒットします。

# grep -r "apr_signal(" *
--- 略 ---
os/unix/unixd.c:    apr_signal(AP_SIG_GRACEFUL_STOP, sig_term);
--- 略 ---
os/unix/unixd.c:        apr_signal(AP_SIG_GRACEFUL, sig_restart);

次のとおり、AP_SIG_GRACEFUL*は、graceful関連シグナルの別名として定義されています。

# grep -r AP_SIG_GRACEFUL *
include/mpm_common.h:#define AP_SIG_GRACEFUL SIGUSR1
--- 略 ---
include/mpm_common.h:#define AP_SIG_GRACEFUL_STOP SIGWINCH
--- 略 ---

以上から、次のように解釈できます。

  • SIGUSR1受信時はsig_term()が呼び出される
  • SIGWINCH受信時はsig_restart()が呼び出される

それぞれの関数(シグナルハンドラー)は以下です。

// os/unix/unixd.c
static void sig_restart(int sig)
{
    --- 略 ---
    retained_data->restart_pending = 1;
    --- 略 ---
}
--- 略 ---
static void sig_term(int sig)
{
    --- 略 ---
    retained_data->shutdown_pending = 1;
    --- 略 ---
}

シグナルハンドラーの主な仕事はステータス変更だと分かります。

シグナルハンドラーから先の処理

シグナルハンドラーで変更されるステータスのうち、restart_pendingshutdown_pendingは、server_main_loop()のループ判定で使用されています。

// server/mpm/event/event.c
static void server_main_loop(int remaining_children_to_start, int num_buckets)
{
    --- 略 ---
    while (!retained->mpm->restart_pending && !retained->mpm->shutdown_pending) {
        ap_wait_or_timeout(&exitwhy, &status, &pid, pconf, ap_server_conf);
        --- 略 ---
    }
}

次のpstackから確認できますが、Apacheプロセス(29487)は、server_main_loop()ap_wait_or_timeout()から先の処理で待ち受けしています。

# pstack 29487
#0  0x00007f9bfba1b7db in select () from target:/lib64/libc.so.6
#1  0x00007f9bfc10a919 in apr_sleep () from target:/lib64/libapr-1.so.0
#2  0x0000563d3355db2d in ap_wait_or_timeout (status=status@entry=0x7ffd2ec87618, exitcode=exitcode@entry=0x7ffd2ec8761c, ret=ret@entry=0x7ffd2ec87620, p=, s=) at mpm_common.c:201
#3  0x00007f9bf09dd041 in server_main_loop (num_buckets=1, remaining_children_to_start=0) at event.c:2904
#4  event_run (_pconf=, plog=, s=) at event.c:3082
#5  0x0000563d3355d13e in ap_run_mpm (pconf=0x563d343b3a18, plog=0x563d343e0c38, s=0x563d343dccc0) at mpm_common.c:94
#6  0x0000563d33555733 in main (argc=, argv=) at main.c:819

そしてシグナルを受信し、restart/shutdown_pendingのうち、いずれかが真になると、このserver_main_loop()を抜け、event_run()に戻ります。

// server/mpm/event/event.c
static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
{
    --- 略 ---
    server_main_loop(remaining_children_to_start, num_buckets);
    --- 略 ---

event_run()に戻ると、ステータスに応じて次のように処理が分岐します。

  • 通常のstop処理
  • graceful stop処理
  • 通常のrestart処理
  • graceful restart処理

以降、上記処理のソースコードを記載します。省略すると流れを追いづらいため、通常のstop/restartの処理も記載します。

しかし、ここから先は大部分の処理が追えておらず、「理解できていないこと」にあたりますので特に説明はしません。ただし、ap_log_error()の処理は理解しやすいです。先述の「ログの確認」と比較すると分かりますが、(問題がなければ)ここからApacheのエラーログへ出力されると解釈できます。

通常のstop処理

    if (retained->mpm->shutdown_pending && retained->mpm->is_ungraceful) {
        /* Time to shut down:
         * Kill child processes, tell them to call child_exit, etc...
         */
        for (i = 0; i < num_buckets; i++) {
            ap_mpm_podx_killpg(all_buckets[i].pod, active_daemons_limit,
                               AP_MPM_PODX_RESTART);
        }
        ap_reclaim_child_processes(1, /* Start with SIGTERM */
                                   event_note_child_killed);

        if (!child_fatal) {
            /* cleanup pid file on normal shutdown */
            ap_remove_pid(pconf, ap_pid_fname);
            ap_log_error(APLOG_MARK, APLOG_NOTICE, 0,
                         ap_server_conf, APLOGNO(00491) "caught SIGTERM, shutting down");
        }

        return DONE;
    }

graceful stop処理

    if (retained->mpm->shutdown_pending) {
        /* Time to gracefully shut down:
         * Kill child processes, tell them to call child_exit, etc...
         */
        int active_children;
        int index;
        apr_time_t cutoff = 0;

        /* Close our listeners, and then ask our children to do same */
        ap_close_listeners();
        for (i = 0; i < num_buckets; i++) {
            ap_mpm_podx_killpg(all_buckets[i].pod, active_daemons_limit,
                               AP_MPM_PODX_GRACEFUL);
        }
        ap_relieve_child_processes(event_note_child_killed);

        if (!child_fatal) {
            /* cleanup pid file on normal shutdown */
            ap_remove_pid(pconf, ap_pid_fname);
            ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00492)
                         "caught " AP_SIG_GRACEFUL_STOP_STRING
                         ", shutting down gracefully");
        }

        if (ap_graceful_shutdown_timeout) {
            cutoff = apr_time_now() +
                     apr_time_from_sec(ap_graceful_shutdown_timeout);
        }

        /* Don't really exit until each child has finished */
        retained->mpm->shutdown_pending = 0;
        do {
            /* Pause for a second */
            apr_sleep(apr_time_from_sec(1));

            /* Relieve any children which have now exited */
            ap_relieve_child_processes(event_note_child_killed);

            active_children = 0;
            for (index = 0; index < retained->max_daemons_limit; ++index) {
                if (ap_mpm_safe_kill(MPM_CHILD_PID(index), 0) == APR_SUCCESS) {
                    active_children = 1;
                    /* Having just one child is enough to stay around */
                    break;
                }
            }
        } while (!retained->mpm->shutdown_pending && active_children &&
                 (!ap_graceful_shutdown_timeout || apr_time_now() < cutoff));

        /* We might be here because we received SIGTERM, either
         * way, try and make sure that all of our processes are
         * really dead.
         */
        for (i = 0; i < num_buckets; i++) {
            ap_mpm_podx_killpg(all_buckets[i].pod, active_daemons_limit,
                               AP_MPM_PODX_RESTART);
        }
        ap_reclaim_child_processes(1, event_note_child_killed);

        return DONE;
    }

graceful restart処理

    if (!retained->mpm->is_ungraceful) {
        ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00493)
                     AP_SIG_GRACEFUL_STRING
                     " received.  Doing graceful restart");
        /* wake up the children...time to die.  But we'll have more soon */
        for (i = 0; i < num_buckets; i++) {
            ap_mpm_podx_killpg(all_buckets[i].pod, active_daemons_limit,
                               AP_MPM_PODX_GRACEFUL);
        }

        /* This is mostly for debugging... so that we know what is still
         * gracefully dealing with existing request.
         */

    }

通常のrestart処理

    else {
        /* Kill 'em all.  Since the child acts the same on the parents SIGTERM
         * and a SIGHUP, we may as well use the same signal, because some user
         * pthreads are stealing signals from us left and right.
         */
        for (i = 0; i < num_buckets; i++) {
            ap_mpm_podx_killpg(all_buckets[i].pod, active_daemons_limit,
                               AP_MPM_PODX_RESTART);
        }

        ap_reclaim_child_processes(1,  /* Start with SIGTERM */
                                   event_note_child_killed);
        ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00494)
                     "SIGHUP received.  Attempting to restart");
    }

    active_daemons = 0;

    return OK;
}

参考

株式会社ハートビーツの技術情報やイベント情報などをお届けする公式ブログです。



ハートビーツをフォロー

  • Twitter:HEARTBEATS
  • Facebook:HEARTBEATS
  • HATENA:HEARTBEATS
  • RSS:HEARTBEATS

殿堂入り記事