What is 分散モノリス(Distributed Monolith)

分散モノリスとは

分散モノリスとは、マイクロサービスアーキテクチャが採用され、1つのアプリケーションを構成するのに複数サービスが存在しているが、実態はモノリスのように各サービスが密結合している状態で構築されたアプリケーションのことを指します。

そもそもモノリシックアーキテクチャでは、アプリケーション全体が1つのパッケージにまとめられ、ソースコード、ライブラリ、設定等のアプリケーションに必要の依存関係が全て含まれています。モノリスは決してそうあるだけでレガシーで悪いものかと言えばそうではありませんが、上記のように全てをまとめているためどこかのタイミングで限界がきます。
よく聞く例としては、コードベースが肥大化したことにより自分が担当するもしくはよく触る部分以外のコードがブラックボックスになってしまう、数十人、複数チームが一つのリポジトリに対して変更を加えるためリリーススケジュールの管理が面倒になる、ユースケース毎の最適な技術選定が出来ない事による問題が発生するなどがあるかなと思います。
つまりモノリスの課題の一つとして、一つにまとめている分当たり前の話ですが、関係者が増えやすいので所有権や意思決定が曖昧になるそれによる混乱が生じることがあげられます。

それに比べてマイクロサービスは、モノリスで生じる課題を解決する事が可能です。
モノリスからマイクロサービスへではマイクロサービスの定義として以下のように記述されています。

  • 独立してデプロイ可能である
  • ネットワークエンドポイントを介して連携
  • データベースはサービス境界の内側に隠蔽される
  • 特定の技術に依存しない

これらの定義通りのマイクロサービスが構築されていると、所有権の問題も解決され、デプロイや技術選定の自由をモノリス時代より得る事が出来ます。
しかしこれらの利点は、同時にデプロイの複雑さ、管理上のオーバーヘッド、監視やトレーシング、データやトランザクション管理などの点でモノリス以上にコストを支払う必要があり、これらの点がマイクロサービスアーキテクチャの必要有無、採用のタイミングに関する議論が盛り上がる事に繋がっていると思っています。

話を戻すと、分散モノリスでは、モノリスの重厚さと柔軟性のなさ、マイクロサービスの複雑さのどちらも抱えることとなり、どちらのアーキテクチャの利点も得ることが出来ないのが最大の問題と言えます。
マイクロサービスとして独立していながらも、本来のマイクロサービスの定義である独立してデプロイ可能なサービスという点に反しており、サービス間の独立性も存在していないため、修正を反映するために複数のサービスを同時にデプロイする必要が出てきてしまうなど運用が複雑になります。

他にもサービスが動作する上での共有ライブラリなどに完全に依存するアプリケーションを分散モノリスと呼んでいる例もあります。
この場合、共有ライブラリに依存しているため、異なる技術を組み合わせてサービスを構成できるという利点が失われているという状態に陥ります。 そもそもの話として、マイクロサービスアーキテクチャで述べられている通り、サービス間の過度な密結合が引き起こす問題は、コードの重複が起こす問題よりも悪質で、コードの共有はサービスの境界内では完全に正しいが、しかし境界の外に漏れたらそれは潜在的な密結合だと言えます。 DRYはソフトウェア開発者にはよく知られているものではありますが、共有されているコードの中にビジネスロジックがある場合、分離した状態でデプロイできるというメリットは少なくなる。そのコードを使っているサービスすべてが影響を受けるからだ。そのためマイクロサービスアーキテクチャではコードの再利用よりも独立性の担保を優先すべきであり、そこを誤る事で分散モノリスが出来上がるアンチパターンの一つなのかと思います。

分散モノリスの特徴

Newman氏が声を大にして警告するのが、"最悪のモノリスである、分散モノリス(distributed monolith)"だ。分散モノリスは多数のサービスを持つが、すべてを同時にデプロイしなければならないため、何かを行うにはチーム間の調整が数多く必要となる。システムが分散モノリスであることを示す指標のひとつが、社内にフルタイムの"リリース調整マネージャ"がいるかどうか、ということだ。その例としては、BBCによる分散モノリスのアーキテクチャ再構築を、Blanca Garcia Gil氏のQConでの講演で確認するとよいだろう。

