「これからのWeb(バックエンド)」を自分の頭で考えてみた

ふと今更、年初のCROSS 2013の「次世代 web セッション」の動画を見て、うんうん唸ってしまった。プロトコル編の方は知識不足であんまり分からなかったですが、アーキテクチャ編の方はグサグサくるものがあった。「自分の頭でこれからの web を考えてブログに書くまでがこのセッション」という宿題が出ていたので、せっかくなので最近考えてることをつらつらと書いておこうと思った次第。特にまとまりはないですし、戯言です。

前提

僕はコード書いてない&サーバサイドしか見たことない&WEB サーバはあんまり見たこと無くて、それより後ろ側ばっかり見てた人なので、ユーザ側とかアプリ開発者がどうなっていくかについて特に尖った意見はありません orz SPDY とかもまだ手を出してなくて眺めてるだけ。

ただ、WEB なシステムの裏側のアーキテクチャの「これから」というのも「これからの Web」の文脈の一部として考えてみる価値はあると思います。「LAMP」とかひとくくりにされていた時代は過ぎ去り、今のバックエンドは多種多様な技術がひしめきあっています。セッションの中の揺り戻し的な話でいうと、次はどこに集中していくのかなぁというのが個人的には気になっています。以下、現状の説明とかは割愛してるので、共感できるところだけ共感してもらえれば幸いです。

僕の経験自体はとても偏っているし年季が入ってないので、突っ込みどころはたくさんあるでしょうが、蓄えた知識・経験から「自分の頭で考えてみる」ことが大事なので、お目こぼしということで。青い話が多いですが、言うだけならタダなのでとりあえず放流。

Programming Languages / Application servers

アプリケーションのビジネスロジックを記述するプログラミング言語。これは昔も今もこれからもたくさんの言語がひしめきあっていくんでしょう。これがどこかに収斂することはなさそう。ruby(というか RoR)さえ書ければどうとでもなる、というのはまぁ RoR から出なければそりゃそうだというだけで、アーキテクチャを考えたりしたい人にとっては選択肢を狭めることにしかなりません。

処理系+モジュール/パッケージ群のファイル資源管理方法

僕はアプリの実行系は OS それ自体から抽象化されていくんじゃないかなぁと思ってます(というかそうしたい)。パブリック/プライベートクラウドや Vagrant の様に、今では簡単に OS の上に好きな OS を配置することができます。1 つの会社の中で、プロジェクト毎に全然違う OS を使ったとしても、その下で実際に処理を支えるホスト OS は共通で使ったりできて、Dev と Ops の資源がいい感じに分離できたりします。すると、Ops 的な要件で必要なセットアップと Dev 的な要件で必要なセットアップがコンフリクトすることがありません。素晴らしい。

が、どうしてもコンピュータをまるまるエミュレートする仮想化方式ではパフォーマンスのオーバヘッドが気になります。また、IP アドレス始めコンピュータリソースの管理(各 VM にメモリをどれだけ割り当てるか、とか)というめんどくさい問題もつきまといます。

この辺は僕なりの POC として、Pandlerというのを作ってみました。chroot 使ってファイル資源のみ隔離して完璧にコントロールする代わりに、それ以外の資源は何も考えなくていいのが楽チンです。とはいえ、今度は port 番号が被るのでそっちを気にしないといけなかったり。。。

あと、処理系とモジュール群を隔離する方法として、rvm/rbenv 初め、LL を自前でコンパイルしてパスとかも専用にしてしまうというアプローチが最近流行っています。僕はそれよりも上記のアプローチの方が優れていると思っているので、それが流行れば古からの積み上げがある deb/rpm(apt/yum)に移してしまえばいいと考えています。が、Dev も Ops も最近は deb/rpm の様なバイナリ配布が嫌いみたいで、割と自前でビルド派が多いですね。僕は基本的に自分でビルドはしたくなくて、実績ある(と思われる)バイナリを使う方が好きで、揺り戻し的な話で言えばそのうちこっちに戻ってくるんじゃないかなと思って逆張りしている次第です。

異言語間の連携方法

1 つのアプリケーションの中でさえ複数の言語を利用することは当たり前です。自分はそんなことしない、と思っている人だって、C 拡張の gem とか使ってたらそれは ruby と C が連携してるわけです。古典的にはこうした C 拡張みたいな連携方法はどの言語にもあるわけですが、大抵が C/C++への拡張のみです。それは当たり前で、1 つの言語が他の全ての言語への拡張を対応できたところで、同じ事を他の全ての言語もやらなければいけないのは大変に辛い。

