BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル マルチランタイム・マイクロサービスアーキテクチャ

マルチランタイム・マイクロサービスアーキテクチャ

原文(投稿日:2020/02/27)へのリンク

優れた分散アプリケーションを開発するのは容易なことではありません。そのようなシステムは一般的に、アプリケーションとマイクロサービスの12項目の原則(12-factor app)に従っているものです。優れた分散システムは、ステートレスで、スケーラビリティがあり、個別にデプロイが可能で、コンテナ化されていて、自動化が可能で、場合によってはイベント駆動でサーバレスであることが求められます。完成後はアップグレードが容易で、長期にわたって安価にメンテナンス可能でなければなりません。これら相反する要件のよいバランスを見つけるのは、今日のテクノロジを持ってしても至難の業なのです。 
この記事では、このようなバランスを可能にする上で、分散プラットフォームがどのような進化を遂げてきたのか、さらに重要な点として、保守管理性に優れた分散アーキテクチャの開発を容易にするために、分散システムは今後どのように進化する必要があるのかを、深く掘り下げたいと思っています。この話題に関する講演を直に見たいのであれば、3月のQCon Londonにぜひ参加してください。

分散アプリケーションに必要なもの

この議論では、現在の分散アプリケーションに必要なものを、ライフサイクル、ネットワーク、状態、バインディングの4つのカテゴリに分けた上で、それぞれが最近の数年間で見せた進歩を簡単に分析してみます。

分散アプリケーションに必要なもの

ライフサイクル

基本的な部分から始めましょう。私たちが何らかの機能を記述する時、プログラミング言語によってそのエコシステム内で利用可能なライブラリ、パッケージングフォーマット、ランタイムなどが決まります。Javaならば、.jarフォーマット、エコシステムとしてすべてのMaven依存関係、ランタイムとしてJVMを使用します。リリースサイクルの短縮化された現在では、ライフサイクルにおいて特に重要なのはデプロイ能力であり、エラーからの復帰能力であり、自動化されたサービスのスケールアップです。これら一連の能力が、私たちのアプリケーションのライフサイクルでは広範に求められているのです。

ネットワーク

今日では、アプリケーションのほぼすべてが何らかの意味で分散アプリケーションであり、ネットワークを必要としています。一方、現代的な分散システムでは、幅広い視点からネットワークをマスタする必要があります。その範囲は、サービスディスカバリやエラーリカバリから始まり、現代的なソフトウェアリリース技術の実現、さらには、あらゆる種類のトレースやテレメトリにまで及んでいます。私たちの場合は、さまざまなメッセージ交換パターン、ポイントツーポイントやパブリッシュ/サブスクライブなどの手法、スマートルーティング機構などもこれに含めなくてはなりません。

状態

状態(state)を話題にする場合、その多くはサービスの状態や、ステートレスが望ましい理由といったことが多いのですが、サービスを管理するプラットフォーム自体にも状態は必要です。信頼性の高いサービスオーケストレーションの実行、分散型のシングルトン、時間的スケジューリング(cronジョブ)、冪等性、ステートフルなエラーリカバリ、キャッシュなどを行なうには、状態が必要になります。ここで挙げたすべての機能が、内部的に状態を持つことに依存しているのです。状態管理の実際はこの記事の範囲ではありませんが、状態に依存する分散プリミティブやその抽象化は関心の範囲内にあります。

バインディング

分散システムのコンポーネントは相互の通信が必要なだけではなく、最新の外部システム、あるいはレガシな外部システムとのインテグレーションも必要です。そのためには、さまざまなプロトコルを変換し、ポーリングやイベント駆動、リクエスト/リプライなどのメッセージ交換パターンをサポートし、メッセージフォーマットを変換し、さらには独自のエラー回復処理やセキュリティメカニズムを実行可能なコネクタが必要になります。

これらは、その場限りのユースケースとして実現するのではなく、優れた分散システム構築に必要な、共通的なプリミティブを集めた便利なコレクションになります。今日では多くのプラットフォームがこの種の機能を提供していますが、この記事では、過去10年間でこのような機能の使い方がどのように変わってきたか、今後10年間でどのように変わっていくのか、といったことを追求したいと思っています。比較対象として、まずは過去10年間について、Javaベースのミドルウェアがこれらのニーズにどのように応えてきたのかを見てみましょう。

従来型ミドルウェアの限界

