🚀

マイクロサービスの概要 モノリスとの違いや特徴について

2024/04/04に公開

アーキテクチャや設計周りのことに興味があったので、マイクロサービスについて色々調べてみました。今回はそれをもとに、マイクロサービスの概要ついて書いていきたいと思います。

マイクロサービスとは

マイクロサービスとは、アプリケーションが小さな独立した機能に分割し、その集合として構築するアーキテクチャや開発手法のことをいいます。ただ、これにはどこまで分割すればそれをマイクロサービスと呼ぶ、といった明確な定義があるわけではありません。組み合わさっているサービスの大きさもバラバラであることが多いです。よって多数のサービスで構築されているアプリケーションを広い意味でマイクロサービスと言っているという解釈で問題ないかと思います。

マイクロサービスとの対比でモノリスという言葉を聞いたことがある人も多いと思います。マイクロサービスについての理解を深めるために、まずはこのモノリスとはどういったアプリケーションのことをいうのかについて書いていきます。

モノリスとは

モノリスとはモノリシック(一枚岩)なアプリケーションのことです。具体的にはアプリケーションの全ての機能が単一のソースコードリポジトリ内で開発されているもののことです。

Ruby on RailsやLaravelなどのフルスタックなWebアプリケーションフレームワークを利用して作られる、フロントエンドやバックエンドでコードが分かれておらず、見た目の部分も裏側のロジック部分も全て同じソースコードリポジトリ内で書くアプリケーションがその代表例です。モノリスのアプリケーションには以下のようなメリットがあります。

モノリスのメリット

  • 開発しやすい
    モノリスアプリケーションでは、全ての機能が単一のソースコードリポジトリ内に存在するため、開発者は様々なコンポーネント間での連携に関して心配する必要がありません。これにより、新しい開発者もプロジェクトに参加しやすく、理解しやすいため、開発の初期段階で効率よく作業を進めることができます。
  • デバッグしやすい
    単一のアプリケーションで完結しているため、エラー発生時の原因特定が比較的簡単です。マイクロサービスのように、複数のサービス間でのデバッグではなく、一つのアプリケーションのコンテキスト内で問題を追跡できるため、デバッグプロセスが簡単になります。
  • デプロイしやすい
    モノリスアプリケーションは、全てが一つのコードベースに含まれているため、デプロイプロセスが単純化されます。一つのアプリケーションとしてビルドし、デプロイするだけで良く、マイクロサービスのように、複数のサービスを個別にデプロイする必要がありません。

モノリスのアプリケーションは規模が小さいうちは上記のメリットを享受しやすく、スピーディに開発できるので、そこが大きな強みになります。しかし、サービスが成長しコードが肥大化するにつれて様々な課題が発生してきます。以下にサービス規模が大きくなるにつれて生まれてくる課題の例をあげてみます。

モノリスの課題

  • コード修正時の影響範囲の特定が難しい
    アプリケーションの規模が大きくなってくると、あるコードを変更したときにそれがどこまで影響するのか特定するのが難しくなってきます。そのため小さな変更を行った際に予期せぬ箇所に不具合が生じることも考えられます。要はコードが密結合になり、ある機能の修正が他の機能にどのような影響を与えるかを完全に理解することが難しくなってしまうのです。
  • 開発速度が低下してくる
    コードが大規模になると、ビルド時間が長くなり、テストやデプロイにも時間がかかるようになります。また、多くの開発者が同じソースコードリポジトリで作業するとコンフリクトが発生しやすくなり、開発速度が遅くなる可能性があります。
  • 一部のバグがサービス全体に影響する
    モノリスアプリケーションでは、一つの部分で発生したバグがアプリケーション全体に影響を与える可能性があります。これは、アプリケーションの各部分が互いに密接に関連しているためで、障害を局所化し、影響範囲を限定することが難しくなってきます。
  • 技術スタックが古くなりやすい
    モノリスアプリケーションでは、新しい技術やフレームワークへの移行が困難になることがあります。全ての機能が単一のコードベースに統合されているため、一部分だけを新しい技術に移行することが難しく、技術スタックを更新するためにはコードを全体的に書き換える必要が出てきます。そうなるとリスクが高く、膨大な作業コストがかかるため技術スタックを更新せずにやり過ごすという選択が取られることが多くなってきます。

