目次


流行語の真相: マイクロサービスのパターンの略歴

過去のソフトウェア・デザイン・パターンがマイクロサービスの作成に及ぼす影響を探る

Comments

はじめに

商用アプリケーションの開発をめぐって最新の話題となっているのは間違いなく、マイクロサービスです。マイクロサービスという新しい流行語がアジャイル、DevOps、RESTful といった以前の流行語に取って代わり、履歴書やカンファレンスで欠かせないトピックとなっています。けれども、マイクロサービスは単なる一過性のブームではありません。実際のところ、マイクロサービスはその由来となっている概念のすべてを集結させた進化であり、アプリケーション開発における長年の問題の多くを切り抜ける手法となる可能性を見せ始めています。マイクロソフトサービスが進化した過程、分野、理由をしっかり把握するには、この記事と併せて、MicroservicesTV のエピソード 13エピソード 14 をご覧になってください (以下を参照)。

起源

マイクロサービスという進化を理解するためには、過去に遡って、マイクロサービスの概要、マイクロサービスが置き換えたもの、そしてマイクロサービスが必要になった理由を確認する必要があります。まずは、1980 年代の初めに遡りましょう。この頃、リモート・プロシージャー・コール (RPC) という初の主要なシステム分散テクノロジーが導入されました。RPC は、Sun Microsystems の初期の ONC RPC を背後で支える概念となっただけでなく、DCE (1988 年) と CORBA (1991 年) の背景にある基本的な考え方でもありました。

これらのテクノロジーのそれぞれで基本的な考え方となっていたのは、リモート呼び出しを開発者に透過的にすることです。開発者がプロシージャー呼び出しやメソッドを呼び出す際に、それがローカルであるかリモートであるかを懸念する必要がなくなれば、複数のマシンにまたがる大規模なシステムを構築して、当時のシステムに影響していた処理やメモリーのスケーラビリティーの問題を回避することが可能になります (当時、最もよく使われていたプロセッサーは、64K のアドレス空間しかない 16 ビットのプロセッサーであったことを考えてください!)。

プロセッサーが改善され、ローカルのアドレス空間が大きくなるにつれ、スケーラビリティーの問題の重要性は低くなってきました。その上、DCE と CORBA の一連の大規模な実装も登場しました。このような状況の中、アーキテクトは分散化されたコンピューティングに関する重要な教訓を得ます。

分散できるからと言って、必ずしも分散する必要はない。

大規模なメモリー空間が当たり前になったことで明らかになったのは、メソッドを複数のマシンに分散するために選ぶ方法が適切でないと、システム・パフォーマンスに悲惨な影響を及ぼすということです。それまでは分散化が推進されてきましたが、その結果は、非常におしゃべりなインターフェースを持つ多くのシステムという形になって表われました。オブジェクト指向言語で変数の getter と setter を分散する場合でも、かなりのやり取りが行われることになりました。このようなシステムでは、ネットワーキングのオーバーヘッドが分散によってもたられるメリットを大幅に上回ります。

このことが、上述の教訓に応じた最初のパターンにつながります。このパターンは、私が John Crupi 氏との研究でも、Martin Fowler 氏との研究でも共通して発見したパターンです。いずれの場合も、私たちの出発点となったのは、Erich Gamma 氏の著書『Design Patterns: Elements of Reusable Object Oriented Design』で説明されている Facade パターンです。Facade パターンでは、大規模システムに実装されている構造化されていない複数のインターフェースを、それよりも構造化された 1 つのインターフェース内にカプセル化して「おしゃべり」を少なくします。つまり、システムで使用するインターフェースの多様性を軽減することを目的としています。私たちが開発した Session Facade 手法では、Facade パターンを分散型システムに適用する手段として、サブシステム全体のきめの粗い主要なインターフェースを識別し、それらのインターフェースだけを公開して分散します。

