読者です 読者をやめる 読者になる 読者になる

人間とウェブの未来

「ウェブの歴史は人類の歴史の繰り返し」という観点から色々勉強しています。

ApacheのVirtualHost単位でMaxClientsを設定するApacheモジュールをOSS化

レンタルサーバ 運用 プログラミング

一つのサーバに多数のホストを収容するような高集積なホスティングサービス(所謂レンタルサーバ)を提供しようとすると、収容しているユーザ単位でいかにリソースをコントロールするかが運用者には求められます。

そういう状況で、これまでApacheには色々なモジュールがあって、それらをうまく組み合わせながらリソースを制御してきたと思います。ですが、意外とシンプルなリソース制御として、VirtualHost(以下vhost)単位で個別にMaxClinetsを制御するだけのモジュールが無かったりします。それと同等の機能を提供するモジュールは幾つかあるのですが、色々と必要の無い機能が沢山盛り込まれていたりとか、特定のバージョン、特定のMPM、特定のOSでしか動かなかったりして微妙に使いにくかったりします。

要件的には単にvhost単位でMaxClinetsを各種MPMやバージョンで制御できれば良いです。こういうモジュールは各社社内で作っていたりすると思うのですが、汎用的なOSSとしてあまり公開されていないように思っています。

また、その他の要件の流れとして、

  • ある特定のユーザだけのnprocをゆるめたい
  • nprocは全ユーザ共通の値なので緩めると他のユーザも緩んでサーバが高負荷になる
  • そもそもnprocはApache以外のユーザプロセスもカウントするので制御が難しい
  • 単にvhost単位でMaxClients個別で設定したくない? -> したい
  • 導入もmrubyとか居れなくてもモジュール単体で簡単に色々な環境にいれられるようなもの
  • 実装もロックとか共有メモリとか使わなくて見通しの良いシンプルなもの
  • Apache2.0.x 2.2.x 2.4.xのprefork worker eventで対応してるとうれしい

というのがあったので、それを満たすようなvhostのMaxClientsを個別に制御するためだけのApacheモジュールを作りました。

github.com

要件の中で、vhostやファイル単位でカウンターを持とうとすると、一気にグローバルなロックとか共有メモリでカウンター・テーブルを保つ必要があったり(http-dos-detecotorhttp-access-limitterはそのアーキテクチャをとっています)、そこのデータをどう制御するかなど色々考える事が増えてコードが複雑になりがちです。

また、マルチスレッド・マルチプロセス・イベント駆動・組み合わせのハイブリッド型のMPMに対応させようとすると、思わぬデッドロックやデータの扱いのバグ(カウンタが上がりっぱなしとか)が発生しやすくなるでしょう。

そういう事をせずに、ApacheのAPIや内部機能を使うだけでうまく制御するには、Apacheのスコアボードを舐める方法が考えられます。そこで、それを使ってNo lock and shared memoryで実装することにしました。

また、性能面でも、mod_vhost_maxclientsを使わない場合で、数十バイト程度の小さな静的コンテンツに対して同時接続数100で25000req/sぐらいでるサーバで、導入後にMaxClinets以内の同時接続数で処理すると大体23000req/s、vhostのMaxClinetsを超えるような同時接続数ではすぐに503が返るので50000req/sぐらいでます。そういう意味では性能面でもそこまで問題にならないと想像しています。これらはあくまで参考情報なので詳しくは自身でご確認下さい。

ただ、スコアボードが全て埋まるようなアクセスパターンが発生した場合は、そこの処理が多少オーバーヘッドになる可能性はあると思っています。そんな事もあろうかと、その問題も解決するために、IgnoreVhostMaxClientsExtというディレクティブを用意しました。これは、MaxClinetsのためのスコアボードの探索を、このディレクティブに登録している拡張子は行わない、つまりは、MaxClinetsを超えた事による503を返さないようにすることができます。これによって、仮にオーバーヘッドになるような静的コンテンツがあったとしても、無視リストに拡張子を登録しておくことでそのオーバーヘッドの影響を受けなくなって便利です。

以下のようにvhost単位に設定を入れて使います。

<VirtualHost *>
    DocumentRoot /path/to/web
    ServerName test001.example.jp

    # MaxClients per vhost using mod_vhost_maxclients
    VhostMaxClients 30

    # Ignore extensions from VhostMaxClients for a performance issue
    IgnoreVhostMaxClientsExt .html .js .css

</VirtualHost>

また、サーバに負荷をかける用な重たい動的コンテンツの場合は、そもそもスコアボードの探索のコストはほぼ無視できるレベルだと思います。色々と心配事を書きましたが、実際に自社の環境に合わせて性能評価してみると、それほど問題にならないのではと思っているので、今回はよりシンプルで見通しの良い実装を優先しました。

ということで、サクッと入れられるので是非ご活用下さい。