1 つのやり方は SOA とか呼ばれている様なイメージで、言語が異なるものはサービスとして別のプロセスにしてしまって、その間は何かしらのユニバーサルな RPC のプロトコルでつなぐという方法。汎用性は高いし、今の技術でも可能だから既にたくさん採用されている(もちろん同じ言語であっても分ける意味があれば分けるでしょうし)。

ただ、なんかこう思考停止な感じも否めない。もうちょっとおもしろいやり方があってもいいと思う。

LLVM がおもしろいなぁと思ってます。全然まだ何者なのかつかみきれていなくて違ったら教えて欲しいんですが、僕の思ってる雰囲気では LLVM の内部形式に変換できれば、LLVM がサポートするいろんなプラットフォームにコンパイルできる、というものかな。例えばemscriptenというのは C/C++のコードを javascript に変換できるという気持ち悪いものだけど、確かに動いてる。javascript に変換できるとなんとブラウザでも動作するわけですね。

こんな感じで、実際の実行形式の一歩手前に共通レイヤができあがれば、たとえフロントエンドは異なる言語でも、特定のインタフェースを介して連携したりできるのかなーと妄想したりしています、そう JVM みたいにね!JVM は一つの解ではあると思うんですが、なんでも JVM でできるよね、ってなるとこれまた思考停止っぽい。

Database

NoSQL みたいな煽り用語は置いといたとしても、Database の選択肢が増えて本当に楽しい。が、ちょっとみなさん食傷気味。大事なのは、使いたい用途と現状に合わせてデータベースを決めるということで、データベースをまず決めるってのはなんか違う。

“スキーマレス”という謳い文句は、ちゃんとしたアプリを作ろうとするならあんまり意味がないと思ってて、MongoDB 使ってるのにスキーマ定義してバリデーションとかしてたらだいぶスキーマレスじゃない感じがする。スキーマは別に RDBMS に限ったものでもなくて、データベースなんだったら何かしらスキーマ決めないと読む側と書く側の合意が取れない。それをデータベース上で表現するのか、アプリサイドに持つのかで色々選択肢があると思う。JSON Schema みたいなのおもしろいなーと思ってるけど、僕は RDBMS で育った人間なのでやっぱり CREATE TABLE が理解しやすい。スキーマレスなデータベースも、後からスキーマが定義できると面白いかもね、結果整合性ならぬ結果スキーマ。しばらく入れてみたデータを眺めて自動で最適なスキーマが決まるとか。

スキーマ云々は、何かしらスキーマ持ちましょう、で FA かなと思ってるけど、それよりもっと大事なポイントはスケールアウトと耐障害性。伝統的な RDBMS が叩かれるのはここが弱いというか後付だからだと思うけど、別に後付であっても MHA みたいにすごくうまくいってる例もある。とはいえ、データベースのインスタンス間で協調動作してくれて、ノードの障害に気を払わなくていいのは魅力的。Hadoop が流行ったのも個人的にはここがとりあえず安心できるからかなぁと思ってる(とはいえ Namenode の SPOF 問題はあるわけですが。。。)。

で、やっぱり大概の規模の大概のデータで RDBMS は超強力かつたくさんのノウハウがあるものなので、これからも価値をもち続けると思ってます。とはいえ、RDBMS と一口に言ってもたくさんあるわけで、MySQL しか使えません、とかは自分の首を締めることになるかと。僕も最近は PostgreSQL 触ってみたりしてます。

RDBMS 以外のデータベースを使う理由としては、セッションの中であったように「RDBMS にはできるあれを捨てる代わりに、これができるようになる」をハッキリとシンプルに言えないものについては、RDBMS よりめんどくさくなるだけで流行らないと思います。なので、新しいデータベースとかを見た時に、何を捨てて何をできるようにしているのか、という点に強く注意を払うべきだと思います。Redis の割り切り方は潔くて好きですね、ストレージとしてどうかは置いといて。

あと例えば HandlerSocket の様に、InnoDB を B+Tree の信頼性高いストレージとみなして、それに独自のインタフェースを生やすというアプローチもあります。インタフェースとデータの持ち方は分離していくのかもしれません。TokuDB という面白いストレージがありますが、MySQLMongoDBに対してストレージ層を提供してたりします。Hive や Impala は Hadoop に対して SQL っぽいインタフェースを提供しています。こんな感じでインタフェースとストレージの組み合わせを色々考えていくのも次を考えるいい切り口ですね。