私たちは初期の Session Facade 手法を、Enterprise JavaBeans (EJB) を使用して実装しました。Java だけで作業する間は問題なかったのですが、EJB は複雑で、デバッグするのが困難です。しかも、他の言語と相互運用することも、他のベンダーの製品で使用することさえもできません。相互運用性の欠如から直接つながったのが、2000 年代半ばまで続いた次の取り組みです。この取り組みは、サービス指向アーキテクチャー (SOA) と呼ばれるようになりました。ただし、当初の SOA はそれほど大それた用語で呼ばれるようなものではなく、Simple Object Access Protocol (SOAP) という名前の「うまくいく方法のうち、もっともシンプルな方法で行え」としての取り組みとして開始され、1999 年に Microsoft によってリリースされました。

SOAP は基本的に、オブジェクト・メソッドを HTTP を介して呼び出す方法でしかありません。SOAP では、2000 年代初期のコンピューティング分野で生まれた 2 つの成果物が利用されました。1 つは、当時の企業ネットワーク内で次第に広がっていた HTTP のサポート、そしてもう 1 つは、このサポートにはテキスト・ベースのネットワーク呼び出しをロギングおよびデバッグするメカニズムが含まれているという事実です。

SOAP を中心とした取り組みが盛んになったことから、各種のプラットフォーム上で各種の言語で実装されたさまざまなシステムを容易に相互運用できるようになりました。SOAP はメソッド呼び出しに他の概念を幾重にも重ねて相互運用を可能にするという手法です。けれども、SOA ではこうした単純なメソッド呼び出しだけでは対処しきれない分野がありました。つまり、すでに複雑なプロトコルだと思われていたところに、例外処理、トランザクション・サポート、セキュリティー、デジタル署名のすべてが追加されたのです。このことが、次の重要な教訓につながりました。

分散された呼び出しをローカルの呼び出しであるかのように振る舞わせると、最終的には悲惨な結果に終わる。

業界全体が徐々に、SOAP と WS-* 標準に伴うプロシージャー型の層化概念に背を向けるようになってきました。代わりに広く採用されるようになってきたのが、Representational State Transfer (REST) です。REST の起源は、2000 年の Roy Fielding 氏による博士論文に遡ります。REST の根本原則は余りにも単純で、HTTP を HTTP として扱うというものです。つまり、プロシージャー呼び出しのセマンティクスを HTTP の上に重ねるのではなく、作成、読み取り、削除、更新のセマンティクスに関して指定されたものとして HTTP 動詞を扱います。また、REST では、一般に認められた Web 原則である URI に従って一意のエンティティー名を指定する方法についても規定しています。

REST に移行すると同時に、業界は Java Platform, Enterprise Edition (JEE) と SOA の世界での別のレガシー、つまり大規模なアプリケーション・サーバー・ファームからも離れていく傾向を見せていました。1999 年に初めて Enterprise Java (おかしなことにバージョン 1.2 として) 導入されて以来、アプリケーション所有者とアプリケーション管理者の間には張り詰めた緊張感が続いていたからです。

JEE が導入されたことで、多くの企業は 1 台のアプリケーション・サーバーでさまざまなアプリケーションをホストするという概念を採用する方向に移っていきました。なぜなら、この概念は、メインフレームの世界での既存の IT モデルと同様だからです。具体的には、単一の運用グループが、Oracle 製または IBM 製の同一のアプリケーション・サーバーの「ファーム」を制御、監視、保守し、別の部門のアプリケーションをそのファームにデプロイするというモデルです。このような標準化と一貫性は、運用チームにとって非常にありがたいことであり、全体的な運用コストも削減される結果となりましたが、その一方で、アプリケーション開発者との対立も生まれました。開発およびテスト環境が大規模になって、環境の作成が困難になり、運用チームの関与が必要になってきたからです。運用チームが関与するということは、多くの場合、新しい環境を作成するのに何か月も必要になることを意味します。そうなると、プロジェクトの進行が遅くなり、開発コストも増加します。さらに、このような環境では開発チームの制御が利かないため、環境によってアプリケーション・サーバーのバージョン、パッチ・レベル、アプリケーション・データ、ソフトウェア・インストールが異なるといった事態も起こりがちでした。

