グレースフルシャットダウン

処理中のリクエストを完了させてからプロセスを安全に停止する終了手法

運用可用性

グレースフルシャットダウンとは

グレースフルシャットダウン (Graceful Shutdown) は、プロセスの停止要求 (SIGTERM) を受けた際に、処理中のリクエストを完了させ、リソース (DB 接続、ファイルハンドル) を適切に解放してから安全に終了する手法である。即座に kill (SIGKILL) すると、処理中のリクエストが中断され、データの不整合やユーザーへのエラーが発生する。

Node.js での実装

Node.js での実装のコード例を示す。

const server = app.listen(3000);

process.on('SIGTERM', () => {
  console.log('SIGTERM received. Shutting down gracefully...');

  // 1. 新しいリクエストの受付を停止
  server.close(async () => {
    console.log('All connections closed');

    // 2. DB 接続を解放
    await db.end();

    // 3. プロセスを終了
    process.exit(0);
  });

  // 4. タイムアウト: 30秒以内に終了しなければ強制終了
  setTimeout(() => {
    console.error('Forced shutdown after timeout');
    process.exit(1);
  }, 30000);
});

server.close() が閉じるのは、リクエストの送受信中でない接続だけである。Node.js 19.0.0 より前のバージョンでは idle 状態の keep-alive 接続がそのまま残り、close() のコールバックが呼ばれずタイムアウト側の強制終了に落ちる。18.2.0 で追加された server.closeIdleConnections()close() の直後に呼べば回避できる (19.0.0 以降は close() 自身が idle 接続を閉じるため不要)。

SIGTERM と SIGKILL の違い

SIGTERM と SIGKILL の主な違いを以下に整理する。

シグナル動作キャッチ可能
SIGTERM終了要求 (グレースフルシャットダウンの機会)はい
SIGKILL即座に強制終了いいえ

KubernetesECS は、まず SIGTERM を送り、一定時間後に SIGKILL を送る。SIGTERM をハンドリングしないと、SIGKILL で強制終了される。

Kubernetes での設定

Kubernetes での設定の例を示す。

spec:
  terminationGracePeriodSeconds: 30  # SIGTERM → SIGKILL の猶予時間
  containers:
    - name: app
      lifecycle:
        preStop:
          exec:
            command: ["sh", "-c", "sleep 5"]  # LB からの除外を待つ

Pod の停止フロー:

1. Kubernetes が Pod を "Terminating" に設定 (ここから猶予時間の計測が始まる)
2. kubelet が preStop フックを実行 (sleep 5: LB の反映を待つ)
3. コンテナの PID 1 へ SIGTERM を送信
4. アプリが処理中リクエストを完了
5. 猶予時間を過ぎたら SIGKILL

上記と並行して、コントロールプレーンがこの Pod を EndpointSlice から外す (新規リクエストの流入停止)

preStopsleep 5 は重要だ。エンドポイントの除外はコンテナの停止処理と並行して進み、しかも kube-proxy やロードバランサーへ反映されるまでに時間がかかるため、SIGTERM を受けた直後にまだ新しいリクエストが届く可能性がある。

猶予時間の計測には preStop フックの実行時間が含まれる。preStop で 5 秒待つなら SIGTERM 後に残るのは 25 秒で、公式ドキュメントが認めている延長は「preStop が猶予時間を超えて動き続けている場合の一度きりの 2 秒」だけである。アプリ側の強制終了タイマーは「猶予時間 - preStop の待ち時間」より短くしておく必要がある。上の Node.js の例の 30 秒は、この設定 (猶予 30 秒 + preStop 5 秒) では発火する前に SIGKILL が届くため、20 秒程度まで縮めることになる。

ECS / Fargate での設定

ECS / Fargate での設定の例を示す。

{
  "stopTimeout": 30,
  "essential": true
}

ECS は stopTimeout 秒後に SIGKILL を送る。既定値は 30 秒、指定できる上限は 120 秒である (Linux ではプラットフォームバージョン 1.3.0 以降が必要)。ALB の Deregistration Delay と合わせて設定する。

Lambda でのグレースフルシャットダウン

Lambda はリクエスト単位で実行され、ハンドラーが返った時点で実行環境は凍結されて次の呼び出しを待つため、通常のグレースフルシャットダウンは不要だ。実行環境を破棄するとき、Lambda は登録済みの外部拡張機能へ Shutdown イベントを送る。この停止処理に使える時間は公式ドキュメント (2026 年 8 月時点) で拡張機能なし 0 ミリ秒、内部拡張機能のみ 500 ミリ秒、外部拡張機能あり 2 秒と定められており、時間内に応答しなければ SIGKILL で打ち切られる。拡張機能を登録していない関数には後片付けの窓が存在しないため、解放が必要な処理はハンドラーの中で完結させるのが原則になる。

グレースフルシャットダウンの比較

グレースフルシャットダウンの比較を以下に示す。

環境シグナル猶予時間対応
ECS FargateSIGTERM既定 30 秒・上限 120 秒 (stopTimeout)プロセスで SIGTERM をハンドル
KubernetesSIGTERM既定 30 秒 (terminationGracePeriodSeconds・preStop の実行時間を含む)preStop フック
LambdaShutdown イベント (登録した外部拡張機能宛て)拡張機能なしは 0 ミリ秒・外部拡張機能ありで 2 秒解放処理はハンドラー内で完結させる
EC2 (ASG)Lifecycle Hook設定可能フックで処理完了を通知

実践的な知識は関連書籍でも得られる。

この記事は役に立ちましたか?

関連用語

関連する記事