4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

KongのGraceful Shutdownの仕組み

Last updated at Posted at 2025-12-11

本記事は「Kong Advent Calendar 2025」の12日目のエントリとして、KongのGraceful Shutdownの仕組みについて解説する。

Kong Gatewayを使っていると、リクエスト・レスポンスをKong Gatewayが捌いている時に停止処理を実行すると、どのような振る舞いになるか気になる人は多いと思う。
今回はこの停止処理時の振る舞いを確認する。

停止処理の実装

ここではOSS版のkong quitのコードを用いて解説する。
https://github.com/Kong/kong/blob/master/kong/cmd/quit.lua

ざっくりフローを示すと以下のようになる。

kongの停止コマンド(kong quit)には--waitオプションと--timeoutオプションがある。
--waitオプションが指定された場合、Kongプロセスの停止を開始するまでの待機時間となる。
一方で--timeoutオプションはKongプロセスの停止処理を開始してから停止するまでの待機する最大時間となる。
そのため、最初の分岐では--waitオプションが指定されているかどうかで処理が分岐する。

--waitオプションが指定された場合、指定された時間を待ち、その後SIGQUITをKongプロセスに送信する。
ちなみに何故SIGTERMではなくSIGQUITかというと、Kongはnginxをベースに開発されており、nginxでは以下のようにシグナル処理が実装されている。(参考:Controlling nginx

シグナル名 説明
TERM, INT 即時終了 (fast shutdown)
QUIT 正常終了 (graceful shutdown)
HUP 設定変更、変更されたタイムゾーンへの追従 (FreeBSDとLinuxのみ)、新しい設定での新しいワーカープロセスの開始、古いワーカープロセスの正常終了
USR1 ログファイルの再オープン
USR2 実行ファイルのアップグレード
WINCH ワーカープロセスの正常終了

ということで、graceful shutdown(処理中のリクエストを完了させてから安全に停止させるシャットダウン)を実現するためにはSIGQUITをプロセスに送る必要がある。
SIGQUIT発行後、--timeoutで指定された時間(デフォルトで10秒)までKongプロセスが停止するのを待って、それでも停止しないようならSIGTERMを送信して強制終了させる流れとなっている。

ちなみに、SIGQUIT中にKong Gatewayで扱っているリクエストは良しなに処理されるが、その処理中にリクエストを新規に受け付けられるかと言うと、もう受付は行っていない。
この時にKong Gatewayにリクエストを送ると以下のような感じで接続エラーとなる。

curl: (7) Failed to connect to 192.168.0.169 port 8000 after 172 ms: Couldn't connect to server

検証(kong quitの場合)

実際に期待値通りに動作するか確認する。
なお検証はRHEL上にRPMでパッケージをインストールした環境で行った。

検証にあたり、以下の設定をKongに適用する。

_format_version: "3.0"
services:
- host: httpbin.org
  name: httpbin-service
  path: /
  port: 443
  protocol: https
  routes:
  - name: httpbin-route
    paths:
    - /httpbin
    plugins:
    - config:
        access:
        - ngx.sleep(10)
      enabled: true
      name: pre-function

Pre Functionプラグインを利用して、リクエスト受信時に10秒間スリープするようにしている。
これにより、Kongがリクエストを処理している最中に停止処理を実行した場合の挙動を確認できる。

実際にリクエストを送っている最中にkong quitコマンドを実行してみる。

curl -i localhost:8000/httpbin/ip

実行した時のログ(error.log)は以下のようになった。
なお、ログを見やすくするためにnginx_worker_processesを1に設定している。

2025/12/05 02:24:18 [notice] 549163#0: signal 3 (SIGQUIT) received from 549190, shutting down
2025/12/05 02:24:18 [notice] 549164#0: gracefully shutting down
2025/12/05 02:24:28 [notice] 549164#0: exiting
2025/12/05 02:24:28 [notice] 549164#0: exit
2025/12/05 02:24:28 [notice] 549163#0: signal 17 (SIGCHLD) received from 549164
2025/12/05 02:24:28 [notice] 549163#0: worker process 549164 exited with code 0
2025/12/05 02:24:28 [notice] 549163#0: exit

graceful shutdownが開始してから約10秒後にKongプロセスが正常に停止していることが分かる。
また、curlコマンドのレスポンスも以下のように10秒後に返ってきている。

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 33
Connection: close
Date: Fri, 05 Dec 2025 02:24:28 GMT
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
X-Kong-Upstream-Latency: 1126
X-Kong-Proxy-Latency: 10010
Via: 1.1 kong/3.10.0.6-enterprise-edition
X-Kong-Request-Id: 1a81751705c6773080956769ca378101

{
  "user-agent": "curl/8.7.1"
}

以上より、正常にgraceful shutdownが動作していることが確認できた。
せっかくなので、kong quit --timeout 5として、タイムアウトした場合にどのようなログが見れるかも確認してみる。

2025/12/05 02:28:07 [notice] 549593#0: signal 3 (SIGQUIT) received from 549619, shutting down
2025/12/05 02:28:07 [notice] 549594#0: gracefully shutting down
2025/12/05 02:28:12 [notice] 549593#0: signal 15 (SIGTERM) received from 549700, exiting
2025/12/05 02:28:12 [notice] 549594#0: exiting
2025/12/05 02:28:12 [notice] 549593#0: signal 14 (SIGALRM) received
2025/12/05 02:28:12 [notice] 549594#0: exit
2025/12/05 02:28:12 [notice] 549593#0: signal 17 (SIGCHLD) received from 549594
2025/12/05 02:28:12 [notice] 549593#0: worker process 549594 exited with code 0
2025/12/05 02:28:12 [notice] 549593#0: exit

SIGQUIT送信後、5秒経過してもKongプロセスが停止しなかったため、SIGTERMが送信されて強制終了されていることが分かる。
curl側の結果は以下のようになった。

curl: (52) Empty reply from server

graceful shutdownが完了する前に強制終了されたため、レスポンスが返ってこなかったことが分かる。

検証(kill -SIGQUITの場合)

コマンドのフローを見ると分かるが、タイムアウト処理はコマンド固有であり、ワーカープロセス自体はタイムアウトを持っていないので、直接シグナルを送ると延々と待つ形になる。
試しに先程のPre Functionプラグインのsleepの値をngx.sleep(120)に変更して確認する。
ログ出力は以下となった。

2025/12/05 02:45:31 [notice] 549803#0: signal 3 (SIGQUIT) received from 548030, shutting down
2025/12/05 02:45:31 [notice] 549804#0: gracefully shutting down
2025/12/05 02:47:32 [notice] 549804#0: exiting
2025/12/05 02:47:32 [notice] 549804#0: exit
2025/12/05 02:47:32 [notice] 549803#0: signal 17 (SIGCHLD) received from 549804
2025/12/05 02:47:32 [notice] 549803#0: worker process 549804 exited with code 0
2025/12/05 02:47:32 [notice] 549803#0: exit

シグナル受信から実際に停止するまで2分掛かっていることが分かる。

curlの結果は以下。

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 33
Connection: close
Date: Fri, 05 Dec 2025 02:47:32 GMT
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
X-Kong-Upstream-Latency: 1840
X-Kong-Proxy-Latency: 120071
Via: 1.1 kong/3.10.0.6-enterprise-edition
X-Kong-Request-Id: d4330e76e44b103f94dc740c8b69e702

{
  "user-agent": "curl/8.7.1"
}

X-Kong-Proxy-Latencyを見ると120071msとなっており、約2分間待機した後にレスポンスが返ってきていることが分かる。

まとめ

Kong Gatewayの停止処理におけるgraceful shutdownの実装と挙動を確認した。
コマンドを利用して停止する場合はgraceful shutdownのためのタイムアウトはデフォルトで10秒なので、10秒を超えるようなリクエスト・レスポンスを扱っている場合は--timeoutオプションで適切に値を設定するとよさそう。
コマンドを利用せずに停止するケース(docker stopなども含む)では、タイムアウトが存在しないため、リクエスト・レスポンスが完了するまで待機する形になる点も少し注意が必要そうだ。

余談

ちなみにDocker利用時のSTOPの振る舞いはDockerfileを見ると以下のようにSIGQUITの指定があるため、通常はdocker stopでSIGQUITが送られる。

STOPSIGNAL SIGQUIT

ただ、ECSだとSIGTERMが送られてきてgraceful shutdownが働かないというナレッジが公開されているので、ECS利用時はこのWorkaroundを適用した方がよそう。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?