「これからの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とかも役立ちそう。でーたさいえんてぃすとさん!はどぅーぷの分析とかだけじゃなくて、運用サイドにもでーたさいえんす眠ってますよ!

まとめ

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