僕の個人的な意見では、データベースは運用が命なのでその点で RDBMS と Hadoop はだいぶ色々とノウハウが溜まっていて先んじてるのかなーという印象。新しいものはなかなか人柱ががんばらないと運用のノウハウは貯まらないのがしんどいところです。あとライブラリ側のバグも使われた量に比例して潰されていくので、この点でも新しいプロトコルを使うよりも既存のプロトコルに乗った方がメリットが高そう。

Messaging

App+DB で完結ということはほぼあり得なくて、システムの裏側でも非同期処理が横行しています。重ための処理は一度キューに投げてすぐにレスポンスを返し、ワーカーがキューを処理したらクライアントに通知(もしくはポーリングで定期チェック)みたいなのはよくある処理ですが、それをどう実現するかというのはまだまだ定まってない感じがして楽しいです。

キューのストレージとしては、僕の会社では Q4M という MySQL のストレージエンジンをよく使っていますが、世間では最近は Redis を使うのが多そうですね(Resque とか)。多少消えても困らない様なものについては個人的には ZMQ がおもしろそうだなぁと思ってます。その一方で、古の SMTP というメッセージングプロトコルは未だに生き残っているわけで、個人的にはシステム内部のメッセージングに使ってもおもしろいのでは?と思ってたりします。冗談ですが。。。

どんなストレージやプロトコルを使うかについてイマイチ指針がないのが困ったところなんですが、大事なのは、一応”ストレージ”なわけで、そのデータの Durability を考えることから始めるべきかと。1 つでも消えたらいけないものは enqueue 時に確実に絶対消えないストレージに入るべきだし、そうでないものはそれなりのものにすればいい。そういう意味でデータベースの一種だとみなすべきですね。

ワーカの作り方も、下手くそな作りをするとせっかく大切に保存したキューがワーカの異常終了で消えちゃったり、反対に 2 回処理されちゃったりして困ったことになりかねませんので、これからのアプリ開発は正しいアプリサーバの作り方と並んで正しいワーカの作り方も学ばないといけませんね。シグナル処理とかは Perl の Parallel::Prefork がとても勉強になります。

一方でキューのワーカというよりジョブという概念で仕事をやらせる考え方もあって、僕はぜんぜんまだまだ知識が追いついてませんが、Hadoop 的な人達のパフォーマンスの指標としてはどれくらいのジョブをこなせるかというスループットとかがあるみたい。他にもApache Mesosみたいなのもあったりして、こういう方向でのシステムの見方も当たり前になってきてる。単に Web や App のスループットやレスポンスタイムだけ見ててもシステム全体の状態を見られるわけじゃない。あぁ奥が深い。。。

OS install

OS をどうやってインストールするかも実はたくさんの選択肢があります。が、大きく分類すれば、コピーかクリーンインストールかの 2 種類でしょう。コピーは AMI のコピー、クリーンインストールは ISO からインストール、と思ってもらえれば。昔はクラウドなんて無かったですが、それでもどちらの方式も存在しました。なので、別に目新しい分類でもなく大して変わってません。今でも kickstart や preseed は現役ですし、これからも使われていくんでしょう。ここを変えるのはあまりメリットが見えないです。

そういう意味で、先述した様に OS install は極端な話をすれば Linux kernel と kernel module、ドライバ系のインストールを指して、それ以外の全てはアプリケーションが独自に管理していく感じになると、抽象化が一歩進んで楽しそうだなぁと思ってます。が、なかなか共感してもらえることがないので、たぶん流行らないと思います。。。

もし今のままでいくのなら、OS install は後述の provisioning を行えるだけの環境を準備するのが仕事になります。ここで必要になるのは更に何かしらの agent が実行できる必要がありますね。sshd は最強だと思いますが、僕は sshd でなんでもやるのは反対です。ssh の接続を担保するために bootstrap 的な処理が必要になる(これは他の agent でも同じですが、sshd だと他にも影響する)し、ssh 経由での実行特有の注意点はどう考えてもハメにしかなりません。ので、chef や puppet のアプローチは好きですし、いろんな agent が生えていくのは割と普通な流れだと思います。

コピーかクリーンかは、場面によって代わりますし、どちらかだけできればいいという時代はもう終わってるので、同じ環境をどちらの手法でも作れる様にしておく必要があると思います。