上述したニーズの旧世代のものを満足する従来型ソリューションとして広く知られているものとしては、Enterprise Service Bus(ESB)、そのバリアントとしてのメッセージ指向ミドルウェア、比較的軽量な統合フレームワークなどがあります。ESBは、サービス指向アーキテクチャ(旧世代のSOAなど)を採用することで、異種環境間の相互運用性を実現するミドルウェアです。

ESBの提供する機能セットは優れたものですが、モノリシックなアーキテクチャであること、ビジネスロジックとプラットフォームが技術的に強く結合しているため、技術的および組織的な集中化が発生すること、などの大きな問題がありました。サービスが開発され、システムにデプロイされた時、分散システムフレームワークと密接に結合しているため、結果としてサービスの進化が疎外されるのです。この問題が露呈するのは、ソフトウェアがリリースされて長く経った後であることも少なくありません。

現在の状況においてESBが有用ではない理由となっている、各カテゴリでの問題や制限を、ここでいくつか挙げてみましょう。

ライフサイクル

従来のミドルウェアは、サポート対象の言語ランタイムが(Javaなど)ただひとつであるのが一般的であったため、ソフトウェアのパッケージ方法や利用可能なライブラリ、パッチの頻度などもそれによって決められていました。ビジネスサービスはこれらのライブラリを使わざるを得ず、結果として、同じ言語で記述されたプラットフォームに強く縛られることになっていたのです。運用面では、サービスとプラットフォームのアップグレードが統合されることになり、サービスとプラットフォームの独立した形での定期的なリリースを阻んでいました。

ネットワーク

従来のミドルウェアは、外部および内部のサービスとのインタラクションを重視した高度な機能を備えていましたが、いくつかの大きな欠点がありました。ネットワーク機能がひとつの主要言語と、その関連テクノロジに集中していたことが、そのひとつです。Java言語であれば、JVM、JDBC、JTAなどがそれに当たります。さらに重要なことは、ネットワークに関する問題や状況が、ビジネスサービスにも深く関与している点です。ネットワーク関連の問題の抽象化を試みた(一時注目されていたHystrixプロジェクトのような)ライブラリもありますが、ライブラリの抽象化はプログラミングモデルやデータ交換パターン、エラー処理のセマンティクス、さらにはライブラリそれ自体という形で、サービスの内部に"リーク"しています。ビジネスロジック全体が一か所でネットワーク機能と接続されているという意味での、コードの書きやすさ、読みやすさというものはありますが、ビジネスとネットワークという2つのテーマがひとつの実装として結合されていることから、最終的にはその発展のパスも共有することになるのです。

状態

サービスのオーケストレーション、ビジネスプロセスの管理、Saga Patternやその他の低速実行(slow-running)プロセスを高い信頼性で実行するためには、プラットフォーム内部に永続的な状態を持つ必要があります。タイマの発火や定時ジョブなどの一時的な動作も同じように状態の上に構築されているので、分散環境にクラスタ化されたレジリエントなデータベースを必要とします。ここで大きな制約となるのが、状態とインタラクションするライブラリやインターフェースが完全には抽象化されておらず、サービスランタイムから分離されていないという事実です。このようなライブラリでは、データベースの詳細に対する設定が必要であるのが一般的です。すなわち、セマンティクスや依存性の問題をアプリケーションドメインにリークさせた形でサービスと同居している、ということになります。

バインディング

統合ミドルウェアを使用するおもな動機のひとつは、さまざまなプロトコルやデータフォーマット、メッセージ交換パターンを使用して、他システムとの接続が可能になるという点にあります。一方で、これらのコネクタがアプリケーションと共存するという事実は、依存関係とビジネスロジックを同時に更新したり、パッチを当てたりしなければならない、ということを意味します。データタイプやデータフォーマットをサービス内で変換したり、元の形式に戻したりすることも必要になります。メッセージ交換パターンに従ったコードの構造化やフロー設計も必要になるでしょう。これらは従来のミドルウェアにおいて、抽象化されたエンドポイントであってもサービス実装に影響を与えるという、ごく一部の例に過ぎません。

クラウドネイティブの傾向

従来のミドルウェアはパワフルです。必要な技術的機能はすべて持っていますが、現在のディジタルビジネスが必要とするような、迅速な変更およびスケールアップの能力に欠けています。マイクロサービスアーキテクチャと、それによって導かれる現代的な分散アプリケーションを設計する上での原則が対処しようとしているのは、まさにこの問題なのです。