デプロイの難しいシステムの原因が、常にサービス境界定義の杜撰さにあるとは限らない。それはソフトウェア開発プロセスの結果かも知れないのだ。Newman氏はリリーストレインをその一例として挙げている。リリーストレインでは、"4週に1回というように、定期的に、用意のできたソフトウェアをすべて出荷します。準備できていないソフトウェアがあれば、次のリリーストレインに回されます"。これは継続的デリバリへの足がかりとなることを意識した方法だが、ソフトウェアリリースの最終メソッドになる場合が多く、分散モノリスへとつながりやすい。残念なことに、リリーストレインは現在、SAFe(Scaled Agile Framework)に成文化されているため、企業のプラクティスに組み込まれるようになっている。

サービスの密結合

結合とは、2つの機能間の分離可能性の度合いを意味します。モノリスでは、コードベースを共有し、同じプロセス空間で動作しているため、異なる機能は緊密に結合しています。密結合には次のような形態があります。

タスクを完了するために依存関係を必要とする(動作的結合)。 他のサービスとの高速かつ低レイテンシーの通信を必要とする(時間的カップリング)。 1つのサービスを変更した結果、複数のサービスを変更しなければならない(実装結合)。 Requiring a dependency to be available to complete a task (behavioral coupling). Requiring fast, low-latency communication with other services (temporal coupling). Having to change multiple services as a consequence of changing a single service (implementation coupling).

従来のモノリシックアプリケーションでは、バックエンドロジック(商品の詳細情報の取得)とフロントエンドロジック(Webページのレンダリング)を論理的に分離するために、モデル-ビュー-コントローラ(MVC)フレームワークを作成することがあります。これらは2つの異なる開発チームによって開発された2つの異なる機能ですが、同じモノリシックなコードベースの一部です。一方をデプロイしたり変更したりするには、もう一方にも同じことをしなければなりません。

それでは、このアプリケーションをマイクロサービスとして再構築してみましょう。1つはフロントエンド用、もう1つは製品カタログ用の2つのサービスを作成します。直接的な関数コールをAPIコールに置き換え、ネットワーク経由でサービスを接続します。見た目も動作も順調なのですが、商品カタログのサービスがクラッシュしてしまいました。フロントエンドはどうなりますか?それとも、サービスが緊密に結合しているので、フロントエンドも故障してしまうのでしょうか?バックエンドチームが製品カタログに修正を加えた場合、フロントエンドにも修正を加える必要があるのでしょうか?最も重要なことは、このような事態が発生した場合、お客様はどのような体験をするのでしょうか?

テストをするにはカオスエンジニアリングを行って、プロダクトカタログシステムが落ちた時のフロントエンドの挙動を確認する事で実施できる

スケーラビリティがない

スケーラビリティは、マイクロサービスの最も重要な品質の一つです。迅速かつ効率的にスケーリングできることで、需要の急激な変化に対応し、システムの過負荷を防ぎ、運用コストを低く抑えることができます。ある程度までは垂直方向のスケーリングが有効ですが、最新のクラウドネイティブアプリケーションは水平方向にもスケーリングできなければなりません。クラウドプラットフォームやオーケストレーションツールによってスケーリングは簡素化されていますが、それでもスケーラビリティを考慮してアプリケーションを設計する必要があります。分散型モノリスは、技術的にはスケーリング可能ですが、非常に非効率な方法でしかありません。

例えば、ウェブサイトのトラフィックが急増し、接続数の増加に対応するためにフロントエンドを水平方向に拡張する必要があるとします。モノリスの場合、アプリケーション全体とその依存関係のすべてのインスタンスを新たにデプロイする必要があります。このプロセスは複雑で失敗しやすいだけでなく、真のマイクロサービスに比べて時間がかかり、リソースの使用効率も低くなります。

分散型モノリスのテストでは、アプリケーションのスケーリングにかかる時間、スケーリング時の潜在的な障害ポイント、他のサービス(特に依存関係)への影響を知りたいと考えています。

新しいポッドがデプロイされるまでの時間を記録します。トラフィックが新しいポッドにロードバランスされたら、データベースの負荷量を監視します。データベースもスケールアップしなければならないほどの大きな増加がありますか?そうであれば、分散モノリスになっている可能性があります。

これは、アプリケーションがモノリスであると考えるかどうかに関わらず、トラフィックの急増に備えたり、予期せぬ負荷がかかったときに高いスループットを維持したりするのに役立つ実験です。