このように、モノリスは規模が小さめで単純なアプリケーションには適していますが、規模が大きく、複雑になっていくにつれて課題が目立ってくるので、そのタイミングでマイクロサービスに移行する組織も多くなっています。

ではモノリスとはどういうものかがわかったところで、マイクロサービスについて詳しく書いていきたいと思います。

マイクロサービス

マイクロサービスのメリット

まずはモノリスの課題も踏まえて、マイクロサービスにはどのようなメリットがあるかについてです。一般的に言われるマイクロサービスのメリットには以下のようなものがあげられます。

  • 修正の影響範囲が限定される
    マイクロサービスでは、各サービスが独立しているため、1つのサービスに加えた修正が他のサービスに影響を与えるリスクが低くなります。これにより、変更を迅速に行いリリースすることができ、イテレーションの速度が向上します。しかし、これを達成するにはサービス間が疎結合になるよう分割することが重要であり、サービスが適切に分割できていない場合、分散モノリスと呼ばれるようなモノリスとマイクロサービスの欠点を併せ持ってしまう状態になるので注意が必要です。
  • 個々のサービスが小さく、メンテナンスが簡単
    マイクロサービスの一つ一つの独立したサービスは比較的規模が小さいので理解しやすく、問題が発生した場合もデバッグしやすくなります。また、チームごとに1つまたは複数の関連サービスを管理することができ、それぞれがそのサービスの専門知識を深めることが可能になります。これにより、メンテナンスと機能拡張の効率が向上します。
  • 大規模で複雑なアプリケーションの継続的デリバリー・デプロイを可能にする
    マイクロサービスの独立性は、部分的なアップデートや機能追加を簡単にします。これにより、大規模なシステムでも継続的なデリバリーとデプロイが可能となり、迅速な機能拡張を実現できます。
  • リリース周期を早められる
    独立したサービスごとに開発、テスト、デプロイを行うことができるため、全体としてのリリースサイクルを短縮できます。
  • 新しい技術を取り入れやすい
    各マイクロサービスは他のサービスと独立しており、異なる技術スタックで開発することが可能です。これにより、既存技術の更新や新しい技術を試す際のリスクが低減され、技術的な負債が蓄積しにくくなります。
  • サービスに合わせたスケールができる
    システムの特定部分に対する需要が高い場合、その部分を提供するマイクロサービスのみをスケールアウトすることが可能です。これにより、リソースの利用効率が向上し、コスト効率的な運用が可能になります。全体として必要なリソース量を最適化でき、ピーク時のパフォーマンス要求にも柔軟に対応できます。

メリットの多いマイクロサービスですが、もちろん課題も存在します。以下にマイクロサービスの課題の例をあげてみます。

マイクロサービスの課題

  • サービスの適切な分割方法を決めるのが難しい
    マイクロサービスを設計する際の最も大きな課題の一つは、どのようにして機能を適切にサービスに分割するかを決定することです。これが適切に行えないとメリットの修正の影響範囲が限定されるの箇所にも書いたように、依存性の複雑化や過度の通信を引き起こし、システム全体の性能やメンテナンス性を低下させる可能性があります。いわゆる分散モノリス状態になってしまうということです。これを防ぐためにはビジネスのドメイン知識やサービスの全体像を正確に把握する必要があります。
  • アプリケーションの設計が複雑になる
    マイクロサービスアーキテクチャを採用すると、各サービス間の通信、データ一貫性の維持、トランザクション管理など、多くの追加的な設計考慮事項が生じます。これらはシステムの設計を複雑にし、開発者が把握すべき仕様の数を増加させます。
  • 複数サービスに影響があるデプロイの場合、タイミングを調整しないといけない
    機能追加や修正が複数のマイクロサービスにまたがる場合、これらのサービスを同時にまたは特定の順序でデプロイする必要があります。このような依存関係があるデプロイは、タイミングの調整が難しく、失敗のリスクを増加させる可能性があります。
  • アプリケーション全体のテストがしにくくなる
    各マイクロサービスが独立しているため、エンドツーエンドでのシステムの振る舞いをテストすることが難しくなります。サービス間の相互作用を考慮した包括的なテスト戦略が必要となり、テストの複雑さとコストが増加します。