マイクロサービスとその技術的要件を支えるアイデアは、コンテナやKubernetesの利用の普及と拡大に貢献しました。それによって始められた新たなイノベーションの方法は、分散アプリケーションに対する今後のアプローチ方法に影響を与えつつあります。Kubernetesとその関連技術が、それぞれの要件にどのような影響を与えているのかを見ていきましょう。

ライフサイクル

コンテナとKubernetesは、言語に依存しないフォーマットによるアプリケーションのパッケージ化、分散、デプロイを可能にしました。開発面における"Kubernetes Pattern"や"Kubernetes Effect"を取り上げた書き物はたくさんあるので、ここでは簡単な説明に留めておきたいと思います。しかしながら、Kubernetesにおける最小管理単位はコンテナであり、コンテナレベルおよびプロセスモデルにおいて重視されているのはプリミティブの分散デリバリである、という点には注意が必要です。つまり、Kubernetesはアプリケーションのライフサイクル面での管理やヘルスチェック、リカバリ、デプロイメント、スケーリングといった優れたジョブを行いますが、その他のフレキシブルなネットワークや状態管理、バインディングといった、分散アプリケーションのコンテナ内部に関する面の改善においては十分ではない、ということなのです。

Kubernetesにはステートフルなワークロードやサービスディスカバリ、定時実行(cron)ジョブなどの機能があるではないか、という指摘があるかも知れません。それは事実ですが、これらのプリミティブはすべてコンテナレベルなのです。コンテナ内部に関しては、この記事の最初に示したような詳細な機能に対して、言語特有のライブラリを使用してアクセスする必要があることに変わりありません。Envoy、Linkerd、Consul、Knative、Dapr、Camel-Kなどのプロジェクトが取り組んでいるのは、このような部分なのです。

ネットワーク

Kubernetesが提供するサービスディスカバリ関連の基本的なネットワーク機能は、基盤としては優れたものですが、現代的なアプリケーションには不十分であることが分かりました。マイクロサービスの数が増え、デプロイメントのペースが早まったことにより、より高度なリリース戦略、セキュリティ管理、メトリクス、エラーからのリカバリ、エラーのシミュレーションといったものを、サービスに触れることなく提供するというニーズが強く訴えられるようになりました。そこから自然発生的に生まれた新たなカテゴリが、サービスメッシュと呼ばれるものです。

この中で特に注目されるのは、ネットワークに関する処理主体が、それまでのビジネスロジックを含んだサービスから、サイドカーやノードレベルエージェントという形式で、外部の独立したランタイムに移行したことです。今日では高度なルーティングやテスト支援、特定面におけるセキュリティ、さらにはアプリケーション特有のプロトコル(例えばEnvoyはKafka、MongoDB、Radis、MySQLをサポートしています)までが、サービスメッシュによって実行可能です。ソリューションとしてのサービスメッシュは、まだ広範に採用されるには至っていませんが、分散システムの真の問題点に対処するものとして、その形とあり方を確立するであろうことは間違いありません。

通常のサービスメッシュに加えて、Skupperのような、ネットワーク機能の外部ランタイムエージェントへの移行を補完するようなプロジェクトも存在します。Skupperは、レイヤ7仮想ネットワークによってマルチクラスタ通信の問題を解決し、高度なルーティング機能や接続機能を提供するものですが、ビジネスサービスランタイムに組み込むのではなく、Kubernetes名前空間毎にひとつのインスタンスが共有サイドカーとして動作する形式を採用しています。

要約すると、コンテナとKubernetesは、アプリケーションのライフサイクル管理に向けた大きな一歩を踏み出すものです。そしてサービスメッシュとその関連技術は、責務をアプリケーション外部のプロキシへ移行するための基盤を提供することにより、コンテナとKubernetesに存在する難題を解決しています。次の項目に移りましょう。

状態

少し前に、状態に関連するおもな統合プリミティブの一覧を挙げました。状態の管理は難しいので、特別なストレージソフトウェアや管理サービスに移譲した方がよいでしょう。ここでは話題としませんが、統合的なユースケースのための言語ニュートラルな抽象化で状態を使う場合などは、それに当たります。今日では、言語ニュートラルな抽象化の裏でステートフルなプリミティブを提供するために、多大な努力が払われているのです。ステートフルなワークフロー管理は、例えばAWS Step Funcrtions、Azure Durable Functionなど、クラウドベースのサービスでは必須の機能です。コンテナベースのデプロイメントでは、CloudStateDaprが、いずれも分散アプリケーションにおいてステート管理の抽象化と分離を実現する手段としてサイドカーモデルを採用しています。

