ルモーリン

hypnotoadで稼働中のサイトを更新

投稿:2021-02-28

先日、単一プロセスで稼働させていてサイトの無応答が頻発するようになりました。 原因を探るのも面倒(症状が無応答だけ)なので安易にプリフォーク型の稼働に切り替えました。 おかげで無応答を解消できましたが困った事に気付きます。 問題は記事を追加後に秘密のAPIを使い更新情報の再作成をリクエストした際に起こります。 リクエストを受け付けたプロセスは再作成しますけれど、 他のプロセスはリクエストを受けてないので更新情報が古いままなのです。 起動時の初期処理で作成するので、その時点での最新なんですけどね。

hypnotoadコマンドからmojoliciousを起動するとプリフォークになります。 稼働中でも同様に起動するとホットデプロイメントと言って新規プロセスを待機させてから、 既存プロセスが待機状態になり次第停止させて交代できます。 端から見ると無休止で再起動できる訳です。 なんだか格好いいな(ウチのサイトには贅沢な機能)。
下記はホットデプロイメント時のログです。 管理プロセス1個とリクエストを受けるプロセス4個の計5個が入れ替わっているのが分かります。 僅か1.2秒ですね。 その間も新規プロセスがリクエストを受けるので無休止なんです。

[2021-02-28 15:51:32.18127] [2216] [info] Starting zero downtime software upgrade (180 seconds)
[2021-02-28 15:51:33.29552] [2555] [info] Listening at "http://www.lemorin.jp:10080"
[2021-02-28 15:51:33.29631] [2555] [info] Listening at "https://www.lemorin.jp:10443?*****(秘密のオプション)*****"
[2021-02-28 15:51:33.29647] [2555] [info] Manager 2555 started
[2021-02-28 15:51:33.29935] [2557] [info] Worker 2557 started
[2021-02-28 15:51:33.30428] [2559] [info] Worker 2559 started
[2021-02-28 15:51:33.30533] [2558] [info] Worker 2558 started
[2021-02-28 15:51:33.30578] [2560] [info] Worker 2560 started
[2021-02-28 15:51:33.30643] [2555] [info] Upgrade successful, stopping 2216
[2021-02-28 15:51:33.30692] [2216] [info] Creating process id file "/home/*****/myapp/script/hypnotoad.pid"
[2021-02-28 15:51:33.30708] [2216] [info] Stopping worker 2221 gracefully (120 seconds)
[2021-02-28 15:51:33.30713] [2216] [info] Stopping worker 2219 gracefully (120 seconds)
[2021-02-28 15:51:33.30716] [2216] [info] Stopping worker 2220 gracefully (120 seconds)
[2021-02-28 15:51:33.30719] [2216] [info] Stopping worker 2218 gracefully (120 seconds)
[2021-02-28 15:51:33.34584] [2216] [info] Worker 2218 stopped
[2021-02-28 15:51:33.36179] [2216] [info] Worker 2220 stopped
[2021-02-28 15:51:33.36760] [2216] [info] Worker 2219 stopped
[2021-02-28 15:51:33.36873] [2216] [info] Worker 2221 stopped
[2021-02-28 15:51:33.36883] [2216] [info] Manager 2216 stopped

mojoliciousならプロセス間の連絡方法ぐらいあるだろとタカを括っていたら、これがまあ見つからない。 ぱっと見、良さそうな
Mojo::MemoryMap - Safely use anonymous memory mapped segments - metacpan.org
をよく見ると
「Unlike normal perl variables, mapped memory is (usually) shared between threads or forked processes.」
File::Map - Memory mapping made simple and safe. - metacpan.org
とあり、Workerのような兄弟プロセス間じゃ共有できないと分かりました(しょんぼり)。

冗談で「頑張ってプロセス間通信やるくらいならホットデプロイメントで良くね?」と思いました。 落ち着いて考えてみるとホットデプロイメントの再起動時に作成してるので特にこれといった問題がないことに気付きます。

秘密のAPIで更新情報の再作成をする代わりに、ホットデプロイメントをすれば記事を追加/更新した際はこれまで通り反映されます。 更新情報の再作成をリクエストするのはVimで、記事の編集を書き出した後で自動的に記事のアップロードと再作成をリクエストしています。 リクエストを受付するコントローラーではパスワード認証で保護してあります。 下記が認証後の処理です。 使う可能性は少ないですが単一プロセス版の処理を残し、稼働方法を判別して切り分けます。 hypnotoad.pidファイルがあればhypnotoad判定という安易な方法です。 ホットデプロイメントを指示するには管理プロセスにSIGUSR2を送ります。 コントローラーはワーカープロセスで、管理プロセスは親ですからgetppidでプロセスIDを入手できます。 管理プロセスもワーカープロセスも同一アカウントで実行していますからセキュリティによる保護でkillを送れないとかそういう心配はありません。 【警告】私のサイトでは一般アカウントでmojoliciousを実行しています。 他のサイトはrootで実行していますからこの方法でどうなるか知りません。 (使えなかったり、セキュリティの弱点になったり、システム障害を引き起こしたり)

if (!-e "script/hypnotoad.pid") {
	$self->PpUpdateNotify;
	$self->render(text => "update notify ok.");
} else {
	my $ppid = getppid();
	kill "USR2", $ppid;
	$self->render(text => "hot deployment ok.");
}

ワーカープロセスの同期が必要と気付いてからのツイート及びスレッドがこちらになります。