グレースフルシャットダウン
処理中のリクエストを完了させてからプロセスを安全に停止する終了手法
運用可用性
グレースフルシャットダウンとは
グレースフルシャットダウン (Graceful Shutdown) は、プロセスの停止要求 (SIGTERM) を受けた際に、処理中のリクエストを完了させ、リソース (DB 接続、ファイルハンドル) を適切に解放してから安全に終了する手法である。即座に kill (SIGKILL) すると、処理中のリクエストが中断され、データの不整合やユーザーへのエラーが発生する。
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);
});
SIGTERM と SIGKILL の違い
| シグナル | 動作 | キャッチ可能 |
|---|---|---|
| SIGTERM | 終了要求 (グレースフルシャットダウンの機会) | はい |
| SIGKILL | 即座に強制終了 | いいえ |
Kubernetes や ECS は、まず SIGTERM を送り、一定時間後に SIGKILL を送る。SIGTERM をハンドリングしないと、SIGKILL で強制終了される。
Kubernetes での設定
spec:
terminationGracePeriodSeconds: 30 # SIGTERM → SIGKILL の猶予時間
containers:
- name: app
lifecycle:
preStop:
exec:
command: ["sh", "-c", "sleep 5"] # LB からの除外を待つ
Pod の停止フロー:
1. Kubernetes が Pod を "Terminating" に設定
2. Service のエンドポイントから除外 (新規リクエスト停止)
3. preStop フックを実行 (sleep 5: LB の反映を待つ)
4. SIGTERM を送信
5. アプリが処理中リクエストを完了
6. terminationGracePeriodSeconds 後に SIGKILL
preStop の sleep 5 は重要だ。Service のエンドポイント除外と SIGTERM の送信は並行して行われるため、SIGTERM を受けた直後にまだ新しいリクエストが来る可能性がある。
ECS / Fargate での設定
{
"stopTimeout": 30,
"essential": true
}
ECS は stopTimeout 秒後に SIGKILL を送る。ALB の Deregistration Delay と合わせて設定する。
Lambda でのグレースフルシャットダウン
Lambda はリクエスト単位で実行されるため、通常のグレースフルシャットダウンは不要だ。ただし、Lambda Extensions を使う場合は SIGTERM ハンドラーでクリーンアップ処理を行う。
グレースフルシャットダウンの比較
| 環境 | シグナル | 猶予時間 | 対応 |
|---|---|---|---|
| ECS Fargate | SIGTERM | 30 秒 (設定可能) | プロセスで SIGTERM をハンドル |
| Kubernetes | SIGTERM | 30 秒 (terminationGracePeriodSeconds) | preStop フック |
| Lambda | - | 不要 (リクエスト単位) | ハンドラの完了を待つ |
| EC2 (ASG) | Lifecycle Hook | 設定可能 | フックで処理完了を通知 |
実践的な知識は関連書籍でも得られる。