torutkのブログ

ソフトウェア・エンジニアのブログ

Redmine、Unicorn、Nginxでの動作安定化(タイムアウトとメモリ関係)

あるプロジェクト用に構築したRedmineがすこぶる不安定となっています。仮想ゲストのCentOS 6.5にUnicornを入れてRedmineを動かし、前段にNginxをリバースプロキシで動かしています。不安定なときはログを見るとNginxでタイムアウトが発生しています。どうやらUnicornの応答がときどきなくなっている感じです。このプロジェクトでは、巨大なファイル(GBオーダー)がばんばん添付としてアップロードされています。ディスクフルも起きています。
他にもUnicornとNginxで動かしているRedmineがありますが、不安定の発生する傾向としてはサイズの大きなファイルを添付したりダウンロードしたあとに発生しているようです。

安定化のためには、UnicornとNginxの設定を見直す必要がありそうです。しかし、今まではインターネット上にある設定をほぼ真似て動かしていただけで、パラメータを洗い直すにはUnicornとNginxを一から勉強しないといけない状況です。

そこで、先週から自宅環境で新規にCentOS 6.5の仮想ホストを立てて、そこに一からRedmine環境を構築し始めました。
せっかくなので、MySQLCentOS 6標準搭載の5.1ではなく、CentOS 7標準搭載予定のMariaDB 5.5を入れ、NginxもEPELの1.0ではなく1.6を入れてみたりと環境をリフレッシュしてみました。構築の経緯は次のURLに記述しました。
RedmineをCentOS 6上で動かすーUnicornとNginx編 - ソフトウェアエンジニアリング - Torutk

Redmineの安定化にむけて

今回の調査で達したRedmine環境の安定化に向けてのポイントは次の3点でした。

  • Nginxのタイムアウトを長めに設定することで、巨大なファイルのアップロード時などのUnicornの処理を中断せずにきちっと待つことが大切
  • Unicornが(正確にはRailsが、あるいはRedmineRailsの使いかたが)ファイルのアップロード時にファイルサイズをほぼそのまま使用メモリとして喰ってしまうので、添付ファイルの上限を物理メモリとの兼ね合いで規制することが大切
  • Unicornのワーカープロセスがメモリを喰って肥大化したら退治してあげる仕組みを講じることが必要

1つ目のNginxのタイムアウトは、proxy_connect_timeout(コネクション時)、proxy_read_timeout、proxy_send_timeoutあたりです。通常のページビューなら数秒でUnicornの応答があるはずですが、ファイルのアップロードやダウンロード時は長めに取っておく必要がありそうです。

2つ目の物理メモリは、アップロードしたファイルのファイルサイズ分だけUnicornがメモリを喰うので、Redmineで定義する添付ファイルの最大サイズを十分賄える物理メモリを用意します。あるいは、物理メモリの許す範囲で添付ファイルの最大サイズを規定します。
なお、複数のUnicornワーカープロセスがいるので最悪それぞれメモリ喰いが発生しますが、それは3つ目の対策でなんとかすることとします。

3つ目は、Unicornの標準では対策ができそうになく、Unicorn Worker Killerというgemパッケージを使って(インストールガイドのとおりの手順で)実現しました。他にもmonitで監視という方法やcronで定期的に監視するという方法も見かけましたが、Railsアプリケーションでの設定の簡単さがUnicorn Worker Killerのいいところです。

https://github.com/kzk/unicorn-worker-killer

(2014-08-30追記)Unicornプロセスの平均メモリ使用量

CentOS 6.5, Ruby 2.1.2、Redmine 2.5.2, Unicorn 4.8 が動作する環境で、Unicornワーカープロセスが使用するメモリ(常駐メモリサイズ:RSS)を観測していると、プロセスあたりおおよそ400MB前後(300MB台〜400MB台)で推移しています。大きなファイルをアップロードした直後はファイルサイズに応じてメモリ使用量が増加しますが(時には1GB超)、その後ワーカープロセスに何回かアクセスがあるともとのメモリ使用量(400MB前後)に戻っていきます。

Unicorn Worker Killerを使う場合、あまり小さなメモリサイズを閾値にしてしまうと、安定度が返って低下します。(1日数回ワーカープロセスがkillされる)

週に1回程度の頻度でUnicorn Worker Killerが発動するように閾値を設定するのがよいかと思います。それよりもいいのは、十分物理メモリを装備することですが。