将来的には上に挙げたようなステートフルな機能はすべて、独立したランタイム内への抽象化と分離が図られていくのではないか、と思います。つまり、ワークフロー管理、シングルトン、冪等性、トランザクション管理、定時ジョブのトリガ、ステートフルなエラーの処理といったものがすべて、サービス内ではなく、サイドカー内で高い信頼性を持って処理されるようになるのです。ビジネスロジックとしては、このような依存関係やセマンティクスをアプリケーションに含む必要がなくなり、バインディング環境から宣言的に処理を要求することができるようになります。例えばサイドカーから定時ジョブを起動したり、冪等性を持ったコンシューマやワークフローマネージャとして動作したり、独自のビジネスロジックをワークフローの特定ステージやエラー処理、一時的な動作、ユニークな冪等要求などにおいて、コールバックやプラグインとして起動することが可能です。

ステートフルのもうひとつの利用例がキャッシュ処理です。キャッシュ機能をアプリケーションのランタイム外に追い出したは、サービスメッシュレイヤが実行する要求キャッシュにも、あるいはInfinispanやRedis、Hazelcastなどのデータキャッシュにもあります。

バインディング

ここまでは、分散システム上のニーズのすべてをアプリケーションランタイムから分離する、という話題を取り上げてきましたが、同じ傾向はバインディングでも続いています。コネクタ、プロトコル変換、メッセージ変換、エラー処理、セキュリティ対策などは、すべてサービスランタイムの外に移すことが可能です。まだそこまでは到達していませんが、KnativeやDaprなど、この方向を試しているプロジェクトはあります。これらの責務をすべてアプリケーションランタイム外に出すことにより、非常にコンパクトな、ビジネスロジックに集中したコードが生まれます。このようなコードは分散システムのニーズから独立したランタイム内で動作するので、パッケージ済機能としての使用が可能になります。

もうひとつの興味深いアプローチは、Apache Camel-Kプロジェクトで採用されているものです。メインアプリケーションに並走するエージェントランタイムを使用する代わりに、このプロジェクトでは、インテリジェントなKubernetes Operatorを使用することで、KubernetesやKnativeの提供するプラットフォーム機能を備えたアプリケーションを構築します。ここでは、アプリケーションに必要な分散システムプリミティブをインクルードするためのオペレータが唯一のエージェントです。違いは、分散プリミティブの一部がアプリケーションランタイムに追加されて、一部はプラットフォーム内で動作する(サイドカーに含めることも可能)という点です。

将来的なアーキテクチャのトレンド

視野を広げれば、機能をプラットフォームレベルに移行することによって、分散アプリケーションのコモディティ化が新たなフロンティアに達している、と結論付けることができます。ライフサイクルに加えて、現在ではネットワークや状態の抽象化、宣言的イベント処理、エンドポイントバインディングなどが当初から使用可能になり、次にはEIPがこのリストに加わろうとしています。特に興味深いのは、コモディティ化がランタイムライブラリや純粋なプラットフォーム機能(Kubernetesの新機能など)の形ではなく、アウトプロセスモデル(サイドカー)による機能拡張を使用していることです。

現在は、従来のミドルウェアが備えていたすべての機能いわゆるESB)を別のランタイムに移行することによる、全方向的なアプローチが進められている状況です。間もなくすれば、サービスが行わなければならないことは、ビジネスロジックの記述のみになるでしょう。


従来型のミドルウェアプラットフォームとクラウドネイティブなプラットフォームの概要

従来のESB時代と比較すると、このアーキテクチャではプラットフォームからビジネスロジックが分離されていますが、まだ完全ではありません。旧式のエンタープライズ統合パターン(EIP) — スプリッタ、アグリゲータ、フィルタ、コンテントベースルータ、あるいはストリーミング処理パターン — マップ、フィルタ、フォールド、ジョイン、マージ、スライディングウィンドウなど、分散プリミティブの多くは、相変わらずビジネスロジックランタイムに含めなくてはなりません。その他にも、プラットフォームアドオンに対して独自の、あるいはオーバーラップした依存関係を持っているものが多数あります。

