Microservices とクラウド、それからオンプレミスの可能性について新卒Webエンジニア向けにまとめてみる


# これはなに

この記事は [第2のドワンゴアドベントカレンダー](http://qiita.com/advent-calendar/2015/dwango2) のために書かれました。昨日は [Rustで動的ライブラリに依存しないLinuxコマンドを作成する方法](http://qiita.com/sile/items/e9d331b3b06565728d1d) について @sile さんが素晴らしい記事を書いて下さりました。Rust は難しくてよく分かりませんが、きっと素晴らしい言語だと思います。

さて今日は、Webサービスへの要求をみたすために、アプリケーションとインフラがどうやってコラボレーションするべきなのかについて考えます。私は 2015年新卒としてドワンゴに入社しました。この記事は私のような Webサービスのエンジニア、システムアーキテクト初心者の方々を対象に書かれています。

もちろん、ドワンゴがいまどのような開発スタイルなのかとか、どのようなインフラを持っているのか、といったこととは全く関係がありません。

# はじめに

Microservices は Martin Fowler が提案したソフトウェアのアーキテクチャのひとつです。Microservice では、単一の（モノリシックな）システムでサービスを実現するのではなく、小さなシステムを組み合わせてサービスを実現します。去年のちょうど今頃に話題になり始めましたが、開発コストの高さや複雑になるインフラ、あるいはコミュニケーションにおけるコストの増大をきらってか、Microservices の導入を諦めている方が多いのではないでしょうか。

一方で、Webサービスへの要求を満たすためには、サーバ資源を API を通じてコントロールできることが必要です。ロードバランサの仕組みを考えることで、なぜアプリケーションサーバがスケールアウトに向いているのかを解説します。また、オンプレミスのインフラがもつアドバンテージについて考えることで、新しい技術を使ったWebサービスの開発には、オンプレミス環境に引き続きアドバンテージがあることを確認します。

この記事では、サービスとシステム、アプリケーションを分けて考えています。

- サービスとは、ユーザが抱えている課題を解決する主体（あるいは解決のための助けになるもの）
- システムとは、インフラやミドルウェア、アプリケーションを総括したもの
- アプリケーションとは、HTTP フロントエンド（nginx など）とデータベースの間にあるソフトウェア

# 現代のWebサービスに求められていること

## Webサービスへの要求

私たちが普段使っている Webサービス、とくに、毎日使っているような Webサービスにはどのようなものでしょうか。Webで何を実現するかはまちまちでしょうが、その（流行の）Webサービスには以下のような特徴があることに同意してもらえるでしょうか。

### いつでも使える

いつでも使えることは、 Webサービスでは特に重要です。

落ちているWebサービスにおいては、ユーザが何かを買うことも、会員になることも、広告をクリックすることもできません。つまり、お金を稼ぐことができません。そして、たとえそれが一瞬のダウンであっても、（とくにスマートフォンの）ユーザはすぐに別のサイトに飛んでいってしまいます。ユーザから「いつも落ちているサイト（アプリ）」と認識されたら、もはやリンクをクリックしてくれさえしてくれないかもしれません。さらに運が悪く、検索エンジンのクローラがアクセスしにきたらどうなるでしょうか。検索インデックスから外されたり、ページの表示順位が落ちたりしてしまうかもしれません。そうなると、もう新しいユーザを期待することもできなくなります。

Webサービスでは、更新の反映速度よりも、データの一貫性よりも、サーバにかかる費用よりも、 ~~社員のライフワークバランスよりも、~~ 何よりも、ほんとうに何よりも、いつでも使えることが重要です。

### ページの表示が高速

つぎに、応答性能も重要です。応答性能は、スループット（単位時間に処理できるトランザクションの量）と応答時間（リクエストを送ってからレスポンスを受信するまでの時間）として測定されます。

このあたりの話はややこしいので具体的な数字を使って説明しておきます：

- 1台のサーバに限界までリクエストを送ります。このとき秒間で 3,000 リクエストを処理できたとすると、サーバを2台に増やすことで秒間 6,000 リクエストを処理することができるでしょう。これがスループットです。
- まったくアクセスがないサーバに、1リクエストを送ってレスポンスを受け取ります。このとき 200ms かかったとすると、サーバを何台増やそうとも（いくらスケールアウトしようとも） 200ms でレスポンスが返ります。これが応答時間です。

スループットが小さいシステムは、SNS でのいわゆる "buzzり" に起因する突然のスパイク（負荷増大）に耐えることができません。負荷のスパイクはシステムに歓迎できるものではありませんが、SNS が発達している現代では必然的に発生します。また、急速に（しかも無料で！）広まるため高い宣伝効果を持ちます。あなたが普段接するディレクターも "buzzり" を狙っているかもしれません。"buzzって" いる1分間と、通常の1分間を比較してみると、ビジネス上価値のあるのは "buzzって" いるときです。"buzzる" 前に大量のサーバを用意すればその分スループットは上がるものの、"buzzって" いないときもその分コストがかかります。また、どの程度負荷がスパイクするかは、事前に予測することが難しいです。

また、応答時間も重要です。応答時間が長いWebサービスは、ユーザにとって、落ちているのと区別がつかないからです。極端な例ですが、表示に 10秒もかかるWebサイトなんて、誰も見たがらないでしょう。しかしながら、応答時間はサーバの台数を増やすことで短くはなりません。応答時間を短くするためには、キャッシュやデータベースを上手に使うなど、システムアーキテクチャの変更が必要でしょう。

### 頻繁に新しい機能が追加される

Webサービスの裏側にあるソフトウェアは、パッケージとして販売されるソフトウェアと違って、継続的に新しい機能が追加されます。何度も言われていることですが、Webサービスはリリースされたら終わりではありません。絶えず新しい機能を追加することで、ユーザを飽きさせないことが必要です。ビジネスとして考えると、競合他社のサービスとの差別化が重要になるでしょう。

システム的にはデプロイ回数の最大化として捉えられることが多いように思えます。継続的インテグレーション（CI）やワンクリックデプロイという言葉を聞いたことがあるかもしれません。

### ネイティブアプリが提供されている

ネイティブアプリの提供において、アプリケーション側で考えなくてはならないのは API の設計です。ここでは、とくに Web API について考えてみます。

ネイティブアプリケーション向けの API には、ある画面を描画するために必要な情報すべてが含まれている必要があります。PC のブラウザであれば、Ajax によって遅延ロードしていたデータも一緒に返す必要があるかもしれません。さらに、API の変更はときにクライアントを破壊します。このため、前のバージョンの API を一定期間提供する必要があるでしょう。しかしながら、古い API を提供し続けるというのは、コードベースに互換性のためのコードが残り続けることを意味します。

あるいは、ユーザエクスペリエンス上の都合でサーバにあるデータを更新した後、再度取得することなく表示されるデータを変更したいかも知れません。これは、サーバアプリケーションで処理すべきことをクライアントアプリケーションでやっているため、サーバアプリケーションのドメインがクライアントアプリケーションに流出してしまっていると考えられます。

# アプリケーションが実現すべきこと

## 巨大になるアプリケーション

### アプリケーションが巨大になる理由

長く運用されているWebサービスは、頻繁な機能要求に応えるために対象とするドメインが広くなっていきます。この場合ドメインとは問題領域のことですから、つまりサービスが解決する課題の数が増える、ということです。要求が増えた結果としてコードベースがどんどん大きくなります。このようなソフトウェアをよく、モノリシック（monolithic）なシステムといいます。モノリシックとは、「単一な」、「一枚岩の」といった意味をもつ言葉です。

### モノリシックなアプリケーション

さて、モノリシックなアプリケーションにはこのような特徴があります：

- コードベースが巨大
- ビルドに長い時間がかかる
- 単一のプログラミング言語で記述されている

実は、モノリシックという言葉は SOA（サービス指向アーキテクチャ）以前の「何でも」出来るエンタープライズアプリケーションのことでした。このため登場は Microservices よりもずっと古いものですが、ここでは説明するのにちょうど良いので使っています。

モノリシックなアプリケーションにはこのような問題が指摘されがちです：

- 内部の依存関係が複雑（あるモジュールを変更したときの影響範囲が広い）
- 使われていないコードが残っている（削除して良さそうだが本当に削除して良いのか分からないコードがある）
- テスト（とくに、インテグレーションテスト）がない

### モノリシックなアプリケーションの欠点

モノリシックなアプリケーションでこのような問題が起こるのはなぜでしょうか。これは、ビジネスとしてWebサービスを提供しているせいである、ともいえます。Webサービスでは頻繁に新しい機能を提供する必要があります。巨大なコードベースを持つ、変更が難しいアプリケーションで新機能を追加しようとすると、無理矢理依存すべきではないモジュールに依存してしまったり、既存のコードをコピペして機能を追加するといったことが行われてしまいます。

また、追加人員が負債の原因となることもあります。代表的なものは、新しいエンジニアがアーキテクチャについて理解が浅いときでしょう。アーキテクチャを理解しないままコードベースに手を入れてしまうと、コードベース内部の依存関係はどんどんぐちゃぐちゃになります。もっとも、巨大なアプリケーションは複雑で理解するのが難しいので、そのようなパートタイムのエンジニアを責めるのは酷かもしれません。しかしながら、アプリケーションのアーキテクチャにとっては良くないことです。このようなコードはいわゆる技術的負債となってしまうかもしれません。

逆に、追加人員は負債の結果であると考えることもできます。コードベースを変更するコストが増大したために、計画された時間や人数で目標を達成できなくなる、ということです。このような状況に陥ったシステムはよくデスマーチを引き起こすため、どんなエンジニアも関わりたくありません。

注意して欲しいのは、これらの問題はアプリケーションがモノリシックであるから引き起こされたわけではない、ということです。モノリシックなアプリケーションであっても、このような問題を必ず抱えているとは限りません。エンジニアがきちんとアーキテクチャに気を払い、継続的にリファクタリングが行われていれば、モノリシックでまったく問題ないのです。

では、どうして Microservices が話題になったのでしょうか。Web業界の先人たちは、システムが巨大になることは避けられないから、コントロール可能な大きさに切り分けようとしたのです。

## Microservices の導入による解決

### Microservices とはなにか

Microservices という名前のオリジナルは Martin Fowler のブログポスト [Microservices](http://martinfowler.com/articles/microservices.html) です。

Microservices とはサービスを複数の小さなシステムに分割することでモノリシックなシステムが持っていた欠点を克服しようとするアプローチです。SOA（サービス指向アーキテクチャ）との類似が指摘されることがありますが、私は以下の点に違いを見出しています：

- ハードウェアのクラッシュを受け入れること
- プロプライエタリな ESB ではなく REST API や RPC（gRPC や JSON RPC）といったオープンな技術を採用すること

SOA ではとくに一貫性を重視した設計が多いように思えます。たとえば、分散トランザクションは難しく複雑なため（計算量の観点から）高コストですが、SOA にはなくてはならないものでしょう。一方で、Webサービスにおいては何よりも応答性能が重要であることは先に示しました。また、SOA と直接関係があるわけではありませんが、一貫性を諦めることによってハードウェアのクラッシュを受け入れることができるようになります。このため、（安価なサーバを使って）低コストにスケールアウトできることがより重要になります。最後に、技術に対するオープンマインドというか、プロプライエタリなものよりオープンなものを好む姿勢も指摘しておきます。

### Microservices によるトレードオフ

ほかのソフトウェアアーキテクチャと同様に、Microservices にもトレードオフが存在します。Martin Fowler はベネフィット（利益）とコストについて以下のように[説明しています](http://martinfowler.com/microservices/)：

#### ベネフィット

- 強いモジュール境界（Strong Module Boundaries）
- 独立したデプロイ（Independent Deployment）
- テクノロジのダイバーシティ（Technology Diversity）

Microservices におけるシステム群では、モジュール間の連携は（明示的に公開された） API を通じて行われます。このため、モジュール間の依存を小さく保つことができます。また、インターフェースが分離することで、システムのデプロイは独立に行うことができます。これらはインターフェースを分離することによる一般的なメリットとも言えるでしょう。

モジュール同士の通信プロトコルに HTTP や AQMP のような規格化された汎用的なプロトコルを採用することで、API をモジュールの実装から切り離すことができます。このとき、システムには自由な基盤技術（言語やミドルウェア、オンプレミスか AWS かといったインフラ）を採用することができるようになります。

#### コスト

- 分散システム（Distribution）
- 結果整合性の実現（Eventual Consistency）
- オペレーションの複雑化（Operational Complexity）

一方で、分散システムを構築する難しさもあります。 CAP 定理では、「Consistency（一貫性）」「Availability（可用性）」「Partition-tolerance（分断耐性）」のみっつを同時に満たすことはできない、とされています。

Webサービスでは、この記事でも確認したように可用性が最重要です。また、利用者の増大にともないシステムをスケールアウトできることが必要ですから、分断耐性は欠かすことができません。したがって、（消去法で）一貫性が犠牲となります。実際の Webサービスにおける影響としては、たとえば「お気に入りしたコンテンツがお気に入り一覧に表示されるまでに時間がかかる」「ブロックしたユーザのコンテンツがタイムラインに表示される」などが考えられます。

一方で、一貫性が最重要とされるサブシステムも存在します。たとえば課金システムです。課金システムでは、データに不整合が起こるぐらいならば落ちてくれていたほうが安心できます。もっとも、ディレクターが激怒するという別の問題が発生しますが（笑）

#### 具体的な注意点

より具体的な注意点については @hirokidaichi 氏が [microservicesに分割する際に注意するべき5つのこと](http://qiita.com/hirokidaichi/items/bf81f1f6f7ca3d694aae) で解説されています。私が解説するよりもずっと分かりやすいのでこちらをご参照ください。

## Microservices は銀の弾丸ではない

本稿の最初でWebサービスに求められることとして、以下の要素を確認しました：

- いつでも使える
- ページの表示が高速
- 頻繁に新しい機能が追加される
- ネイティブアプリが提供されている

Microservices を上手に導入することで、サービス全体がダウンするようなことは少なくなるでしょう。また、新機能のリリース頻度は上がることが期待できます。その新しい機能はしばしば新しいシステムとして設計され、既存のシステムとは独立にデプロイすることできます。また、ネイティブアプリ向けの API も、たとえば API Gateway を使うことで互換性を担保しつつコードベースに負債が残りにくいような作り方ができるようになります。（Netflix が発表した [オリジナルの API Gateway](http://techblog.netflix.com/2013/01/optimizing-netflix-api.html) は API の管理において問題を抱えがちなことが分かっています。詳細は [Building Microservices](http://www.amazon.co.jp/dp/1491950358) をご参照ください）

しかしながら、負荷のスパイク問題が解決していません。また、新しいシステムをリリースするたびにサーバを購入していたら、インフラコストが飛躍的に増大してしまいます。サービスを構成するシステムがそれぞれ予想されるスパイクに耐えられる十分なサーバリソースを確保していたとしても、将来の "buzzり" を受け止めきれるかまったく分かりません。どんな負荷見積もりも、SNS には無力なのです。また、このようなサーバリソースはほとんど使われないので、経営層からするといったいどこまで投資すれば良いのか分かりません。

# インフラが実現すべきこと

## スケールアウトを実現する

### スケールアウトのしくみ

システムがリクエスト数にあわせてスループットを増大させることができれば、スパイクしたリクエスト負荷をさばくことができます。スループットを増大させる方法としてスケールアウトが有効なことについて、もう少し詳しく解説しておきます。

スケールアウトは、ロードバランサ（Load Balancer、LB）を使ってリクエストを複数のサーバに振り分けることで実現します。リクエストはまず、ロードバランサによってどのサーバに振り分けるか決定されます。決定ロジックは複数から選択できますが、セッション数がいちばん少ないサーバか、Load Average（CPU への負荷とだいたい同義）のいちばん小さいサーバにする、というのが一般的なように思えます。もちろん、サーバのハードウェアスペックが異なる場合、これを考慮する必要があります。サーバは、ロードバランサによって転送されてきたリクエストを処理し、レスポンスを返します。このとき、ロードバランサを通さないで直接返すこともできます（DSR 構成）。

このロードバランサに接続されるサーバの台数を増やすことで、システムが処理可能なスループットを増やすことができます。

オンプレミス環境では、ロードバランサはアプライアンスとして提供されているものを採用します。nginx や Apache WebServer を利用したリバースプロキシで代用することもできますが、信頼性やコストパフォーマンスの観点からプロダクション環境では採用されづらいように思えます。ロードバランサ周辺のネットワーク構成を考えてみると分かりますが、ロードバランサは SPOF（単一障害点）となり得ます。したがって二重化することで高可用性を実現します。

一方で IaaS ではサービスとして提供されているでしょう。たとえば AWS では Amazon Elastic Load Balancing という名前で提供されています。可用性はクラウド事業者から SLA として提示されていますが、実装の詳細はドキュメントに記載されている以上のことは分かりません。

ちなみに、代表的な IaaS には Amazon Web Services （以下 AWS）やさくらのクラウドなどがあります。一方で、自社でデータセンタを持ったり機材を持つことをオンプレミス（on _one's_ premise）と呼びます。

### API によるサーバ資源のコントロール

オンタイムで（つまりリクエスト数の増大と同時に）サーバを増やしたり減らしたりするにはどうしたら良いのでしょうか。まず、サーバ資源を API を通じてコントロールできることが必要になります。API を使ってサーバ資源をコントロールすることを、よくオーケストラレーションと呼びます。

オーケストレーションの実装のうち、よく知られているものに Amazon Cloud Formation があります。Amazon Cloud Formation ではサーバ（EC2）だけでなくロードバランサ（ELB）やデータベース（RDS など）をひとつの JSON ファイルから展開することができます。また、AWS は全般的に Web API が提供されているため、この API を使ってサーバ資源をコントロールすることができます。

### サーバ仮想化技術

実際にサーバを仮想化する技術として代表的なものは次のふたつです：

- ハイパーバイザを使ったサーバ仮想化技術（VMWare ESXi、OpenStack Nova など）
- コンテナ型仮想化技術（Docker、rkt など）

どのような仮想化技術をもったインスタンスをオーケストラレーションしているのか、というのはシステム特性やデプロイ方法、そして障害時の問題切り分けなどの時に考慮しておく必要があります。ハイパーバイザは、軽量な OS に仮想化のためのソフトウェアを実装したものです。しかし、ハイパーバイザは（とくに I/O において、ときに無視できない）オーバーヘッドを生みます。これをきらって、エンタープライズで長らく使われてきたコンテナ型の仮想化技術がWebサービスのバックエンドとしても台頭してきました。

それぞれの仮想化技術の詳細については本稿の範囲を超えるため、これらを専門に扱った技術書などをご参照ください。

## 負荷のスパイクに耐える

### サービスのスケールアウトとその限界

Webサーバを構成するサーバにはこのような種類に分かれています：

- アプリケーションサーバ（Webサーバ）
- キャッシュサーバ
- リレーショナル・データベース（RDB）

ここでのキャッシュサーバは Redis や memcached のような KVS を想定しています。

スパイクに耐えられるようなスケールアウトを実現できるということは、サーバが仮想化されているということですから、ここでは慣例にならって仮想化されたサーバのことをインスタンスと呼びます。

#### アプリケーションサーバのスケールアウト

アプリケーションサーバは、ロードバランサの裏側に配置することでスループットを増やすことができます。必要な作業は、インスタンスの起動と、ミドルウェアのインストール（プロビジョニング）およびアプリケーションのデプロイ、それからロードバランサへの接続です。

スケールアウトするときは、インスタンスの負荷をモニタリングして、一定の負荷を超えたら自動的に新しいインスタンスを起動し、一定の負荷を切ってからシャットダウンします。ここでは新しいインスタンスについて考えます。インスタンスが立ち上がったら、必要なミドルウェアのインストールおよびセットアップを行います。ミドルウェアのセットアップやインストールのことをまとめてプロビジョニングと呼びます。プロビジョニング済みのインスタンスをイメージとして保存しておくことで、新しいインスタンスを一気にプロビジョニング済みの状態にすることもできます。

インスタンスが立ち上がったら、アプリケーションのデプロイを行い、必要であればミドルウェアの再起動を行います。最後に、ロードバランサへ接続してリクエストを振り分けて貰います。システムを監視するツールに接続する必要もあるかもしれません。

複雑に感じるかもしれませんが、アプリケーションサーバはキャッシュサーバと並んでスケールアウトが簡単です。台数を増やせばスケールするというのは、ステートレスな HTTP のおかげですね。

#### キャッシュサーバのスケールアウト

キャッシュサーバのスケールアウト方法は、アプリケーションサーバとよく似ています。

スケールアウトが必要なキャッシュサーバは通常、クラスタリング構成を採用します。クラスタに参加するとき、他のサーバからデータを受け取る必要があります。このとき、ある程度大きなトラフィックがネットワークを流れるために、クラスタ全体で許容できるスループットが一時的に低下することになります（どの程度低下するかは実装に強く依存します）。また、スケールアウトの途中でほかのノードがダウンすることも考えなくてはなりません。おそらく自動的に復旧するような仕組みを導入しているでしょうが、このとき許容できるスループットはさらに低下してしまいます。

新しくできたキャッシュサーバがアプリケーションから接続を受けつけるための方法はいくつもあります。また、ミドルウェアによってサポートしている方法にも違いがあります。システムの要求に応じて使い分けたり組み合わせたりする必要がありますが、ロードバランサや HA Proxy を使う方法が代表的でしょう。

応答時間を短くするために、キャッシュサーバに大きく依存するようなシステム・アーキテクチャを採用することもあると思います。このとき、キャッシュサーバがダウンするとサービスは落ちるため、サービスの可用性はキャッシュサーバに依存します。キャッシュサーバはどの程度ダウンしても良いのか（あるいはいけないのか）というのは、インフラ投資を最適化するうえでも必要なことです。

しかしながら、ただ台数を増やせば良いという特性でいえば、キャッシュサーバはアプリケーションサーバと同じです。

#### リレーショナル・データベースのスケールアウト

MySQL や PostgreSQL といったリレーショナル・データベースはスケールアウトできません。しかしながら希望はあります。

リクエスト負荷の解決策としては、Webサービスでは書き込みよりも読み取りのほうが圧倒的に多いです。このため、書き込み専用のノード（master）と複数の読み取り専用のノード（slave）を使うことで、読み取り性能を伸ばすことができます。これが、いわゆる master-slave 構成です。slave は比較的簡単に増やすことができるので、読み取り性能だけが問題のときは、master-slave 構成がとても向いています。

しかしながら、master-slave 構成にはレプリケーション遅延という構造的な問題があります。master に書き込んだはずのデータが slave から読み取れないことがあるのです。これは、master から slave に書き込まれたデータが転送されるまでの、わずかな時間差が原因です。

また、容量やレコード数の方向にスケールしたいときは、シャーディングが有効です。シャーディングには垂直シャーディングと水平シャーディング（パーティショニング）のふたつがあります。いずれの方法でも、シャーディングを行うことでインデックスの範囲が区切られるため、高速なレスポンスを維持できることがあります（クエリに依存します）。

データベースはとても奥が深いため、詳細については解説されている技術書などを参照してください。

### ネットワークの仮想化

ネットワークの仮想化についても少し触れておきます。

サーバ資源だけでなく、ネットワークも仮想化することができます。具体的には OpenFlow などの SDN（Software Defined Network）に対応したアプライアンスを導入することになります。ネットワークを仮想化するメリットはいくつかありますが、設定変更を柔軟に行えることと、ネットワーク・セグメントを細かく切ることによるセキュリティ向上でしょう。

仮想化されたネットワークでは、API を通じて設定を変更することができます。この API は（ベンダ固有の機能以外は）標準化されているため、ベンダごとに、違う言語や方法を使う必要がありません。設定変更が簡単になることで、より細かく（仮想的な）プライベート・ネットワークを構築できます。これを適切に設定することで、あるサービスを提供するサーバが侵入を受けたとしても、他のセグメントにあるサーバにはネットワーク的に到達不可能ですから、被害を限定できます。これによってセキュリティの向上を期待できるでしょう。

ネットワークを仮想化するデメリットのうち、いちばん心配されているのはパフォーマンスでしょう。ネットワークを酷使しないようなサービス特性であれば、SDN は受け入れられやすいと思います。

## オンプレミス環境の利点

### オンプレミス環境での仮想化

負荷のスパイクに対応するためにはスケールアウトが必要で、スケールアウトするためには API によってサーバ資源をコントロールできる必要があることを確認してきました。クラウド事業者が提供する IaaS ではふつう API によってサーバ資源をコントロールできます。一方で、オンプレミス環境でサーバ資源をコントロールできるようにするための基盤ソフトウェアを導入する必要があります。これには、代表的なものに VMWare vCenter や OpenStack、Kubernates などがあります。

しかしながら、オンプレミス環境には、IaaS にはないメリットもあります。現実的にはコストが指摘されることが多いですが、私は以下ような優位性があるのではないか、と考えています：

- ベアメタル（仮想化されていない）サーバによるスケールアップ
- 動的なサービスレベル変更
- 柔軟なミドルウェア選択
- データやトラフィックの増大に対するコスト構造上の利点
- ハードウェア購入による利益圧縮

### ベアメタル（仮想化されていない）サーバによるスケールアップ

オンプレミス環境ではスケールアウト（サーバの台数を増やす）だけでなく、スケールアップ（サーバのスペックを上げる）こともできます。たとえば、AWS EC2 の汎用インスタンスでは、最高でも 40vCPU 、メインメモリ 160GB のインスタンスしか選択できません（記事執筆時、[参考 URL](https://aws.amazon.com/jp/ec2/instance-types/)）。一方で、オンプレミス環境では 144 スレッド同時実行可能な CPU と 6TB のメインメモリを搭載したサーバを導入することもできます（記事執筆時、[参考URL](http://www8.hp.com/jp/ja/products/proliant-servers/product-detail.html?oid=8085087)）。さらに、このようなサーバにテラバイトクラスの FusionIO を搭載することもできます。性能の比較は難しいですが、後者のほうが十数倍から数十倍程度高速だと思われます。

スケールアップの恩恵を受けるのは主にデータベース、とくに MySQL のようなリレーショナルデータベース（RDB）です。RDB はスケールアウトが難しく、master-slave レプリケーションは常に遅延しており（わずかですが、ときに致命的です）、水平分割（シャーディング）や垂直分割はスキーマの変更をよりいっそう難しくします。

伝統的に、スケールアップは高コストでした。しかし、遅延やシャーディングを一切考えなくてよいとなると、開発や運用がずいぶん楽になります。総コストで考えると、スケールアップのほうが安上がりとなる可能性は十分にあります。さらに重要なことは、レプリケーション遅延やシャーディングのためのコードは、頻繁にバグを引き起こすものの、カスタマバリューには繋がりません。しかも、サーバのスペックが向上したときには負債となり、間違いなく将来のエンジニアを苦しめます。

一方で、そもそもデータストアとしてスケールアウトがしやすいミドルウェアを選択することもできます。たとえば KVS はスケールアウトが有効なことが多いです。KVS の特性は、キャッシュやセッションストアとよくマッチするでしょう。

しかしながら、たとえば AWS であれば Amazon RDS や AuroraDB クラスタのような選択肢があります。しかしながら、これらのサービスのスケールアップには限界があります。

### 動的なサービスレベル変更

クラウドサービスが提供するサービスレベルは、提供されているものから選択することになります。たとえば、AWS EC2 では [SLA がひとつ](https://aws.amazon.com/jp/ec2/sla/) 提示されており、ほかにスポットインスタンスというオプションが用意されているため、実質的には 2 段階から選択できるといえるでしょう。一方でオンプレミス環境では、インフラを好きなようにコントロールできるため、自由にサービスレベルを決定できます。

サービスレベルは、そのサービスが提供できる価値と、サービスレベル向上にかかるコストがクロスする点となるべきです。これは、ユーザが受け取るバリューや他のサービスとの依存関係から算出されるので、シンプルに損益分岐点とはなりません。たとえば、ログイン機能はまったく利益を生みませんが、ユーザがログインできないと大きな損失になることを考えると、サービスが大きな価値していると考えることができます。

現実的にはこの分岐点を計算することは難しいですが、ひとつ言えるのはサービスレベルはほんらい固定すべきではないということです。サービスの重要性やサービスレベルを向上させるときのコスト構造の変化にあわせて、動的に変化すべき指標と考えられます。クラウド事業者のサービスレベルは固定なため、このようにサービスレベルを変更することはできません。（じつは、この考え方はリアクティブプログラミングに通じています。）

Microservices に限らずですが、複数のシステムに依存するサービスのサービスレベルはかけ算となることにも気をつけなくてはなりません。あるサービスが、可用性 99.99% で提供される 3 つのシステムに依存しているとすると、そのサービスの可用性は 99.99% ^ 3 = 99.97% となります。サービスが高可用性を実現するためには、より高い可用性をもつシステムを組み合わせる必要があります。

一方で、モノリシックなシステムで提供されるサービスは、そのシステムの可用性がそのままサービスの可用性となります。したがって、99.97% の可用性を実現するのであれば可用性 99.99% の（おそらく高コストな）インフラを使う必要はありません。

そのサービスは SLA にあるサービスレベルで十分でしょうか。あるいは、SLA にあるサービスレベルが必要でしょうか。動的なサービスレベルの更新は、オンプレミス環境におけるメリットのひとつであるといえるでしょう。

### 柔軟なミドルウェア選択

また、オンプレミス環境は柔軟なミドルウェアの選択を可能にします。たとえば、Aerospike は SSD の利用を前提とする高速・低遅延な KVS 型のデータベースです（データストアとしてメインメモリも利用できます）。Aerospike はファイルシステムを利用せず、SSD ヘ直接アクセスします。この他にもハードウェアリソースを使い切るためのさまざまなチューニングが施されています。このようなミドルウェアの利用を考えたとき、オンプレミスはミドルウェアに最適化したサーバを用意できるという点で優れています。

しかしながら、[Aerospike も AWS へデプロイできます](http://www.aerospike.com/docs/deploy_guides/aws/install/)し、最近では GPGPU が利用できる IaaS も増えてきました。[AWS でも利用できます](http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/using_cluster_computing.html)。

ほかにも、特定のハードウェアを必要とするアプリケーションをデプロイできるのはオンプレミス環境の魅力といえるでしょう。

### データやトラフィックの増大に対するコスト構造上の利点

データセンタでは、データをストレージサーバに格納しています。ストレージサーバはサーバの種類のひとつで、多数のハードディスクが入るように設計されています。クラウドサービスのデータセンタでもオンプレミスのデータセンタでも同様に、ストレージサーバ本体の導入コストが高く、ストレージそのもの（ハードディスク）は比較的低コストです。

多くのクラウドサービスでは、データ容量に対するコスト（利用料）は線形に増加します。このように、ストレージの利用料を容量に比例させるためには、小容量を使う利用者向けには割安に課金し、大容量を使う利用者へ割高に課金することになります。したがって、とくに大容量のデータを保存する必要があるサービス事業者にとって、オンプレミスのコスト構造は魅力的です。

トラフィックについて、クラウドサービスの方が割高になるケースは限定的ですが存在します。たとえば、AS 番号を取得してプロバイダと Peer 接続する、といったコスト削減方法がとれないことや、サービスレベルを回線容量にあわせて変更することでコストを固定費のなかに押さえ込むことができない、などの理由が考えられます。また、クラウド事業者といえどもバックボーンの投資には多大な費用がかかり、またオンデマンドに増やすこともできないので、きっちり課金することでトラフィックをコントロールしたい、というインセンティブが働いているのかも知れません。

### ハードウェア購入による利益圧縮

サーバ機器やネットワーク機器は減価償却が必要なことが多いと思われます。これは現在や将来の利益を圧縮することで節税効果を生むかもしれません。詳細についてはお近くの税理士にお尋ねください。

# あわせてよみたい

- [インフラ/ネットワークエンジニアのためのネットワーク技術&設計入門](http://www.amazon.co.jp/dp/4797373512)
- [理論から学ぶデータベース実践入門](http://www.amazon.co.jp/dp/4774171972)
- [Patterns of Enterprise Application Architecture](http://www.amazon.co.jp/dp/0321127420)
- [Design an SOA solution using a reference architecture](http://www.ibm.com/developerworks/library/ar-archtemp/)
- [Building Microservices](http://www.amazon.co.jp/dp/1491950358)
- [Migrating to Cloud Native Application Architectures](https://pivotal.io/jp/platform/migrating-to-cloud-native-application-architectures-ebook)

# おわりに

だいぶざっくりまとめましたが、いかがでしたでしょうか。誤りがあればコメントでご指摘いただければ幸いです。

明日は @kouki_dan がクラウドネイティブ・アプリケーションについて書くそうなので楽しみにしています。