SIGTERM送信時のNestJSの挙動

2024-09-02
市川 翔
#
#
#

概要

NestJSはNode.jsのフレームワークで、アプリケーションのライフサイクルイベントを管理するためのフックを提供しています。この記事では、SIGTERMシグナル送信時のNestJSの挙動について調査し、特にデフォルトの設定での動作とフックを有効にした場合の違いについて説明します。また、SIGTERM受信時のイベントハンドリングの有無についても調査を実施し、Cloud RunやECSでNestJSを運用する場合の考慮点も触れます。

調査内容

以下のコードを用意して、SIGTERM送信時の挙動を検証しました。

テストエンドポイント


@Post('/api/webhook_test')
@HttpCode(200)
async test(): Promise {
  Promise.resolve().then(async () => {
    await new Promise((resolve) => setTimeout(resolve, 1000 * 10));
    logger.info('10秒経過');
    await new Promise((resolve) => setTimeout(resolve, 1000 * 10));
    logger.info('20秒経過');
    await new Promise((resolve) => setTimeout(resolve, 1000 * 10));
    logger.info('30秒経過');
    await new Promise((resolve) => setTimeout(resolve, 1000 * 10));
    logger.info('40秒経過');
    await new Promise((resolve) => setTimeout(resolve, 1000 * 10));
    logger.info('50秒経過');
    await new Promise((resolve) => setTimeout(resolve, 1000 * 10));
    logger.info('60秒経過');
    const connection = await this.getConnection();
    connection
      .createQueryBuilder()
      .select('user_id')
      .from('users', 'users')
      .execute();
  });
  return 'OK';
}


SIGTERMハンドラ設定


function handle(signal) {
  console.log(signal);
}

process.on('SIGTERM', handle);

調査結果

  • SIGTERMシグナル送信後も、非同期処理は続行され、DBクエリは正常に実行されました。
  • ログにはSIGTERMシグナルが表示され、その後も処理が続いていることが確認できました。

結論

デフォルトの設定では、SIGTERMシグナルは特にハンドリングされていないため、NestJSの非同期処理やDBコネクションは切断されることはありません。ただし、非同期処理が長時間かかる場合や、アプリケーションがシャットダウン中に重要な処理が続行される場合には、NestJSのライフサイクルフックを利用して適切に処理を完了させる必要があります。

補足

SIGTERMのフックについて

NestJSでは以下のライフサイクルフックを提供しています。

  • onModuleDestroy(): モジュールの破棄前に呼ばれる。
  • beforeApplicationShutdown(): すべてのonModuleDestroy()ハンドラが完了した後に呼ばれる。
  • onApplicationShutdown(): アプリケーションのシャットダウン後に呼ばれる。

これらのフックを有効にするには、bootstrapで次の設定を行います。


app.enableShutdownHooks();

コントローラーでフックを設定する例:


@Controller()
export class WebhookController {
  async onModuleDestroy() {
    console.log('onModuleDestroy');
    await new Promise((resolve) => setTimeout(resolve, 1000 * 5));
    console.log('onModuleDestroy: 5秒経過');
  }

  async beforeApplicationShutdown(signal: string) {
    console.log('beforeApplicationShutdown', signal);
    await new Promise((resolve) => setTimeout(resolve, 1000 * 5));
    console.log('beforeApplicationShutdown: 5秒経過');
  }

  onApplicationShutdown(signal: string) {
    console.log('onApplicationShutdown', signal);
  }
}

GCP Cloud Runでの考慮点

  • シャットダウン時: SIGTERM送信後、Cloud Runはデフォルトで10秒間待機し、その後もコンテナが終了しない場合、SIGKILLが送信されます。重要な処理が10秒以上かかる場合は、NestJSのライフサイクルフックを使用して、処理の完了を保証する必要があります。

AWS ECSでの考慮点

  • シャットダウン時: SIGTERM送信後、デフォルトで30秒間待機し、その後もコンテナが終了しない場合、SIGKILLが送信されます。stopTimeoutパラメータでこの秒数を調整可能です。重要な処理が30秒以上かかる場合は、NestJSのライフサイクルフックを使用して、処理の完了を保証する必要があります。

株式会社Grandreamでは、フルリモートであなたのスキルを活かし、活躍できるエンジニアを募集しております。 詳しくは採用ページをご確認いただき、お気軽にお問い合わせください。

株式会社グランドリームでは、AWSを駆使した開発からUI/UXデザインまで、Webアプリケーションに関するすべての要望に応えます。
まずは一度お気軽にご相談ください。

お問い合わせはこちら