開発者が好むのは、規模が小さく、軽量のアプリケーション・プラットフォームです (通常は、Tomcat や Glassfish などのオープンソースのアプリケーション・サーバーが好まれます)。それと同時に、制御の反転や依存性注入などの手法がよく使われるようになってきたことから、Spring プラットフォーム本来の単純さが支持されて、JEE の複雑さが敬遠されるようになってきたのです。このような状況の中で、開発チームが学んだのは、開発環境、テスト環境、そして本番環境をできるだけ近くにセットアップし、それらの環境のすべてで、自分たちで一貫してアプリケーションを構築してデプロイすることができれば、開発時間が短縮されるだけでなく、環境の不一致に起因するあらゆるクラスのエラーを排除できることから、エラーが起こりにくくなるということでした。つまり、次の教訓につながったのです。

可能な限り、プログラムとそのランタイム環境を完全な自己完結型にする。

以上の 3 つの教訓が、Fowler 氏が説明するマイクロサービスのあるべき形の基本となっています。Fowler 氏のマイクロサービス設計原則の 1 つは、マイクロサービルは「ビジネス機能を中心に編成される」というものです。この原則は、「分散できるからと言って、必ずしも分散する必要はない」という教訓に直接端を発しています。Facade パターンのさまざまな具現化に共通する概念は、システムまたはサブシステムごとに固有の外部 API を定義するというものです。その根底には、この API がビジネス主導であるという意味があります。Fowler 氏はそのコンテキストを明確にしています。

一部の開発チームにとっては、ビジネス主導の意味を理解することが開発の障害になりがちです。開発チームはビジネス・インターフェースという観点での設計にはまったく慣れていないため、結局は技術的なインターフェース (ログインやロギングなど) を設計しがちです。このような場合、Eric Evans 氏の著書『Domain Driven Design』で説明されているパターンのうちのいずれかを適用できると結論した開発チームは少なくありません。特に、彼の Entity パターンと Aggregate パターンは、マイクロサービスに直接マッピングされる特定のビジネス概念を特定する際に役立ちます。同じく、Services パターンも、単一のエンティティーに対応していない処理や、マイクロサービスに必要となる Entity ベースの手法に集約されない処理をマッピングする方法となります。

同様に、Fowler 氏の「スマート・エンドポイントとダム・パイプ」を採用するというルールは、EJB や SOAP、あるいは他の複雑な分散手法を使用していたチームが学んだ「分散型システムをローカル・システムに見せかけようとすると、最終的には必ず悲惨な結果に終わる」という教訓に端を発します。さらに、Fowler 氏の Decentralized Governance パターンと Decentralized Data Management パターンも、プログラムとランタイム環境は自己完結型にしなければならないという、苦労して学んだ教訓に由来しています。

これまでの歴史に基づく現状

Fowler 氏、Adrian Cockcroft 氏をはじめとする人々によって、開発チームがマイクロサービスを採用すべき理由について説得力のある主張が展開されました。けれども、これまでのあらゆる教訓がマイクロサービス・アーキテクチャーという結論をどのように導いたかに目を向けると、私がこれまで話した開発者中心のものとは若干異なる結論が引き出されます。特に、既存のアプリケーションからなる企業世界の中でマイクロサービスを機能させるという実際面に目を向ける必要があります。さらに、マイクロサービス・アーキテクチャーは DevOps の運用側に配置されるという点が特に強調されていることも認識しなければなりません。

企業世界における現状

Netflix、Gilt.com、Amazon などの企業が、いくつもの成功事例を公開したことから、マイクロサービス・アーキテクチャーが注目を浴びるようになりました。その一方で、こうした企業のすべて、そして他にもあるマイクロサービスの成功例の多くには共通する点が 1 つあります。それは、いずれの成功例も、新しいアプリケーションを開発している Web 企業、あるいは相当な量のレガシー・コード・ベースを置き換える必要のない Web 企業から生まれていることです。従来型の企業がマイクロサービスを採用する場合、マイクロサービスをテストするための最初のグリーンフィールド・アプリケーションを選んだ後に突き当たる問題のうちの 1 つは、大規模なモノリシック・アプリケーションをリファクタリングしなければならないときに、「分権化されたデータ管理」と「分権化されたガバナンス」をはじめとするマイクロサービス・アーキテクチャーのいくつかの原則を導入するのは難しいことです。

幸い、こうした問題に対する取り組みは何年にも渡って行われていて、Martin Fowler 氏がマイクロサービスに取り組む数年前の 2004 年に文書化したパターンとして形になって表れています。彼の概念は「Strangler Application パターン」と呼ばれていて、このパターンは、実際にグリーンフィールドで作業することはほぼないという事実に対処することを目的としています。マイクロサービスが特に必要となるプログラムは、Web 上で最も大規模で、最も厄介なプログラムです。けれどもやはり、Web のアーキテクチャーを利用すれば、必要とされるリファクタリングを管理するための戦略を実現できます。

Strangler Application は、木の幹にぐるぐる巻き付いた「つる」の例えに基づく単純な概念です。このパターンの概念としては、まず、Web アプリケーションの構造 (Web アプリケーションは、ビジネス・ドメインの機能的に異なる側面にマッピングされる個別の URI からなるという事実) を利用して、アプリケーションを異なる複数の機能ドメインに分割し、それらのドメインを 1 つずつ新しいマイクロサービス・ベースの実装で置き換えていきます。この間、別個のアプリケーションからの 2 つの側面が同じ URL 空間内で共存することになりますが、段階的に新しくリファクタリングしたアプリケーションで元のアプリケーションを「抑え込む」か置き換えて、最終的にはモノリシックなアプリケーションをシャットダウンします。

企業世界の中でマイクロサービスの手法を機能させる上で役立つパターンは、これだけではありません。さらに別の重要な側面となるのが、多くの場合、開発チームがデータの制御を分権化できることはほとんどないことです。このことが、Erich Gamma 氏ほかによる『Design Patterns』の Adapter パターンを拡張した、Adapter Microservice と呼ばれるパターンの根拠となっています。

Adapter Microservice パターンでは、2 つの異なる API をその中間点で適応させます。一方の API は、RESTful または軽量のメッセージング手法を使用して作成されたビジネス指向の API であり、従来のマイクロサービスと同じドメイン駆動型手法によって設計されています。このビジネス指向の API が適応するもう一方の API は、既存のレガシー API、または従来型の WS-* ベースの SOAP サービスです。純粋主義者はこのアプローチに反論して、分権化されたデータを採用しないのであればマイクロサービスを利用していることにならないと主張するでしょう。けれども、企業データは理由があって存在するものであり、通常は、組織的な惰性で残されているという以外の正当な理由があります。例えば、そのデータに現在のままの形でアクセスする必要がある多数のレガシー・アプリケーションがある場合は、データの形を新しい API に簡単に適応させることはできません。あるいは、単一のサービスで使用しているデータの大きさ (数百テラ・バイト規模、ペタ・バイト規模の大きさ) から、新しい形への移行が不可能である場合もあるでしょう。

DevOps 内での運用の復帰

マイクロサービスに伴う別の重要な側面は、DevOps として知られる一連の手法の運用側に関するものです。DevOps は、従来型のアプリケーション管理を対象に開発された数々のパターンに根差しています。Fowler 氏はマイクロサービスに関する最初の論文で、このことの重要性を強調し、継続的デリバリーと継続的インテグレーションに基づく DevOps プロセスの一環としてインフラストラクチャーの自動化に適応する必要があると述べています。ただし、小さなスケールでマイクロサービスを採用し始めたばかりのチームにとって、その必要性が常に明らかであるとは限りません。問題は、マイクロサービスを採用すれば個々のサービスを簡単に変更してデプロイできるようになる一方、対応するモノリシックなアプリケーションに比べ、一連のサービスを管理および保守する全体的な作業負荷が大きくなることにあります。

これが理由で、例えば Netflix のマイクロサービス・フレームワークや Amalgam8 などの一般的な多くのフレームワークでは Service Registry パターンを適用しています。Service Registry パターンでは、特定のマイクロサービス・エンドポイントをコード内にハードコーティングしないようにすることで、ダウンストリームのマイクロサービス実装の変更を可能にすると同時に、DevOps パイプラインのさまざまなステージで異なるサービス・ロケーションを選択できるようにします。このパターンを採用しなければ、マイクロサービスの呼び出しチェーンによって、コードの変更がアップストリームに伝播されや否や、アプリケーションは機能不全に陥ってしまいます。

分離を確実にするとともに、マイクロサービスをデバッグしやすくするという考え方が、これまでに識別したいくつかの DevOps パターンの基本となっています。その好例は、Correlation ID と Log Aggregator です。Correlation ID パターンは、Gregor Hohpe 氏の著書『Enterprise Integration Patterns』の中で具体的な形で特定されて文書化されていますが、現在は、その概念が OpenTracing などのプロジェクトの中で一般化され、異なる言語で作成されたいくつものマイクロサービスを通じた伝播を追跡可能にするために使用されています。Log Aggregator は、数々のオープンソースおよび商用の製品 (Cloud Foundry やオープンソース ELK スタックなど) に実装されている新しいパターンです。このパターンは、多種多様なマイクロサービスからのログを 1 つの検索可能なリポジトリーに集約できるようにすることで Correlation ID を補完します。この 2 つのパターンを組み合わせると、サービスの数や各呼び出しスタックの深さに関わらず、マイクロサービスを効率的に、わかりやすくデバッグできます。

最後に、運用と開発の間の懸け橋として DevOps に不可欠な別の側面を取り上げます。それは、Fowler 氏が彼の記事の中で主張している、故障に備えた設計の重要性です。特に、Netflix の Hystrix フレームワークはその Circuit Breaker パターンの実装により、多くのマイクロサービス実装内で重要な役割を果たすようになっています。Circuit Breaker は、2007 年に出版された Michael Nygard 氏の著書『Release It!』で最初に文書化されました。Circuit Breaker パターンに従うと、ダウンストリームですでに障害が発生していることがわかっている場合、その障害の処理に無駄に時間を使うことがなくなります。障害の処理は、アップストリームのサービス呼び出しの中にコードの Circuit Breaker セクションを埋め込むことで対処できます。この Circuit Breaker セクションで、ダウンストリームのサービスが正常に機能していない場合はそれを検出し、呼び出しの試行を回避できるようにするという仕組みです。このパターンの利点は、それぞれの呼び出しが「早急に失敗」するため、全体的なユーザー・エクスペリエンスを改善できることです。さらに、ダウンストリームの呼び出しが失敗することがわかっていれば、スレッドや接続プールなどのリソースを不適切に管理することもなくなります。

今までは、このようなリソース管理は DevOps の運用側が独占する分野でしたが、運用側と開発側がアプリケーションの信頼性、パフォーマンス、回復力の向上を目指して協力する際に、マイクロサービスはこの 2 つをより効果的に結び付けます。

次のステップ

この記事と付属の動画では、マイクロサービスの歴史的前例のいくつかを探り、マイクロサービス・アーキテクチャーがどのようにして形になったのかを辿りました。また、企業世界の中でマイクロサービスをうまく適用するためには、どのようなパターンに従わなければならないのか、そしてマイクロサービス・アーキテクチャーを適用する際にはどのような課題に直面する可能性があるのかを説明しました。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Cloud computing
ArticleID=1064108
ArticleTitle=流行語の真相: マイクロサービスのパターンの略歴
publish-date=12272018