provisioning

最近 Chef や Puppet 隆盛で妙にここだけ取り沙汰されてますが、ここだけ完璧にしても結局 OS install がずさんだと大した管理にはならないし、後述の orchestration まで無理に provisioning に組み込むと死ねます。なので、自分のシステムの中のどこを Chef や Puppet に管理させたくてどこを管理させたくないのかをハッキリさせるところから始めるといいですね。

Chef や Puppet が今後もずっと使われるかというと、僕はそうは思ってません。どっかで言ったことありますが、手作業での構築作業が機械語、手順書のコピペがアセンブリ、みたいなアナロジーを適応すると、Chef や Puppet はやっと高級言語ができはじめたぐらいだと思います。ので、これからどんどんと進化していくと僕は思ってます。手始めに gcc みたいな標準的なコンパイラができると、インタフェースだけの違いになってくるし、他の言語も生まれやすくなるのでそういうところを誰かがやってくれるとうれしいですね。

あと、provisioning には test を組み合わせるべきです。test とは、想定する spec の状態にちゃんと構築できているかをチェックすることですね。重要なのは新しく作ったサーバだけじゃなくて、稼働中のサーバに対しても継続的なテストを実施するということ。これによって、予期せぬ設定ミス等を発見できます。serverspec をとりあえず推しておきますが、まだまだこの分野は未開拓です。いいプロダクトが生まれてくるといいなぁと思ってます。

orchestration

まだまだ全然手がついてないなぁと思うのがここですね。準備したサーバを他のサービスと協調させつつサービスインするところは、未だに手作業だったりするのではないでしょうか?ここも自動化していけば自律的なシステムに近づきます。

関係者が多いので大変です。アプリケーションサーバなら、ロードバランサに入れなきゃいけないし、その前に各種データベースとちゃんと接続できる様にしないといけない。ものによってはいきなり 100%投入できないから徐々に比率を上げていきたいかもしれないし、稼働中のクラスタの一部のサーバだけ変更したいとかあるかも知れない。クラスタに対して協調的なジョブ(1 台ずつサービスから外して再起動して徐々に戻す)とかどうやってやるか?

ココらへん実はすごい宝の山で個人的にはここで色々遊びたいなぁと思いつつ、とてもそんな開発力はないので仕方なく目 grep とキータイプ速度を向上させている次第です。スーパーマンが現れることを期待しています。

server metadata

で、僕が一番ミッシングリンクだと思ってるのがこれで、サーバのメタ情報を表現するものが全然共通化されていないんですよね。多分それなりの規模のシステムなら Excel でもなんでもいいですが何かしらメタデータは保持していると思います。僕の会社では RDBMS にぶち込んでます。

一方で、いろんなシステム管理ツールが自分用にメタデータを持ちたがります。Chef Server しかり、Zabbix Server しかり。でもホントは Chef の attribute を使って zabbix で監視したかったりするんですよね。そういうためのメタデータ共通基盤。

OS install も provisioning も orchestration も、その他システムに関わる全ての場面でこれらメタデータは必要になるはずなのに、割とおざなりにされていて場合によっては二重三重管理になっていて不整合が起こったり、おかしな更新を誰かがしたせいでサービスがぶっ壊れたりしてるんじゃないかと思います。

クラウドが色々あったりオンプレもあったりで、なかなか僕もどういう形式がいいのか考えきれなくてはや 1 年以上何にも作れていないんですが、そろそろなんかいい方法を見つけないと僕のシステムに対する興味が失せそうだなぁと思っているところです。

それは置いといて、多分ものすごくシンプルな HTTP の API になるはずなんですが、何かできないものだろうか。Triglavとかありますね。古の何かが実はあるのかもしれないし、Google とか Twitter とかの巨人が何かを公開してくれるのかもしれない。という感じで、ここら辺の議論が巻き起こってもいいよなぁと思ってるけど全く起こらなくて、僕の予測は外れてるのかもしれません:p

internal DNS

metadata とも関係するんですが、internal DNS もどうなっていくのか気になるところです。provisioning とは別のレイヤで、小さいデータを全てのサーバに効率よく高速に配布できれば、hosts ファイル全撒きってのも十分通用すると僕は思ってます。ただし配布のために metadata が必要になります。internal DNS のコンテンツサーバを立てて、各サーバにキャッシュサーバをローカルで立てるのは今現在一般的なやり方な気がしますが、変更の反映が TTL 依存になるのが許容できない場面も多いですね、DB のフェールオーバとか。フェールオーバの際に超高速で hosts ファイルをデプロイするとかそれなりに現実性あると思いません?