別々のドメインに導入されるさまざまなクラウドネイティブプロジェクトを積み上げれば、最終的には次の図のようになるでしょう。


マルチランタイム・マイクロサービス

この図はあくまで説明のためのものですので、代表的なプロジェクトを意図的にピックアップして、それを分散プリミティブのカテゴリにマップしています。実際には、機能的にオーバーラップする部分があったり、ワークロードモデルに互換性がなかったりするので、これらすべてのプロジェクトを同時に使用するということはないでしょう。では、この図をどう解釈すればよいのでしょうか?

  • Kubernetesとコンテナは多言語アプリケーションのライフサイクル内で大きく飛躍し、将来的なイノベーションの基礎を築いている。
  • 高度なネットワーク機能によってKubernetes上で進化したサービスメッシュ技術は、アプリケーションの責務へと踏み込み始めている。
  • Knativeはその迅速なスケールアップを通じて、サーバレスワークロードをそのおもな対象としているが、サービスオーケストレーションや、イベント駆動バインディングのニーズにも対応している。
  • DaprはKubernetes、Knative、サービスメッシュの思想に基づいて構築されており、ステートフルなワークロード、バインディング、インテグレーションといったニーズに対処するためにアプリケーションランタイムに進出し、現代的な分散ミドルウェアの役割を担っている。

この図は、おそらく将来的に現れるであろう、複数のランタイムを使用した分散システム実装を視覚的に表現したものなのです。複数のランタイムとは、マイクロサービスが複数存在するからではなく、マイクロサービスそれぞれが複数の — おそらくはカスタムビジネスロジックランタイムと分散プリミティブランタイムの2つで構成される、という理由によるものです。

マルチランタイム・マイクロサービスの導入

ここでは、形になりつつあるマルチランタイム・マイクロサービスアーキテクチャについて、手短に説明したいと思います。

映画"Avator"で、原生林でPandoraを探すために科学者たちが開発した、AMP(Amplified Mobility Platform)"メカスーツ"をご存じでしょうか。このマルチランタイムアーキテクチャは、ヒューマノイドであるドライバにスーパーパワーを提供するこのメカスーツに似ています。映画では、人がこのスーツを着ることで力を得て、破壊兵器にアクセスします。一方のソフトウェアアーキテクトでは、ビジネスロジック("マイクロロジック(micrologic)"と記されている部分)がアプリケーションのコアを形成して、サイドカーのメカコンポーネントがパワフルな組み込み分散プリミティブを提供するのです。"メカ"の機能を組み込んだマイクロロジックが、分散システムに必要なプロセス外部の機能を使用して、マルチランタイム・マイクロサービスを形成します。そして、何より素晴らしいのは、近々Avator 2が公開されて、このアーキテクチャのプロモーションを支援してくれることです。そうなれば、すべてのソフトウェアカンファレンスで、古臭いオートバイのサイドカーではなく、かっこいいメカの写真を使って説明できるようになります ;-)次に、このソフトウェアアーキテクチャを詳しく見ていきましょう。

これはクライアントサーバアーキテクチャと同じような2コンポーネントモデルで、それぞれのコンポーネントが独立したランタイムです。純粋なクライアントサーバアーキテクチャと違うのは、2つのコンポーネントが同じホスト上に配置されているため、ネットワークの信頼性に関する懸念が存在しないことです。2つのコンポーネントの重要性は同じです。どちらの方向へもアクションを起こすことが可能で、いずれもクライアントあるいはサーバとして動作することができます。コンポーネントのひとつはMicrologicと呼ばれるもので、分散システムに関する機能をほぼ取り除いた、最小限のビジネスロジックを保持します。もうひとつのコンポーネントはMechaで、この記事でこれまで論じてきた分散システムの全機能(プラットフォームが提供するライフサイクルは除く)を提供します。


マルチランタイム(プロセス外部)マイクロサービスアーキテクチャ

MicrologicとMechaは1対1でデプロイされる場合(いわゆるサイドカーモデル)と、いくつかのMicrologicランタイムがひとつのMechaを共有する場合があります。前者のモデルはKubernetesなどの環境に、後者はエッジデプロイメントに適しています。

Micrologicランタイムの特徴