マイクロサービスの主なメリットと課題について何となくイメージがついたかと思います。ここからはマイクロサービス化を進めるにあたって知っておくべきポイントをいくつか紹介します。

マイクロサービス化のポイント

サービスのサイズは重要ではない

マイクロサービスのポイントとして抑えておきたいのが、サービスのサイズはあまり重要ではないという点です。「マイクロ」というくらいなので、個々のサービスは小さくないといけないような気がしてしまいますが、そういう訳ではなく、サービスの大きさにはあまり意味はありません。サービスを分割すると自ずと個々のサービスサイズは小さくなってはいきますが、サービスのサイズを小さくすること自体が目的ではないので、その点は抑えておきましょう。
 それよりも重要なのはサービスを適切な粒度で分割し、サービス間を疎結合にすることです。上に書いたメリットを享受するためにはここはマストになってくるので、フォーカスする点を間違えないことが重要です。

データベースもサービスに合わせて分割する

マイクロサービスはサービスと一緒にデータベースも分割します。サービスだけが分かれてデータベースは共通なのではなく、データベースもサービスに合わせて分割されるということです。モノリスのサービスでは当然サービスとデータベースは共通ですが、そのようなサービスとデータベースのセットが集まって一つのアプリケーションを構築しているというわけです。これは、サービス間を疎結合にし、API以外で通信できない状態にするというマイクロサービスの特徴を実現するために必要な処理です

新規サービスに対するマイクロサービスの考え方

マイクロサービスは、新規サービスをマイクロサービスで作るよりも、既存のサービスをマイクロサービスに分割していくことの方が多いと思います。というよりも、そもそも新規サービス立ち上げの際はサービスの境界が曖昧なことも多いため、マイクロサービス化するには時期尚早であることも多いです。上に書いたようにサービスを適切な粒度で分割することが最重要です。そのため、最初はモノリスやそれに近い状態でサービス開発を進め、ある程度サービスが成熟して境界がはっきりしてきた頃にコードベースの増大に伴う開発速度の低下や、メンテナンス上の問題が顕在化してきたらサービス分割(マイクロサービス化)を検討する方が理にかなっていると言えるでしょう。

マイクロサービスの課題にも書いたように、マイクロサービス化を進める際にサービスの適切な分割方法を決めるのは難しく、分散モノリス状態になってしまうとデメリットの方が大きくなってしまう可能性もあります。では適切にサービスを分割するにはどのような点に注意すれば良いのでしょうか。ここからはそれについて書いていきたいと思います。

適切にサービスを分割するには?

マイクロサービス化しなければいけない理由を考える

これは分割を進める前段階の話になりますが、なぜマイクロサービス化する必要があるのか、達成すべき目標は何なのかをしっかりと考える必要があります。上のマイクロサービスの課題でも書いたようにマイクロサービスは万能ではなく、マイクロサービス化することで発生する課題も多く存在します。それでもマイクロサービスを採用する必要があるのか、その場合どのような成果を得るためにマイクロサービス化を進めるのかしっかりと考えることがマイクロサービス化の第一歩となります。

分割の粒度を決定する

分割の粒度を適切に決定することがマイクロサービス化するための一番の肝と言っても間違いではないでしょう。ここの判断を誤るとデメリットの方が大きくなってしまう可能性があります。具体的な粒度を決めるもとになるのがドメインです。ここでいうドメインとはドメイン駆動設計の文脈で使用されるものと同様のビジネスの専門領域や業務領域のことです。オンラインショップを例にドメインの分割について簡単に考えてみます。

ドメイン名 内容
商品管理 商品情報の登録、更新、削除などを扱う。
注文管理 顧客の注文処理、注文状況の追跡、注文履歴の管理などを行う。
顧客管理 顧客情報の管理と、顧客サービス機能を提供する。
支払管理 支払方法の管理、支払承認、請求処理などを行う。
配送管理 配送オプションの提供、配送状況の追跡、配送業者との連携を行う。

ここでは簡単に書きましたが、実際にマイクロサービスを設計する際は、これらのドメインをさらに細かく分析し、サービス間の依存関係や通信のパターンを考慮してサービスの独立性を確保するバランスを見つけることが重要です。

まとめると、マイクロサービスにおけるサービス分割は、ドメインをもとに各サービスが独立し、疎結合になるように行うことが重要です。そのためにはコードベースの理解はもちろんですが、ビジネスプロセスの理解も重要になってきます。そのため、分割の粒度を決定する際にはエンジニアだけではなく、ビジネスサイドとも連携を取りながら進めていくケースも多いようです。

ここからは、実際にマイクロサービス化を実行するフェーズに考慮すべきことを書いていきます。

新機能からマイクロサービス化を進める

マイクロサービス化を進める際にまず実践しやすいのは、モノリスに対する機能追加が必要になった場合にそれを新たなサービスとして実装することです。もちろん全ての追加機能がモノリスから単純に独立させられるとは限りませんが、可能であればここから始めていくのが既存サービスに与える影響も小さく、効果も実感しやすいでしょう。逆に後々マイクロサービス化を進めていく方針であるにも関わらず、モノリスのコードベース内で追加機能を実装してしまうとモノリスのコードベースがさらに肥大化し、いざサービス分割をする際に考慮すべき要素が増える原因にもなるので、その後のサービスの運用方針に合わせて追加機能の実装方法を適切に判断することが重要です。

優先順位を決めて移行を進めていく

分割の優先順位を決めることもマイクロサービス化を進める上で重要になってきます。基本的にはマイクロサービス化の進めやすさと、分割するメリットの大きさの2つを指標として優先順位を判断していきます。分割を進めやすいかつ、分割するメリットの大きい箇所から移行を進めていく形になるでしょう。その後は分割の進めやすさと分割のメリットどちらを優先するかはサービス内容や組織の都合によると思いますが、分割のリスクが大きい(依存関係が複雑で分割の影響範囲が大きい)箇所や分割のメリットが小さい(ユーザーの目に触れない管理画面で変更に急を要さない)箇所は基本的に優先順位は低くなるでしょう。

徐々に移行を進めていく

マイクロサービス化を進めることが決定した場合、まず重要なのはいきなりサービス全てを分割しようとしないことです。これは何となくイメージはつくと思いますが、一気に分割を進めようとすると影響範囲が大きすぎて手に負えなくなる可能性が高いためです。まずは部分的にマイクロサービス化して徐々にモノリスのサイズを縮小していくのが基本的なマイクロサービス化手法となっています。このように、サービス分割を進めていく上で徐々に作られていく個々のサービスのことをストラングラーアプリケーションと呼びます。

一通りマイクロサービスの解説が済んだところで、マイクロサービスと似たアーキテクチャ手法であるサービス指向アーキテクチャについて少し書いていきます。

マイクロサービスとサービス指向アーキテクチャ

サービス指向アーキテクチャは、マイクロサービスと同様に複数のサービスから一つのシステムを構築する設計手法です。ただ、サービス指向アーキテクチャの個々のサービスは再利用可能なコンポーネントとして設計されており、新規システム開発時に既存のサービスを組み合わせて再構築することで開発速度を上げることを目的としています。複数のサービスの集合が一つのシステムを作るという意味で似たような設計手法ですが、サービス指向アーキテクチャとマイクロサービスには以下のような違いがあります。

  • サービスのサイズと数
    サービス指向アーキテクチャは少数の大きめなサービスから構成されます。それに対してマイクロサービスは通常多数の小さめなサービスから構成されます。
  • データベース
    サービス指向アーキテクチャは全てのサービスが共通のデータベースを使用します。マイクロサービスは個々のサービスが専用のデータベースを持ちます。
  • サービス間通信
    サービス指向アーキテクチャはサービス間通信にSOAP等のプロトコルを用いるESB(エンタープライズサービスバス)が使用されることが多く、マイクロサービスではRESTやgRPCなどの軽量なプロトコルが使用されることが多いです。ここでいうESBとは異なるアプリケーションやサービス間のデータ通信や統合をサポートするアーキテクチャのことです。

マイクロサービスとサービス指向アーキテクチャの関連についてですが、マイクロサービスはサービス指向アーキテクチャの欠点に対処できるものとして生まれました。そのためマイクロサービスはサービス指向アーキテクチャの上位互換と考えられています。

