Web サーバやアプリケーションサーバは、リクエスト数やメモリ使用量がある閾値を超えたらプロセスの再起動を行う仕組みが用意されています。
Apache にも IIS にもそのための設定項目が用意されていて、この仕組みを一般にリサイクルと呼ぶようです。
UA からのリクエストをさばくワーカープロセスが疲弊せず、パフォーマンスを維持するための仕組みですね。
Ruby で人気のアプリケーションサーバである Unicorn ですが、残念ながらこのリサイクルの機能を持っていません。
どうにかできないかなーと思って調べていたら、この短所を補完する Gem がリリースされていました。
Unicorn Worker Killer | github
そのまんまですね。
この Gem でできるのは、以下のようなリサイクルです。
・一定のリクエスト数を超えたらランダムで再起動する(上限に達したら必ず再起動)。
・一定のメモリ使用量を超えたらランダムで再起動する(上限に達したら必ず再起動)。
公式そのまま引用すると、以下のような設定を行うことになります。
# Unicorn self-process killer
require 'unicorn/worker_killer'
# Max requests per worker
use Unicorn::WorkerKiller::MaxRequests, 3072, 4096
# Max memory size (RSS) per worker
use Unicorn::WorkerKiller::Oom, (192*(1024**2)), (256*(1024**2))
リクエスト数もメモリ使用量も、値が二つあります。最初はなんで?と思いますよね。
公式の解説を読む限り、上限値ひとつにしなかったのは、複数のワーカープロセスの再起動タイミングの足並みが揃わないようにするため、ということらしいです。
実際に使用してみると、以下のようなログを吐くようになります。
Jun 19 13:57:52 jobhub app/web.3: W, [2013-06-19T13:57:52.262901 #10] WARN -- : Unicorn::WorkerKiller send SIGQUIT (pid: 10) alive: 68769 sec (trial 1)
Jun 19 14:10:36 jobhub app/web.3: W, [2013-06-19T14:10:36.849141 #14] WARN -- : #<Unicorn::HttpServer:0x00000002963690>: worker (pid: 14) exceeds memory limit (211341312 bytes > 211015086 bytes)
一度決めてしまえばそんなに頻繁に変えない設定項目だと思いますが、ApacheBench や JMeter などで負荷テストをしながらおいしい値を探る場合、デプロイを繰り返すのは少々面倒です。
弊社では heroku で運用していることもあり、以下のように環境変数を使用するように変更しました。
# Unicorn self-process killer
require 'unicorn/worker_killer'
max_request_min = ENV['UNICORN_MAX_REQUEST_MIN']&.to_i || 3072
max_request_max = ENV['UNICORN_MAX_REQUEST_MAX']&.to_i || 4096
# Max requests per worker
use Unicorn::WorkerKiller::MaxRequests, max_request_min, max_request_max
oom_min = ((ENV['UNICORN_OOM_MIN']&.to_i || 250) * (1024**2))
oom_max = ((ENV['UNICORN_OOM_MAX']&.to_i || 300) * (1024**2))
# Max memory size (RSS) per worker
use Unicorn::WorkerKiller::Oom, oom_min, oom_max
まあこちらをマネしたんですけどねw
ちなみに、Gem の作者は 日本発のBigData取り扱いサービスである、 TreasureData の中の人のようですね。
作者が日本人って、なんとなく嬉しくなります。