Micrologicランタイムの特徴について、簡単に見ていきましょう。

  • Micrologicコンポーネントは、それ自体はマイクロサービスではありません。マイクロサービスが持つビジネスロジックを含んではいますが、それはMechaコンポーネントと組み合わせることで始めて動作するものです。それに対して、マイクロサービスは自己完結的であって、全体的な機能の一部であったり、あるいは他のランタイムにまで広がる処理フローの一部であったりすることはありません。Micrologicは対応するMechaと組み合わせることで、このようなマイクロサービスを形成するのです。
  • これは関数でも、サーバレスアーキテクチャでもありません。サーバレスは、管理されたそのスケールアップの速さと、0スケール(scale-to-zero)によって広く認知されています。サーバレスアーキテクチャでは、ひとつの関数が単一のオペレーションを実装し、それがスケーラビリティの単位になります。その点において関数は、複数のオペレーションを実装するが、その実装はエンドツーエンドではないという意味において、Micromagicとは異なるものです。最も重要なことは、オペレーションの実装がMechaとMicrologicランタイムの両方に広がっていることです。
  • これは既知の分散プリミティブをコーディングなしでコンシュームすることに最適化された、クライアントサーバアーキテクチャの特殊な形式であると言えます。同じように、Mechaがサーバの役割であるとするならば、個々のクライアントとともに動作するように、それぞれのインスタンスを特別に設定する必要が生じます。一般的なクライアントサーバアーキテクチャのように、複数のクライアントを同時にサポートする汎用サーバインスタンスではないのです。
  • Micrologic内のユーザコードが他システムと直接的に通信したり、他の分散システムプリミティブを実装したりすることはありません。MicrologicはMechaと、HTTP/gRPCやCloudEvents仕様などのデファクト標準を使ってインタラクションを行い、Mechaは豊富な機能を使用して、設定ステップやメカニズムに従う形式で、他のシステムとの通信を行います。
  • Micrologicは、分散システムに関連するものを削ぎ落したビジネスロジックの実装のみを責務としていますが、それでも最小限のAPIは実装しなければなりません。事前に定義したAPIとプロトコルによって(例えば、Kubernetesデプロイメント用のクラウドネイティブな設計原則に従って)、Mechaやプラットフォームとの対話を可能にしなければならないのです。

Mechaランタイムの特徴

Mechaランタイムの特徴をいくつか挙げます。

  • オフ・ザ・シェルフ機能として分散プリミティブを提供する、汎用的で設定範囲が広い、再利用可能なコンポーネントです。
  • それぞれのインスタンスは、ひとつのMicrologicコンポーネント(サイドカーモデルの場合)用に設定するか、あるいはいくつかのコンポーネントで共有するように設定されなければなりません。
  • Micrologicランタイムに対して、一切の仮定をしてはなりません。HTTP/gRPC、JSON、Protobuf、CloudEventsといったオープンなプロトコルとフォーマットを使用することで、多言語マイクロサービスのみならず、モノリシックなシステムとも動作が可能です。
  • YAMLやJSONなどシンプルなテキスト形式で、有効化する機能やMicrologicエンドポイントとのバインド方法を記述することによって宣言的に設定します。特殊なAPIインタラクションのために、OpenAPIAsyncAPI、ANSI-SQLなどの仕様を追加的に提供することも可能です。ステートフルなワークフローには、複数の処理ステップを組み合わせた、Amazon State Languageなどの仕様を用いる方法があります。また、ステートレスなインテグレーションに関しては、Enterprise Integration Patterns(EIPs)をCamel-K YAML DSLのようなアプローチと併用することが可能です。ここで重要なのは、これらがいずれもシンプルなテキストベースの、宣言的な多言語定義であることです。これにより、コーディングの必要なく、Mechaを完成させることが可能になっています。ただし、これらは将来的な予測であり、現時点ではステートフルなオーケストレーションやEIP用のMechaは存在しませんが、間もなく既存のMecha(Envoy、Depr、Cloudstateなど)に、このような機能が追加され始めるだろうと期待しています。Mechaは、アプリケーションレベルの分散プリミティブ抽象化レイヤです。
  • ネットワークプロキシ、キャッシュプロキシ、バインディングプロキシというように、目的別に多数のエージェントに依存する代わりに、ひとつのMechaがこれらすべての機能を提供するようになります。ストレージやメッセージの永続化、キャッシュなど一部の機能の実装は、他のクラウドやオンプレミスサービスをバックとしたプラグインの形式になるでしょう。
  • 一方で、分散システムのライフサイクル管理に関わるような責務は、Open App Modelなどの汎用的なオープン仕様を使ってMechaランタイムで実現するよりも、Kubernetesやその他のクラウドサービスなどの管理プラットフォームが提供した方が合理的だと思われます。

