Catyの配備に関するおよその方針
網羅的に調べたり、徹底的に考えたわけではないが、現状におけるおよその方針を書いておく。考えがまとまったら本編にも書くだろうが、今はまだ叩き台。
Catyが圧倒的に重視しているのは「分かりやすさ」「単純さ」だ。そのために他のことを犠牲にすることに、僕はなんの躊躇も感じない。が、「欠点を自慢するな」とも説教された(いや、ご忠告いただいた)。
主に犠牲になるのはパフォーマンスと柔軟さ。しかし、中小サイトで問題になることはないだろうと思う。ある程度の規模とアクセスを持つサイトではどうか? それをこれから考える。本音を言えば「大規模サイトはスコープ外」なんだが、小規模で始めたら意図せずにアクセスが爆発したらどうする? に対する答を用意しておかないと、さすがにマズイ。
プラットフォーム・アダプターは不要だろう
当初の計画では、プラットフォーム・アダプターと称して、次のような実行形態に対応する予定だった。
- CGI
- FastCGI
- mod_python
- mod_wsgi
今あらためて考えると、CGIはもう無理ムリ。FastCGIも何がうれしいか分からない。mod_pythonは既に死んでいる。やるにしてもmod_wsgiだけだろう。だが、mod_wsgiだって別に要らん気がする。
僕もKuwataさんも「世間はやっぱりApacheだし」という強迫観念があったのだが、もうイイヤ。今(Sat Jan 23 2010)僕は、nginx(http://wiki.nginx.org/Main)で十分だと思っている。とはいっても、Apacheを無視はできないので別途考えるが、推奨はnginx。
外部からのHTTPリクエストを受けるサーバーをフロントサーバーと呼ぶ(普通はフロントエンド・サーバー)ことにして、フロントサーバーにnginxを使い、ひとつまたは複数のCatyサーバーをその背後に置く -- これをCatyの標準構成/推奨構成としたい。
フロントサーバーへの期待
フロントサーバーに何を使うかはともかくとして、次のことはフロントに任せたい。
- SSL -- Catyサーバー単体での運用を考えると、SSLくらいはCaty本体にもあったほうがいいだろうが、ポート443番のlistenはフロントがやって、Catyサーバーは単に80番から入って来たように扱えばいいなら、楽だ。
- 静的コンテンツのキャッシュ -- Catyは静的コンテンツと動的なコンテンツを区別しない。静的・動的を別な場所に置いて重ね合わせる方式も極めてわかりにくいので嫌だ。つまりはURLを見ても静的・動的の区別はできない。というわけで、事情がめんどくさいのだが、キャッシュはフロントでやって欲しい。
- ロードバランシング -- 複数のCatyサーバーへのリクエストの振り分け。セッションとストレージ(mafs含む)の共有が問題になるが、パフォーマンスを上げるために必要。
http://wiki.nginx.org/NginxModules を見ると次のようなモジュールがある。
- HTTP Access : Allow/deny based on IP address.
- HTTP Auth Basic : Basic HTTP authentication.
- HTTP Headers : Set arbitrary HTTP response headers.
- Log : Customize access logs.
- Image Filter : Transform images with Libgd
- Substitution : Replace text in pages
こんなゴタゴタをCatyサーバーにやらしたくはない。フロントさんお願いします。
全体の構成案
フロントにnginxまたはApacheを置く。フロントはポート80番と443番を見てる(「聞いてる」か)。背後にCatyサーバーが数個いるが、例えば次のような構成。
- localhost:8000 フロントと同じ機械
- localhost:8001
- localhost:8002
- www2.private.lan:8000 LAN内の別な機械にいるCatyサーバー
- www3.private.lan:8000
これらのサーバー間では、セッション、ファイルシステム(mafs)、JSONストレージ、場合により普通のRDBを共有しなくてはいけない。
- mafsとJSONストレージは、完全に抽象化されているので、バックエンドは何でもいい。最近いろいろあるKey-Value Storeを選んで、それでmafs、JSONストレージを実装すればよい。特に、カウチDBが有望。
- セッションは共通ストレージに入れてもいいが、クッキーにセッションデータ(=状態値)自体を入れてしまう方法を考慮すべきだろう。
フロントでのキャッシュ
Catyがサービスするコンテンツがフロントでキャッシュ可能かどうかはCatyじゃないと分からない。フロントにキャッシュを任せたいなら、次のような仕掛けが必要。
- Catyにリクエストが来たら、キャッシュ可能かどうかを判断して、レスポンスヘッダに情報を書き込む。
- フロントがその情報を読んで、キャッシュ可能なコンテンツは手元に取っておく。
- キャッシュ期限が切れるまでは、フロントが直接返す。このため、Catyサーバーに負担がかからない。
- キャッシュの期限が切れれば、再びCatyにリクエスが行く。コンテンツの更新があっても、この期間は更新されないが、それはキャッシュなんだからしょうがない。
Catyサーバーがすることは、コンテンツがキャッシュ可能かどうかを判断して、適切なキャッシュ期間を設定すること。
URLのマッピング
「URLを書き換えたりマップしたりしない」はCatyの原則である。フロントが書き換えるのは勝手、ともいえるが、Catyクラスター構成でも、URLのマッピングは最小限に留めるべきだろう。
例えば、//example.jp/ をフロントで受けて、//example.jp/hiyama/ だけを1つのCatyでサービスするなら、//example.jp/hiyama/ --> //localhost:8000/ とマップされ、//example.jp/hiyama/ より下のパスはすべてCatyが扱う。//localhost:8000/ じゃなくて、//caty.private.net:8000/ (LAN内の他の機械)でもいい。
2つのCatyサーバーが完全なコピーとして動いているときは、//example.jp/ が、//localhost:8000/, //localhost:8001/ に(例えばラウンドロビンで)マップされる。
複数のアプリケーションを別々なCatyサーバーで実行することもあるだろう。//example.jp/docs/ --> //localhost:8000/, //example.jp/blog/ --> //localhost:8001/ とか; この場合は、2つのCatyのルートアプリケーションがそれぞれ、docsとblogになる。ちょっと分かりにくいので、//example.jp/docs/ --> //localhost:8000/docs/, //example.jp/blog/ --> //localhost:8001/blog/ のほうがいいと思う。このとき、Caty側のルートは無意味になるが、開発時にだけルートを使い、捨て置けばいい。
ただし、CatyのアプリケーションはCatyのルート直下にしかマウントできないから、//example.jp/hiyama/blog/ を //localhost:8000/hiyama/blog/ にはマップできない。このときは、//example.jp/hiyama/blog/ --> //localhost:8000/blog/ とマップするか、hiyamaを1つのCatyアプリケーションと考えて、//example.jp/hiyama/ --> //localhost:8000/hiyama/ とするしかない。
その他、いろいろなマップ方式があるだろうが、Catyサーバーは単純に800x番からのHTTPリクエスを処理する以外に「何も考えなくていい」という構成にしたい。
コマンドに影響があってはダメ
コマンドが構成に影響されのは最悪。コマンド作者は作法を守る必要があるが、フレームワーク側も次の情報を抽象化しなくてはならない。
クラスター環境でセッション・リプリケーションは面倒だしやりたくない。コマンドプログラマから見てもセッションはいじりたくない。セッションには、もっと工夫が必要だ。
Apacheの考慮
フロントとしてApacheを使えるようにすべき理由は: