『実践マイクロサービスAPI』のメモ
マイクロサービスの特徴
基本的に1つのサービスは複数の機能を組み合わせて構成していることが多い。
例えば、Uber Eats等のデリバリー型のオンライン注文を提供するサービスを考えてみると、以下のような複数の機能によって1つのサービス(機能)が構成されている。
- 商品カタログサービス
- 注文受付サービス
- 配達サービス
- 決済サービス
- 認証サービス
従来のモノリス型では、上記に上げた複数の機能が単一のアプリケーションとして構成されており、ビルドや実行プロセスの単位が同じになっている。すべてのサービスが単一管理で運用されていることで様々な課題があることから、マイクロサービスアーキテクチャが誕生した。(ただし、モノリスが完全に悪ということではなく、ケースバイケースで使い分けましょうという話)
マイクロサービスアーキテクチャは、モノリス型のようにシステムの各コンポーネントが単一アプリケーションで管理/実行されるわけではなく、サービス単位で個別に管理/実行させるアーキテクチャパターン。
マイクロサービスアーキテクチャにおける各サービス間の通信はWeb API経由で行うことが一般的であるため、サービスごとにAPI仕様(エンドポイントやリクエスト/レスポンスの内容)を定義しておくことが重要。
マイクロサービスの利点
モノリスと比較したときにマイクロサービスアーキテクチャには以下のような利点がある。
柔軟性とスケーラビリティ
個々のサービスを独立してスケーリングできるのでシステム全体のリソース使用を最適化できる
疎結合
サービス間の依存関係が最小限に抑えられるため、システムの一部を変更しても他の部分に波及する影響が少なくなる。サービスの独立性が高まるので、別のサービスに依存せずに小規模で独立して作業できる
耐障害性
各サービスが疎結合になっていることで、サービスの一部に障害が発生しても他のサービスは影響を受けずに機能し続けることができる(影響の極小化)
また、サービスごとに障害回復手順を設計できるため、システム全体の復旧時間を短縮できる
技術的な自由度
これも疎結合であるが故の恩恵だが、異なるサービスで異なる技術スタックを採用できるので、状況や環境に応じて最適な言語/FWを選択できる
デプロイメントの簡易化
個々のマイクロサービスを独立してデプロイできるので新機能のリリースや既存機能の更新を迅速に行うことができる
マイクロサービスの課題
一方で、モノリスと比較すると以下のような課題もある。
1.サービスの効果的な分解
マイクロサービスとして各サービスを分解する単位をどの粒度にするのかを事前に設計する必要がある。明確な境界を決めないと、マイクロサービスではなく中途半端な分散モノリスを作ってしまうおそれがある。
2.統合テストの実行
単体テストはマイクロサービスのほうが通常はテストしやすいし時間もかからないが、各サービスを統合したテストはモノリスに比べて大幅に難しくなることがある。以下のようなことを考慮しないといけないから。
2.1.分散システムの複雑性
各サービスはWeb API経由で通信することになるので、その際のネットワーク遅延やメッセージの損失、サービスが利用できないケースでの異常系等を考慮する必要がある。
これらを踏まえると統合テストが複雑化するのでテストケースの設計が難しくなる。
2.2.環境のセットアップと構成
各マイクロサービスが異なる環境で動作する場合、統合テストのためにこれらのサービスを一つのテスト環境に統合する必要がある。
サービスごとに異なるデータベースや外部依存関係がある場合、これらを正しく構成し、同期させることは時間がかかり、エラーが発生しやすくなる。
2.3.テストの自動化と維持の難しさ
各マイクロサービスが更新(例えばAPI仕様が変更)されると、それに追随して統合テストのテストケースの内容も更新する必要がある。
2.4. データ整合性の確認
データ整合性を保証する上で異なるサービス間でのデータの流れを正確に確認する必要がある
3.サービスが利用できないときのエラーハンドリングと分散トランザクションの追跡
マイクロサービスでは、ユーザーからの1つのトランザクションによって複数のサービス間でリクエストチェーンが発生することがある。
例えばUber Eatsだと注文を処理して配達するまでのサービス間で一連のリクエストが実行されるが、いずれかのリクエストが失敗したときにどうなるだろうか。
注文が処理されないままの状態になったり、各サービスで保持する情報が矛盾した状態になってしまうおそれがある。
そのため、処理が失敗したときに確実に対処できるような設計が極めて重要。
また、複数のサービスにまたがってエラーを追跡するのは困難なので、なんらかのソフトウェアテレメトリツールを導入してエラーを特定できる仕組みを作るのが重要。
4.運用の複雑さとインフラのオーバーヘッドの増大
各サービス単位でインフラが必要になるため、その分インフラのプロビジョニングや運用のコストが増大する。Amazonが最初にマイクロサービス化に着手したとき、チーム全体の時間の約70%がインフラの管理に費やされていることがわかったらしい。
そのため、マイクロサービスに着手する前にはインフラの効果的な自動化戦略も考慮に入れる必要がある。
ドキュメント駆動開発を導入する
マイクロサービスは独立して機能する小さなサービスで構成されているので、これらが互いに通信し合うための明確なAPIを用意する必要がある。各サービス間におけるAPI統合の成功はAPIドキュメントの品質にかかっている。これを実現するための有効な手段として、以下のようなドキュメント駆動開発がある。
-
APIを設計して文章化する
-
アプリケーションのUI設計と同じように、APIの設計にもAPIコンシューマーを参加させて仕様を決めることが重要。そうすることで利用者と開発者のエクスペリエンスが向上するだけでなく、APIの統合に失敗する可能性を減らすことができる。
-
APIの仕様を文章としてドキュメント化する際は、以下のような標準インターフェイス記述言語(IDL)を用いる。
- REST API
- OpenAPI
- GraphQL API
- スキーマ定義言語(SDL)
- REST API
-
また、それぞれのIDLにはAPIの仕様の可視化や構築/テストが容易になるようなエコシステムがある。
-
-
APIドキュメントに従ってAPIクライアントとAPIサーバを構築する
-
APIドキュメントに対してAPIクライアントとAPIサーバの両方をテストする
マイクロサービスの設計原則
うまく設計されたマイクロサービスは以下3つの原則が守られている。
1.サービスごとのデータベースの原則
マイクロサービスはそれぞれ独自のデータをDBに保持している。そのデータにはサービスのAPIを通じてアクセスするべきである(DBに直接アクセスしてはならない)
2.疎結合の原則
- サービスはそれぞれ他のサービスから独立した状態で動作できること
- 別のサービスを呼び出さないと機能しない場合、それは関心事が明確に分離されていないおらず、それらのサービスは1体であることを表す。
- サービスはそれぞれ他のサービスに影響を与えることなく更新できること
- サービスを更新するときに別のサービスの更新も必要になる場合、それは密結合なので設計を見直す必要がある。
3.単一責任の原則(Single Responsibility Principle)
サービスの責務ができるだけ少なくなるよう(理想的には1つだけ)に設計すること。
設計の手法としては、ビジネスケイパビリティ or サブドメインに基づいてサービスの分割を行うやり方がある。
- ビジネスケイパビリティによる分割
- ビジネスの構造をもとに組織内のチーム単位でマイクロサービス化する方法
- サブドメインによる分割
- ビジネスプロセスとフローをモデル化してマイクロサービス化する方法。DDDを適用する。
Tolerant Readerパターン
分散システムにおける設計パターンの1つ。
以下のポステルの法則に従うものらしい。
入力の多様性には寛容に、出力は厳密にする
クライアントから送信されてきたペイロードを読む際は、データ構造に多少の変更には影響を受けないように「寛容(Tolerant)」に作るべき、という設計原則
一方でクライアントに送信するペイロードは厳密に検証しなければならないことを意味する。
それ以外にも設計パターンはいくつかある