Webサーバの動的コンテンツを処理する方式としてFastCGIがあります。FastCGIの動きをざっくりとまとめると、
- Webサーバがリクエストうける
- 動的コンテンツ実行時にFastCGIデーモンが動いているか確認
- 動いていればそのまま処理を依頼してコンテンツ実行、動いていなければデーモンを起動させて実行
- 実行後はFastCGIデーモンは暫く起動しっぱなしで連続的なリクエストを処理する
- 一定時間処理がこなければデーモンが落ちる
というような動きをします。これによって、最初の一回はデーモンプロセス起動に時間がかかっても、起動後の連続的なアクセスはprefork的に処理できるため、都度プロセスの生成・破棄が生じるCGI方式よりも性能が良くなります。また、一定時間たつと、プロセスが停止するため、使っていないFastCGIプロセスがたまることもなくエコです。
その考え方をコンテナに適用してみましょう。それを勝手に「FastContainer」と呼ぶことにします。Webアプリを提供するコンテナが、ストレージを共有する複数のホストOS上に高集積に起動しているようなシステムを、自分達で作ろうしている側であることを想定します。その場合に、FastCGIならぬFastContainerのアーキテクチャを適用した場合に、どういう設計になるか、具体的にはどういう実装になりそうか、さらに、得られるメリットを考察してみます。
まずは、大体のFastContainerのためのアーキテクチャ概要として、
- コンテナに関する情報はCMDB(構成管理DB)などに保存しておく
- CMDBにはコンテナを起動するための各種パラメータや収容ホストのアドレス、Listenポート等を保存
- 複数コンテナにリクエストを振り分けるプロキシをフロントに用意しておく
- 既にHTTPリクエスト先のコンテナが起動していれば、そのままコンテナにプロキシする
- 起動していなければ、reactiveにコンテナを起動してからプロキシする
- 一定時間リクエストがなければコンテナ停止
というようなアーキテクチャを想定することができます。実際にこれを、より具体的に連携イメージ図に落とし込むと、例えば以下のようになるでしょう。
この場合、クライアントからリクエストを受けたフロントのWebプロキシが、CMDBからリクエスト先のコンテナの収容ホストとListenポートを取得した上で、X-Container-Port: 8080
のようなヘッダをHTTPリクエストに追加し、そこから収容ホストで待ち構えているLocal Web Proxyにアクセスします。リクエストを受けたLocal Web Proxyは、ホスト上で対象のポートやUnix Domain SocketがListenしているかを極めて高速な処理によって確認を行い、Listen済みであればコンテナが起動しているとしてそのままプロキシ、Listenしていない場合は、CMDBからコンテナ起動に関するパラメータを受け取って、コンテナを起動してから、該当のコンテナにプロキシを行ってレスポンスを返す、という処理になるでしょう。
そして、同様に、連続的にリクエストがつづけばそのまましばらく処理を行い、リクエストが長い間なければ、コンテナを停止してプロセスからいなくなるようにします。
このような、FastContainerのようなアーキテクチャをとるメリットは以下のようになるでしょう。
- リクエストのない不要なコンテナは停止するためホストOSのメモリは節約される
- コンテナなので最初の起動もそれほど遅くならないはず
- ホスト間の移行時もCMDBのデータベース上の収容ホスト情報を変えるだけで、別ホストにreactiveにコンテナが作られる
- 移行前のコンテナはアクセスが0になるので、いずれ停止する
- CMDBに複数収容ホスト情報があれば勝手にホストにアクセスしてreactiveにコンテナを複数ホスト上に作って負荷分散することもできる
つまり、データベースとCMDBのみが状態を持ち、その状態によってコンテナがreactiveでstatelessにホストを駆け巡ることができるようになります。また、ホストを追加するのも容易ですし、メンテナンス時に別ホストにDBの向け先を変えるだけで、動的にホストを超えて入れ替わっていく、というような設計も可能でしょう。コンテナの作成を制御するためにAPIやジョブなどを用意せずとも実現でき、非常にシンプルな設計になるように思います。もちろん状況に応じて、DBのキャッシュなどを用意する必要はあるでしょう。
上記のような例では、例えば、Web Proxyにngx_mrubyのようなプログラマブルにプロキシをコントロールできるソフトウェアを使いつつ、ngx_mrubyからのコンテナの扱いに、これまたプログラマブルにコンテナを制御できるhaconiwaを使うことによって、ngx_mrubyがCMDBから得た情報からhaconiwa用のDSLを作って、そのDSLをhaconiwaに渡してやれば、haconiwaはそのDSLに従ってコンテナを起動させることができるので、比較的簡単にコードでこのような、所謂FastContainerの動きを実現できそうです。haconiwaについては、以下の記事で検証していますので、是非御覧ください。
このように、ストレージでデータを共有する複数ホスト上に大量にWebコンテナが動いている状況で、いかにホストOSのリソースを最適化しながらも、状態を持つ・持たないでコンポーネントを分離し、コンテナがホスト間を自由、かつ、即座に行き来できるようなアーキテクチャを考えた時に、このFastContainer構想を応用すれば非常にシンプルな設計で実現できる気がしております。
FastCGIだけでなく、xinetdといった古くからあるUnixのソフトウェアを色々見ていると、昨今の技術背景においてもうまく応用すれば面白いアーキテクチャが生まれそうだと思わせられるような技術が沢山あり、再度、昔からある汎用的な技術に目を向けるのは重要であると実感しておるところです。
ということで、最近考えていたFastContainer構想についてざっとまとめてみました。