アーキテクチャのおもなメリット

増え続ける分散システムの責務からビジネスロジックを分離できることがメリットです。ソフトウェアシステムにおけるこの2つの要素は、ダイナミクスがまったく違います。ビジネスロジックは常にユニークであり、インハウスで記述される独自コードです。企業内の優先度や遂行能力によって頻繁に変わります。一方の分散プリミティブは、この記事で挙げたような、既知の問題に対応するためのもので、ソフトウェアベンダが開発し、ライブラリ、コンテナ、サービスとして利用されます。コードの変更はベンダの優先度やリリースサイクル、セキュリティパッチ、オープンソースの管理ルールなどに従って実行されます。2つのグループに、相互の可視性やコントロール性はほとんどありません。

さまざまなアーキテクチャにおけるビジネスロジックと分散システム関連機能との結合性

マイクロサービスの原則は、各サービスの独立的な発展を可能にするコンテキスト境界によってビジネスドメインの分離を促進します。しかし、マイクロサービスアーキテクチャは、ビジネスロジックとミドルウェアとの結合を原因とする問題には対処していません。インテグレーションのユースケースがそれほど重要ではないタイプのマイクロサービスであれば、これは大きなファクタではないかも知れませんが、対象とするドメインが複雑なインテグレーションに関わるものである場合(すべてのケースがそうなりつつあります)には、マイクロサービスの原則に従うことは、ミドルウェアとの結合回避の役には立ちません。ミドルウェアがライブラリとしてマイクロサービスに含まれていたとしても、ライブラリの移行や変更を行った途端に、その結合があからさまになります。プリミティブを分散する必要性が増すほど、インテグレーションプラットフォームとの結合性も高くなるのです。ミドルウェアをライブラリとしてではなく、事前定義されたAPI経由で参照する別のランタイム/プロセスにすることで、疎結合が促進され、各コンポーネントの独立的な更新が可能になります。

この方法は、ベンダが複雑なミドルウェアソフトウェアを分散し、メンテナンスする手段としても優れています。ミドルウェアとのインタラクションがオープンAPIと標準によるプロセス間通信上で行われていれば、ソフトウェアベンダは、パッチのリリースやアップデートを自分たちのペースで行うことができます。コンシューマ側もまた、自分たちの望む言語、ライブラリ、ランタイム、デプロイメント方法、プロセスを使用することができるのです。

このアーキテクチャのおもなデメリット

プロセス間通信。ビジネスロジックと分散システムのミドルウェアメカ(名称の理由はすでに述べたとおりです)が別々のランタイムにある以上、プロセス内のメソッドコールではなく、HTTPあるいはgRPCコールが必要になります。ただしこれは、マシン間やデータセンタ間の場合のようなネットワークコールではありません。MicrologicとMechaは同一ホスト上にあることが前提なので、レイテンシは低く、ネットワークの問題が発生する可能性も最小限です。

複雑性。次の疑問は、このようなシステムから得られるメリットは、開発やメンテナンスの複雑さに見合ったものなのか、ということです。その答はイエスに向かって傾いていくだろう、と私は考えています。分散システムの要求は高まり、リリースサイクルのペースは早まっています。そのためには、このアーキテクチャが最適なのです。少し前に私は、今後の開発者にはハイブリッドな開発スキルが必要になるだろう、という記事を書きました。このアーキテクチャは、このトレンドをより確実なものとして進めていきます。アプリケーション部分はより高レベルなプログラム言語で記述され、機能部分は宣言的なコンフィギュレーションの可能なコンポーネントとしてオフ・ザ・シェルフで提供されるようになるでしょう。2つの部分はコンパイル時や、あるいは起動時のプロセス内の依存性注入を通じてではなく、デプロイメント時のプロセス間通信を通じて相互接続されます。このモデルにより、ソフトウェアの再利用率の向上と、更新ペースの高速化が同時に実現します。

マイクロサービスの次に来るものは関数ではない

マイクロサービスアーキテクチャには明確な目標があります。変更への最適化です。アプリケーションをビジネスドメインに分割したこのアーキテクチャでは、分離され、独立したチームによって管理され、独立したペースでリリースされるサービスを通じて、ソフトウェアの進展とメンテナンスに最適なサービスバウンダリを提供します。

