Node.jsのデプロイの問題点
Node.jsにはホットデプロイという機能が存在しないため、最新のソースコードを反映するにはプロセスの再起動をしなければいけません。その際に問題となるのがプロセスの再起動中に処理していたリクエストを最後まで処理することができないことです。
また、websocket接続中に再起動すると、websocketの接続が切れてしまいます。プロセスの再起動が完了すると、接続が切れていたクライアントが一斉に再接続しにくるのでサーバー負荷が高まってしまいます。
再起動が完了する時間はソースコードの規模によりますが最低でも数秒間はサービスにアクセスできない時間が発生してしまいます。
グレースフルリスタート
この問題の解決策としてNode.jsのデプロイにはグレースフルリスタートを使用します。グレースフルリスタートを説明すると、
- 現在稼働中のプロセスとは別に最新のソースコードを反映させたプロセスを新規に立ち上げる。
- 最新のソースコードを反映させたプロセスが立ち上がり、リクエストを受け付けられる状態になったら、リクエストを古い方のプロセスではなく新しいプロセスに処理させるようにする。
- 古いプロセスがリクエストを受けなくなり、処理しているリクエストがなくなったら古いプロセスを消す。
やるべきことはすごくシンプルです。実際の本番環境ではこれを複数台のサーバーでクラスター構成のものを管理しないといけません。
本番環境でのグレースフルリスタート
本番環境の構成は下記を想定します。全てのアクセスはELBから各EC2に振られます。1つのサーバー内で1個のmasterプロセスと2個のworkerプロセスでクラスター構成を組んでいる場合を考えます。workerの個数はCPUが足りれば何個でも問題ありません。
クライアントからELBにはHTTPSで接続するものとし、Connection: KeepAliveの設定をしているものとします。KeepAliveの設定をしないとHTTPSの場合、毎回接続時のオーバーヘッドが大きくなってしまうので必須の設定です。

グレースフルリスタートの手順
- 稼働中のworkerはそのままに、最新のソースコードを反映した新しいworkerを新規で立ち上げ、リクエストが受けられる状態にします(状態②)。
- masterプロセスが受け付けた新規のリクエストは新しいworkerにリクエストを流し、古いworkerは新規にリクエストを受けないようにします(状態③)。リクエストを受け付けないようにするにはmasterからworkerにdisconnectイベントを送信するだけで大丈夫です。
- disconnectイベントを受け取ったworkerはそれ以降のレスポンスのヘッダにConnection: Closeをつけて返さなければなりません。これをつけないと古いworkerがコネクションを維持してしまい、古いworkerをすぐに削除することができないからです。
- 古いworkerに接続していたコネクションが全てなくなったのを確認できたらプロセスを終了し、最新のプロセスのみ残るようにします(状態④)。
以上でグレースフルリスタートの手順は完了です。

まとめ
Node.jsにはホットデプロイという機能がありませんがグレースフルリスタートで対応することが可能です。その際、ConnectionのKeepAlive・Closeやdisconnectの設定など少し面倒な処理をしないといけませんがユーザーのサービス体験の向上には欠かせないので是非導入してみてください。
今回は図と文章のみの説明になってしまったので、次回は実際にソースコードも交えて詳しく説明したいと思います。要望があればすぐに書きます!最後まで読んで頂きありがとうございます。
おすすめ書籍
基礎から応用までサンプルコードを使って解説しており、Node.jsが初めての人でも読める技術書となっています。上級者向けにはExpressフレームワークを使った実用的な開発手法の解説もありこれからアプリケーションを開発する人の参考になると思います。現状では書き方が古くなっていますが、Node.jsの初心者から上級者で学ぶことの多い書籍です。
髙妻智一
最新記事 by 髙妻智一 (全て見る)
- Polkadot(Substrate)のアドレスとトランザクションについて - 2023-03-09
- 【無料公開】「Goで始めるBitcoin」3章 Bitcoinノードとの通信 技術書典8 - 2020-03-08
- エンジニアがゼロから技術ブログを書くための方法をまとめました - 2019-05-25
コメントを残す