マイクロサービスでよく使用される機能

ここからはマイクロサービスでよく使用される機能をいくつか紹介していきます。

サーガ

マイクロサービスのようにサービスに合わせてデータベースを分割すると、複数のデータベースを同時に更新する際に整合性を保つための仕組みが必要になってきます。その際に利用されるのがサーガです。サーガは一つのサービスのトランザクションが完了すると、それをトリガーに次のトランザクションを実行するという仕組みになっています。サーガは以下の3つのトランザクションから構成されています。

  • 補償可能トランザクション
    ロールバックされる可能性があるトランザクションです。ロールバックには補償トランザクションが利用されます。
    ※補償トランザクションとは、トランザクションの一連の処理を逆向きに実行するロールバック用のトランザクションのことです。サーガでは個々のサービスがデータベースにコミットするため、ACIDトランザクションのように自動的にロールバックを行うことができません。そのため、ロールバック専用の補償トランザクションを用意する必要があります。
  • ピボットトランザクション
    処理を最後まで実行するか、キャンセルするかを判断するポイントとなるトランザクションです。ピボットトランザクションがコミットされた場合、サーガは最後まで実行されます。
  • 再試行可能トランザクション
    ピボットトランザクションに続いて実行されるトランザクションで、成功することが保証されています。

サーガには以下の2種類の実装方法があります。

  • オーケストレーション
    オーケストレーターという各サービスを制御するサービスを定義し、中央集権的にトランザクションを制御します。
  • コレオグラフィ
    各サービスが必要な他のサービスのイベントをサブスクライブする形でトランザクションを制御します。

コレオグラフィにはオーケストレーションと比べてサービス間にサーガの実装が分散してわかりにくくなることや、サービス間の結合度が上がってしまうなどのデメリットがあるため、基本的にオーケストレーションを使用することが推奨されています

メッセージブローカー

メッセージブローカーはサービス間の非同期通信を仲介する機能です。

メッセージブローカーを導入することで、全てのメッセージングはメッセージブローカーを経由して行われるようになります。メッセージブローカーを導入するメリットには以下のようなものがあげられます。

  • メッセージのバッファリング
    メッセージブローカーは、メッセージが処理できるようになるまでメッセージをバッファリングします。メッセージは処理するサービスが処理可能になるまでメッセージブローカーで待機します。これにより、仮に処理するサービスが速度低下や障害に陥っても、処理をユーザーから受け取るサービスは処理の受け取りを継続し、それをメッセージブローカーに送信して待機させておくことでサービスの停止を防ぐことができます。
  • サービス間が疎結合になる
    メッセージブローカーを介して通信することで、サービス間の依存関係が最小限に抑えられます。各サービスは他のサービスの実装詳細を知る必要がなく、インターフェース(メッセージ形式)だけを理解していればよいため、疎結合が実現されます。マイクロサービスではサービス間の通信が多くなるため、その通信の耐障害性や柔軟性を高めるためにメッセージブローカーが広く採用されています。

APIゲートウェイ

マイクロサービスのようにサービスを細かく分割すると、クライアントが必要なデータを手に入れるために複数のリクエストを行う必要が出てきて、通信効率が悪くなってしまいます。また、個々のサービスにアクセスしていると、認証認可の管理も複雑になってきます。そこで、マイクロサービスではクライアントからのリクエストをAPIゲートウェイで一元的に管理します。これにより個々のサービスのエンドポイントを一つにまとめることができ、クライアントは単一のエントリーポイントを通じてシステムの様々な機能にアクセスできるようになります。認証と認可のロジックもAPIゲートウェイに集約することで、個々のサービスが認証や認可の詳細を気にすることなく、ビジネスロジックに集中できるようになります。

最後に

今回はマイクロサービスについて概要をまとめてみました。今後もマイクロサービスに限らず、色々なアーキテクチャに関して理解を深めていきたいです。最後までご覧いただきありがとうございました。

参考

https://www.amazon.co.jp/マイクロサービスパターン-実践的システムデザインのためのコード解説-impress-top-gear/dp/4295008583

Discussion