一方で、サーバレスアーキテクチャのプログラミングモデルを見た場合、その基盤となっているのは関数です。関数はスケールアップに適しています。関数では、すべてのオペレーションが独立したコンポーネントに分割されるため、迅速に、独立的に、必要に応じてスケールアップすることが可能になります。このモデルでは、デプロイメントの粒度が関数なのです。関数は入力を持つコード構造として選択されますが、この入力のレートがスケーリング動作に直接関わってきます。つまり、複雑なシステムの長期的なメンテナンス性ではなく、スケーラビリティに対して究極的に最適化したものがサーバレスアーキテクチャなのです。

サーバレスのその他の特徴は、代表的なサービスであるAWS Lambdaの持つフルマネージドな運用特性によるものではないでしょうか。この面で"AWS Serverless"は、コントロールとロックインの欠如と引き換えに、プロビジョニングの速度に最適化されていると言えます。しかし、フルマネージドであるというのは、アプリケーションアーキテクチャではなく、ソフトウェアの利用モデルに関するものです。機能的には直交していて、モノリシック、マイクロサービス、Mecha、関数など、あらゆるアルゴリズムを理想的な世界で利用できるSaaSベースのプラットフォームを利用するのと似ています。AWS Lambdaは、多くの点でフルマネージドのMechaアーキテクチャに似ていますが、大きな違いがひとつあります。Mechaでは関数モデルの使用は必須ではありません。ミドルウェアに関する部分から分離され、ビジネスドメインを中心とすることで、よりまとまりのあるコード構造を可能にしているのです。

アーキテクチャの最適化

Mechaアーキテクチャはまた、ミドルウェア非依存にマイクロサービスを最適化します。マイクロサービスは相互には独立していますが、組み込まれる分散プリミティブには強い依存性を持っています。Mechaアーキテクチャはこの2つの要件を別々のランタイムに分離することで、別々のチームによる独自のリリースを可能にします。このような分離は"Day-2 Operarions"(パッチやアップグレード)を向上するとともに、長期的にはビジネスロジックの集合ユニットのメンテナンス性も改善します。この意味から、Mechaアーキテクチャは、最もフリクションを起こすバウンダリに基づいてソフトウェアを分割したマイクロサービスアーキテクチャが、自然な形で進化したものであると言えます。このような最適化は、ソフトウェアの再利用や進化という形で、コードの過度な分散と引き換えに究極的なスケーラビリティに最適化された関数モデルよりも多くメリットをもたらします。

結論

分散アプリケーションには多くの要件があります。効率的な分散システムの構築には、複数のテクノロジと優れた統合アプローチが必要です。従来のモノリシックなミドルウェアであっても、分散システムに必要な技術的要件をすべて提供することは可能ですが、ビジネスが必要とする変更、適用、迅速なスケールアップといった能力が欠如しています。コンテナやKubernetesの急速な普及にマイクロサービスベースのアーキテクチャが貢献した背景には、このような理由があります。クラウドネイティブな世界における最新の開発では、従来のミドルウェア機能のすべてをプラットフォームとオフ・ザ・シェルフの外部ランタイムに移行することで、以前の姿に戻ろうとしています。

アプリケーション機能のこのようなコモディティ化には、ランタイムライブラリや純粋なプラットフォーム機能ではなく、外部プロセスモデルによる機能拡張がおもに使用されています。このことから、将来的な分散システムの実装には、複数のランタイムが使用される可能性が強いものと思われます。複数のランタイムが使用されるのは、マイクロサービスが複数あるからではありません。個々のマイクロサービスが、独自のマイクロビジネスロジック用のランタイムと、オフ・ザ・シェルフでコンフィギュレーション可能な分散プリミティブ用のランタイムという、複数のランタイムで構成されるようになるからです。

著者について

Bilgin Ibryam氏はRedHatのプリンシパルアーキテクトで、Apache Software Foundationのコミッタ兼メンバです。オープンソースのエバンジェリストでブロガであり、時には講演も行う氏は、書籍"Kubernetes Patterns""および"Camel Design Patterns"の著者でもあります。日頃はオープンソースソリューション開発の成功を目指したメンタリングやコーディング、開発者の指導などをしています。現在はブロックチェーン、分散システム、マイクロサービス、DevOps、クラウドネイティブなアプリケーション開発に注力しています。

この記事に星をつける

おすすめ度
スタイル

BT