マイクロサービスは、回復性があり、単独でのデプロイが可能で、迅速に展開できる非常にスケーラブルなアプリケーションを構築するための一般的なアーキテクチャ スタイルです。 成功したマイクロサービス アーキテクチャを構築するには、考え方の根本的な変化が必要です。 アプリケーションをより小さなサービスに分解する以外にも行きます。 また、システムの設計、展開、運用方法についても再考する必要があります。
マイクロサービス アーキテクチャは、小さな自律サービスのコレクションで構成されています。 各サービスは自己完結型であり、境界付けられたコンテキスト内で 1 つのビジネス機能を実装している必要があります。 境界付けられたコンテキストは、ビジネス内の自然な区分であり、ドメイン モデルが存在する明示的な境界を提供します。
マイクロサービスとは
マイクロサービスは、小規模で独立した疎結合のコンポーネントであり、1 つの小規模な開発者チームが作成および保守できます。 各サービスは個別のコードベースとして管理されるため、小規模なチームが効率的に処理できます。 サービスは個別にデプロイできるため、チームはアプリケーション全体をリビルドまたは再デプロイすることなく、既存のサービスを更新できます。 一元化されたデータ 層を持つ従来のモデルとは異なり、マイクロサービスは独自のデータまたは外部状態を保持する役割を担います。 これらは明確に定義された API を介して通信します。これにより、内部実装が他のサービスから隠されます。 このアーキテクチャでは、ポリグロット プログラミングもサポートされています。つまり、サービスは同じテクノロジ スタック、ライブラリ、またはフレームワークを共有する必要はありません。
コンポーネント
サービス自体に加えて、他のコンポーネントは一般的なマイクロサービス アーキテクチャに表示されます。
管理またはオーケストレーション: この管理コンポーネントは、マイクロサービスオーケストレーションを処理します。 ノード間でサービスをスケジュールしてデプロイし、障害を検出し、障害から復旧し、需要に基づく自動スケールを有効にします。 通常、Kubernetes のようなコンテナー オーケストレーション プラットフォームでは、この機能が提供されます。 クラウドネイティブ環境では、Azure Container Apps などのソリューションによって、マネージド オーケストレーションと組み込みのスケーリングが提供されます。 これらのツールにより、デプロイの複雑さと運用上のオーバーヘッドが軽減されます。
API ゲートウェイ: API ゲートウェイは、クライアントのエントリ ポイントとして機能します。 クライアントは、サービスを直接呼び出すのではなく、API ゲートウェイに要求を送信します。 ゲートウェイは、これらの要求を適切なバックエンド サービスに転送します。 また、認証、ログ記録、負荷分散などの横断的な問題も処理します。 クラウドネイティブ マイクロサービス アーキテクチャでは、Envoy や Nginx などの軽量サービス プロキシが内部サービス間通信をサポートします。 この種類の内部トラフィックは、東西トラフィックと呼ばれ、高度なルーティングとトラフィック制御を可能にします。
メッセージ指向ミドルウェア: Apache Kafka や Azure Service Bus などのメッセージング プラットフォームでは、疎結合を促進し、高いスケーラビリティをサポートすることで、マイクロサービスでの非同期通信が可能になります。 これらは、イベント ドリブン アーキテクチャの基盤を形成します。 このアプローチにより、サービスはリアルタイムでイベントに反応し、非同期メッセージングを介して通信できます。
可観測性: 効果的な可観測性戦略は、チームがシステムの信頼性を維持し、問題を迅速に解決するのに役立ちます。 ログの一元化により、ログをまとめ、簡単な診断をサポートします。 OpenTelemetry などのアプリケーション パフォーマンス監視エージェントとフレームワークを使用したリアルタイム監視により、システムの正常性とパフォーマンスを可視化できます。 分散トレースは、サービス境界を越えて要求を追跡します。 これは、チームがボトルネックを見つけ、パフォーマンスを向上させるのに役立ちます。
データ管理: 適切に設計されたデータベース アーキテクチャは、自律性とスケーラビリティをサポートします。 マイクロサービスでは、多くの場合、各サービスの特定のニーズに基づいて、SQL や NoSQL などの異なるデータベースの種類を選択することで、ポリグロット永続化が使用されます。 このアプローチは、ドメイン駆動設計 (DDD) と境界付きコンテキストの概念に合わせて調整されます。 各サービスは、そのデータとスキーマを所有します。 この所有権により、サービス間の依存関係が減少し、サービスを個別に進化させることができます。 この分散型モデルにより、柔軟性、パフォーマンス、システムの回復性が向上します。
メリット
敏捷: マイクロサービスは個別にデプロイされるため、バグ修正と機能リリースを管理する方が簡単です。 アプリケーション全体を再デプロイせずにサービスを更新し、問題が発生した場合は更新プログラムをロールバックできます。 多くの従来のアプリケーションでは、アプリケーションの 1 つの部分でバグが見つかると、リリース プロセス全体がブロックされる可能性があります。 たとえば、バグ修正を統合、テスト、公開する必要がある場合、バグによって新機能がストールする可能性があります。
小規模で集中したチーム: マイクロサービスは、1 つの機能チームが構築、テスト、デプロイできる十分な小さいものにする必要があります。 小規模なチーム編成により、機敏性が向上します。 大規模なチームは、コミュニケーションが遅くなり、管理オーバーヘッドが増加し、機敏性が低下するため、生産性が低下する傾向があります。
小さなコード ベース: モノリシック アプリケーションでは、コードの依存関係が時間の経過と同時に絡み合うことがよくあります。 新しい機能を追加するには、コードベースの多くの部分で変更が必要になる場合があります。 マイクロサービス アーキテクチャでは、コードやデータ ストアを共有しないことで、この問題を回避できます。 この方法では、依存関係を最小限に抑え、新機能の導入を容易にします。
テクノロジの組み合わせ: Teams は、必要に応じてテクノロジ スタックを組み合わせて使用することで、サービスに最適なテクノロジを選択できます。
障害の分離: 個々のマイクロサービスが使用できなくなった場合、アップストリームマイクロサービスが障害を正しく処理するように設計されている限り、アプリケーション全体が中断されることはありません。 たとえば、 サーキット ブレーカー パターンを実装したり、 非同期メッセージング パターンを使用してマイクロサービスが相互に通信するようにソリューションを設計したりできます。
スケーラビリティ: サービスは個別にスケーリングできます。 この方法では、アプリケーション全体をスケールアウトすることなく、より多くのリソースを必要とするサブシステムをスケールアウトできます。 Kubernetes などのオーケストレーターを使用して、1 つのホストに高い密度のサービスを追加し、リソースをより効率的に使用できるようにします。
データの分離: 影響を受けるマイクロサービスは 1 つだけであるため、マイクロサービス アーキテクチャではスキーマの更新が簡単になります。 一方、モノリシック アプリケーションでは、複数のコンポーネントが同じデータと対話することが多いため、スキーマの変更が複雑になります。 この共有アクセスにより、変更が危険になる可能性があります。
課題
マイクロサービスの利点にはトレードオフがあります。 マイクロサービス アーキテクチャを作成する前に、次の課題を考慮してください。
複雑さ: マイクロサービス アプリケーションには、同等のモノリシック アプリケーションよりも多くの可動部分があります。 各サービスはより単純ですが、システム全体としてはより複雑です。 アプリケーションを設計するときは、サービスの検出、データの整合性、トランザクション管理、サービス間通信などの課題を考慮してください。
開発とテスト: 他の依存サービスに依存する小規模なサービスを作成するには、従来のモノリシックまたは階層型アプリケーションを記述する方法とは異なるアプローチが必要です。 既存のツールは、サービスの依存関係を操作するように設計されているわけではありません。 サービス間の境界を越えたリファクタリングは、難しい場合があります。 また、特にアプリケーションが急速に進化している場合は、サービスの依存関係をテストすることも困難です。
ガバナンスの欠如: マイクロサービスを構築するための分散型アプローチには利点がありますが、問題が発生する可能性もあります。 多くのさまざまな言語やフレームワークを使用するために、アプリケーションの維持が難しくなることがあります。 チームの柔軟性を過度に制限することなく、プロジェクト全体に標準を導入することが有益な場合があります。 この方法は、特にログ記録などの横断的機能に適用されます。
ネットワークの輻輳と待機時間: 小規模で細かいサービスを多数使用すると、サービス間通信が増える可能性があります。 また、サービスの依存関係のチェーンが長すぎると (サービス A は C...を呼び出す B を呼び出します)、余分な待機時間が問題になる可能性があります。 API は慎重に設計する必要があります。 過度におしゃべりな API を避け、シリアル化形式について考え、 Queue-Based 負荷平準化パターンのような非同期通信パターンを使用する場所を探します。
データの整合性: 各マイクロサービスは、独自のデータ永続化を担当します。 その結果、複数のサービス間でのデータの整合性が課題となる可能性があります。 さまざまなサービスがさまざまなタイミングで、さまざまなテクノロジーを使用してデータを永続化しますが、それぞれに成功レベルが異なる可能性があります。 新しい日付または変更日の永続化に複数のマイクロサービスが関係している場合、完全なデータ変更がアトミック、一貫性、分離、および永続的 (ACID) トランザクションと見なされる可能性はほとんどありません。 代わりに、この手法は Basically Available、Soft State、Eventual Consistency (BASE) に合わせて調整されます。 可能であれば、最終的な整合性を優先します。
管理: マイクロサービス アーキテクチャを成功させるには、成熟した DevOps カルチャが必要です。 サービス間で相互に関連付けられたログを記録することは難しい場合があります。 通常、ログ記録は単一のユーザー操作を要求するために複数のサービスの呼び出しを関連付ける必要があります。
バージョン管理: サービスの更新によって、サービスに依存するサービスが中断されないようにする必要があります。 複数のサービスが特定の時点で更新される場合があるため、慎重に設計しないと下位互換性または上位互換性に問題が生じる可能性があります。
スキル セット: マイクロサービスは高度に分散されたシステムです。 成功のために必要なスキルと経験がチームにあるかどうかを慎重に評価してください。
ベスト プラクティス
ビジネス分野周辺のモデル サービス。 DDD を使用して境界付きコンテキストを識別し、明確なサービス境界を定義します。 過度に細かいサービスを作成することは避けてください。これにより、複雑さが増し、パフォーマンスが低下する可能性があります。
すべてを分散化します。 個々のチームは、サービスの設計と構築をエンドツーエンドで行う責任があります。 コードまたはデータのスキーマを共有しないようにします。
使用する言語とフレームワークの数を制限することで、テクノロジの選択を標準化します。 ログ記録、監視、デプロイのためのプラットフォーム全体の標準を確立します。
データ ストレージは、そのデータを所有するサービス専用にする必要があります。 各サービスとデータの種類に最適なストレージを使用します。
サービスが、適切に設計された API を介して通信するようにします。 実装の詳細が漏えいしないようにします。 API は、サービス内部の実装ではなく、ドメインをモデル化する必要があります。
サービス間の結合は行わないようにします。 結合の原因には、共有データベース スキーマや固定の通信プロトコルなどがあります。
サービス間暗号化に相互トランスポート層セキュリティ (mTLS) を使用してセキュリティを強化します。 ロールベースのアクセス制御を実装し、API ゲートウェイを使用してポリシーを適用します。
認証や Secure Sockets Layer の終了などの横断的な問題をゲートウェイにオフロードします。 Dapr のようなサービス メッシュとフレームワークは、mTLS 認証や回復性などの一般的な横断的な問題にも役立ちます。
ドメインのナレッジをゲートウェイの外部で保持します。 ゲートウェイは、ビジネス ルールやドメイン ロジックのナレッジを使用せずに、クライアントの要求を処理し、ルーティングする必要があります。 そうしなければ、ゲートウェイが依存関係となり、サービス間の結合が生じる可能性があります。
サービスには、疎結合と機能の高い凝集度が必要です。 まとめて変更される可能性がある機能は、まとめてパッケージ化してデプロイする必要があります。 それらが別々のサービスに存在する場合、一方のサービスの変更ではもう一方のサービスを更新する必要があるため、これらのサービスは密接に結合されます。 2 つのサービス間の過度におしゃべりな通信は、緊密な結合と低凝集の症状である可能性があります。
継続的インテグレーションと継続的デプロイ (CI/CD) パイプラインを使用して、テストとデプロイを自動化します。 サービスを個別にデプロイし、ロールアウトの正常性を監視します。
障害を分離します。 回復性戦略を使用して、サービス内の障害が連鎖しないようにします。 詳細については、「 回復性パターン 」および「 信頼性の高いアプリケーションの設計」を参照してください。
カオス エンジニアリングを使用して、マイクロサービス アーキテクチャとその依存関係の回復性をテストします。 システムが部分的な障害を処理する方法を評価して改善します。
監視を確保するために、一元的なログ記録、分散トレース (OpenTelemetry)、メトリック収集を実装します。
マイクロサービスのアンチパターン
マイクロサービスを設計して実装すると、特定の落とし穴が頻繁に発生し、このアーキテクチャ スタイルの利点を損なう可能性があります。 これらのアンチパターンを認識することで、チームはコストのかかるミスを回避し、回復性と保守性の高いシステムを構築するのに役立ちます。 次のアンチパターンは避けてください。
ビジネス ドメインを深く理解せずにマイクロサービスを実装すると、サービスの境界が不適切になり、意図した利点が損なわれます。
過去または将来のイベントに依存するイベントを設計することは、アトミックで自己完結型のメッセージングの原則に違反します。 この依存関係により、コンシューマーは強制的に待機し、システムの信頼性が低下します。
イベントとしてデータベース エンティティを使用すると、内部サービスの詳細が公開され、多くの場合、適切なビジネス意図を伝えることができず、緊密に結合され、統合が不明確になります。
すべてのコストでデータの重複を回避することは、アンチパターンです。 具体化されたビューのようなパターンを使用してローカル コピーを維持すると、サービスの自律性が向上し、サービス間の依存関係が減少します。
汎用イベントを使用すると、コンシューマーはメッセージを解釈およびフィルター処理する必要があります。 この方法では、不要な複雑さが増し、イベントドリブン通信の明瞭さが軽減されます。
マイクロサービス間で共通のライブラリまたは依存関係を共有すると、緊密な結合が作成され、変更が危険で広範囲に及び、自己完結型サービスの原則に反します。
マイクロサービスをコンシューマーに直接公開すると、密接な結合、スケーラビリティの問題、およびセキュリティ リスクが発生します。 API ゲートウェイを使用すると、クリーンで管理しやすい安全なエントリ ポイントが提供されます。
マイクロサービス内に構成値を保持すると、それらを特定の環境に密に結合できるため、デプロイが困難になります。 ただし、構成を外部化すると、柔軟性と環境の移植性が向上します。
トークン検証などのセキュリティ ロジックをマイクロサービス内に直接埋め込むと、コードとメンテナンスが複雑になります。 または、専用コンポーネントにセキュリティをオフロードすることで、サービスに集中してクリーンな状態を保ちます。
一般的なマイクロサービス タスクを抽象化しないと、エラーが発生しやすいコードが繰り返され、柔軟性が制限されます。 または、Dapr のような抽象化フレームワークを使用すると、ビジネス ロジックをインフラストラクチャの問題から切り離すことで開発が簡略化されます。
マイクロサービス アーキテクチャを構築する
次の記事では、マイクロサービス アーキテクチャを設計、構築、運用するための構造化されたアプローチについて説明します。
ドメイン分析を使用する: マイクロサービスを設計するときの一般的な落とし穴を回避するには、ドメイン分析を使用してマイクロサービスの境界を定義します。 次の手順を実行します。
サービスを設計します。 マイクロサービスでは、アプリケーションの設計と構築に分散型のアジャイル アプローチが必要です。 詳細については、「 マイクロサービス アーキテクチャの設計」を参照してください。
運用環境で運用する: マイクロサービス アーキテクチャは分散されているため、デプロイと監視のための堅牢な操作が必要です。