まぁさすがに hosts ファイルは無いとしても、internal なんだからコンテンツそれ自体を全サーバが自分で持つというのは十分にありだと思います。前に考えたことがあるのは、MyDNS や PowerDNS を全サーバに入れて、データベースも全サーバに入れて全てをレプリケーションでつなぐという方式。何百何千というサーバのレプリケーションはちょっとどうなのという気はしますが、単純に pub/sub だと思えば、こういう感じの構成にして、常に自分のローカルのコンテンツを信じる様にすれば可用性はあがるし、レプリケーションで(結果整合性ではありますが)更新が自動で反映されます。

zookeeper を使って DNS っぽいことをやっているところはあるかなぁと思いますが、zookeeper に毎度直接問い合わせるという仕様だと可用性が低いので、なんらかローカルにキャッシュの仕組みを入れて、しかも zookeeper 側に更新があったらキャッシュ更新処理が必要で、とか考えると、意外と全レプリケーションがアリに見えてきます。もしくは全サーバに zookeeper とか。。。

deploy

capistrano が一番流行ってる気はしますが、全然改良の余地はありますね。まず metadata は先程の様に外出しすべきだし、agent が sshd でいいのかという議論もあるべき。僕は sshd は反対派で、協調動作できる専用の agent があっていいかなと思ってる。

高速デプロイは男のロマンだと思うんですが、ファイルの転送には光速みたいな律速要因がどうしてもあるので、そこは諦めて事前にがんばって撒いておいて、タイミング合わせてみんなで切り替えるアプローチの方が、僕はアリかなと思ってます。極端な例を言えば、アプリのコードの中にタイマーを仕込んでおいて、ある時刻に発火するとか。さすがにそれはやりすぎでも、deploy agent 経由で指令を受けたら指定のリビジョンに切り替えて、agent 間でやりとりしながらクラスタ内の 10%の台数ずつ再起動していくとか。大きい会社ならもうそういうのやってそうですね。

あと、ボタン一個でデプロイできるのも楽しいんですが、個人的には pull request の merge ボタンみたいな感じで、あと一歩というところで止めて最後の確認をダブルチェックしてからポチッとできると、オペレーションのログ的にもいい感じのものが残るんじゃないかなぁと思ってます。

monitoring

そろそろ体力が尽きてきたので最後に監視について。システム監視については言いたいことは山ほどあるんですが、今後どうなるという点でいうと、今までのヒューリスティックな監視設定が高度にパターン化されていくのかなと。これも provisioning と似ていて、手作業による熟練の監視項目から、ある程度パターン化がなされていって、テンプレートができ、それが制約となって逆に監視される側を強制的に変更させるのかなぁと。

例えば、ワーカはある形式に則った push 通知かログ出力を一定間隔で必ず行い、それが無くなったら監視側は異常とみなす、とか。監視する側ががんばるんじゃなくて、監視される側がパターンに合わせて監視されやすくするために擦り寄る、みたいな感じがあると、監視だけがいつも後回しになって、しかもリリースしたアプリはいじれないから監視しかける人だけが妙に苦労して少ない情報から監視しなければならない、とかが減っていくとハッピーですね。

一方で、監視と通知的な部分の定式化もまだまだできてなくて、これからキレイに定義されていくのかなぁとも思います。まぁきっと頭の良い人たちの中ではもうそんなのできあがってるんでしょうが、一般的な枠組みとしてはまだないのかなぁ、実はあるのかなぁ。false negative にすべき監視、false positive にすべき監視の定式化とか、傾向を踏まえた監視とか、どれもなんか経験と直感でやられているのが現状なのかなぁと思います(僕の観測範囲では)。この辺は機械学習のトピックが役立つと思ってます。単に学習 → 異常予測的な文脈だけじゃなくて、Error Analysis とかも役立ちそう。でーたさいえんてぃすとさん!はどぅーぷの分析とかだけじゃなくて、運用サイドにもでーたさいえんす眠ってますよ!

まとめ

思ったより長くなったうえにひとつひとつの説明は端折っているので、分かる部分だけ読んで下さい。普段こういうことを妄想しながら、現実と向き合って過ごしています、という話でした。そんじゃーね!