分散システム間でデータを共有するには、ネットワークコールのレイテンシーが加わるため、困難が伴います。モノリスでは、データはほぼ瞬時に機能間を流れることができます。しかし、マイクロサービスでは、サービス間の広範な通信により、アプリケーションのスループットが大幅に低下し、タイムアウトなどの予期せぬ障害が発生する可能性があります。 例えば、WordPressでは、MySQL(またはMariaDB)データベースを使用して、ユーザーアカウント、ページコンテンツ、構成設定などのデータを保存しています。1 つのページロードで複数の MySQL クエリが生成されます。そのため、WordPressは低レイテンシー(理想的にはローカル)のデータベース接続で最適に動作しますが、この場合、2つのサービス間に時間的な依存関係が生じます。

Kubernetesでは、WordPressとMySQLを同じPodにまとめてデプロイするという方法があります。しかし、この場合、複数の機能を持つPodが作成され、かなりのシステムリソースを必要とし、起動時間が長くなり、データの複製と同期が必要となり、アプリケーションチームとデータベースチームの両方が所有することになります。つまり、マイクロサービスとしてパッケージ化されたモノリスなのです。

2つのサービスをネットワーク化することは、ネットワークが飽和したり、劣化したりするまでは、問題とは思えないかもしれません。カオスエンジニアリングでは,これがアプリケーションに与える影響を確認することができます.

たった20ミリ秒の追加遅延で、応答時間が5.5倍、転送速度が82%低下し、ユーザーに顕著な遅延が発生しました。ネットワークの飽和、スイッチの不具合、KubernetesによるMySQL Podの別ノードへのスケジューリングなど、現実世界ではほとんど何でも同じような結果を引き起こす可能性があります。40ミリ秒の遅延や100ミリ秒の遅延が発生した場合を想像してみてください。

遅延の影響を軽減するには、サービス間のラウンドトリップネットワークコールの数を減らします。呼び出しを非同期にしたり、Apache Kafkaのようなメッセージキューを使ってこれらのサービスを切り離すことを検討してください。タイトカップリングと同様に、これもドメイン駆動設計を用いてサービスとデータを異なるドメインに分割すべき理由を示す良い例です。

  • 結合の種類

    • デプロイ結合
    • ドメイン結合
  • 分散モノリスを避けるためには Christensen氏が提唱する代替案は、規約とプロトコルだ。サービスは実装の詳細を隠蔽し、データの規約とネットワークプロトコルだけを公開する。サービスの実装へ依存しないようにすることで、利用者はどんな技術でもどんな言語でも使え、自分たちのペースで進化させられる。これは、インターネットの原理と同じだ。氏はロギングや分散トレース、ルーティングのような領域には標準化することに対する合理的なニーズがあることを認めているが、これらは、利用者が使うか使わないか選択できる、独立したライブラリで実現するべきだと主張する。

Christensen氏の考えでは、このような失敗に陥る理由は明白だ。開発者は共有ライブラリの使い方を知っており、短期的な最適化を図ろうとする。そのほうが生産的に感じるからだ。疎結合にするための遅延コストは大きく、開発者は解決策を知っているが、最初から解決策を適切に用意しておくのは追加の努力と思慮が不可欠だ。しかし、適切に用意さえすれば、その解決策はそのまま活用される。それゆえ、Christensen氏は短期的な簡単さを超えた視点を持ち、バイナリでの結合を避け、規約とプロトコルを使ってマイクロサービスアーキテクチャの利点を享受することを推奨する。

また、氏は、ひとつのサービスの中で大規模なフレームワークを利用することについては問題ないと考えている。しかし、システム全体で単一の大規模フレームワークを導入することには反対している。長期的な密結合が生まれるからだ。

  • まとめ

    マイクロサービスへの移行は、モノリシックなアプリケーションをコンテナに詰め替えるだけではありません。アーキテクチャには根本的な違いがあり、データの転送方法や障害からの復旧方法など、あらゆることに影響します。これらの違いを考慮しないと、スケーラビリティの制限、パフォーマンスの低下、予期せぬ障害の発生につながります。

お使いのアプリケーションが分散型モノリスであるかどうかを判断するには、Gremlin Freeアカウントにサインアップすると、数分でアプリケーションの実験を開始できます。CPU攻撃でアプリケーションのオートスケール能力をテストしたり、シャットダウン攻撃でサービス間の緊密な結合をテストしたりすることができます。

参考