コンテナー化されたマイクロサービス

ヒント

この内容は電子ブック『.NET MAUI を使用したエンタープライズ アプリケーション パターン』からの抜粋です。これは .NET Docs で閲覧することも、無料の PDF をダウンロードしてオフラインで読むこともできます。

電子ブック『.NET MAUI を使用したエンタープライズ アプリケーション パターン』の表紙のサムネイル。

クライアント/サーバー アプリケーションの開発においては、各層で特定のテクノロジを使用する階層化されたアプリケーションの構築に重点が置かれました。 このようなアプリケーションは、多くの場合 "モノリシック" と呼ばれ、ピーク時の負荷に合わせて事前にスケーリングされたハードウェア上にパッケージ化されます。 この開発アプローチの主な欠点は、各層のコンポーネント間の緊密な結合、そのため個々のコンポーネントを簡単にスケーリングできないこと、テストのコストです。 簡単な更新が層の残りの部分に予期しない影響を与える可能性があるため、アプリケーション コンポーネントを変更した場合、その層全体を再度テストして展開し直す必要があります。

特に懸念されるのは、このクラウドの時代において、個々のコンポーネントを簡単にスケーリングできないことです。 モノリシック アプリケーションにはドメイン固有の機能が含まれ、通常は、フロントエンド、ビジネス ロジック、データ ストレージなどの機能層に分割されています。 次の図は、モノリシック アプリケーションが、アプリケーション全体を複数のマシンに複製することによってスケーリングされることを示しています。

モノリシック アプリケーションのスケーリング アプローチ。

マイクロサービス

マイクロサービスは、アプリケーションの開発と展開に対する異なるアプローチを提供します。これは、最新のクラウド アプリケーションの機敏性、スケール、信頼性の要件に適したアプローチです。 マイクロサービス アプリケーションは、連携してアプリケーションの全体的な機能を提供する独立したコンポーネントに分割されています。 マイクロサービスという用語は、アプリケーションが特定の関心事項を反映するのに十分な小さいサービスで構成される必要があることを強調しており、したがって各マイクロサービスは 1 つの機能を実装します。 さらに、各マイクロサービスには、他のマイクロサービスとの通信およびデータ共有に使用される明確に定義されたコントラクトがあります。 マイクロサービスの一般的な例としては、ショッピング カート、在庫処理、購入サブシステム、支払い処理などがあります。

まとめてスケーリングされる大規模なモノリシック アプリケーションと比較して、マイクロサービスは個別にスケーリングできます。 つまり、需要に応じるために多くの処理能力やネットワーク帯域幅を必要とする特定の機能領域をスケールアウトでき、他のアプリケーション領域を不必要にスケールアウトすることはありません。 次の図は、このアプローチを示しており、マイクロサービスが個別に展開およびスケーリングされ、マシンにまたがるサービスのインスタンスが作成されます。

マイクロサービスのアプリケーション スケーリング アプローチ。

マイクロサービスのスケールアウトは、ほぼ瞬時に行うことができるため、アプリケーションは負荷の変化に適応できます。 たとえば、アプリケーションの Web 向け機能内の 1 つのマイクロサービスが、追加の受信トラフィックを処理するためにスケールアウトする必要がある唯一のマイクロサービスである可能性があります。

アプリケーションのスケーラビリティのための従来のモデルには、永続的なデータを格納する共有の外部データストアを含む、負荷分散されたステートレスな層があります。 ステートフルなマイクロサービスは独自の永続的なデータを (通常は配置されたサーバーにローカルに格納して) 管理し、ネットワーク アクセスのオーバーヘッドとサービス間操作の複雑さを回避します。 これにより、可能な最速のデータ処理が可能になり、キャッシュ システムが不要になります。 さらに、スケーラブルなステートフル マイクロサービスは、単一のサーバーがサポートできる以上のデータ サイズと転送スループットを管理するために、通常はインスタンス間でデータを分割します。

マイクロサービスを使用すると、独立した更新もサポートされます。 マイクロサービス間の疎結合は、迅速かつ信頼性の高いアプリケーションの進化をもたらします。 独立しており、分散されているという性質は、単一のマイクロサービスのインスタンスのサブセットのみが常に更新されるローリング更新に役立ちます。 したがって、問題が検出された場合は、すべてのインスタンスが欠陥のあるコードまたは構成で更新される前に、バグを含む更新をロールバックできます。 同様に、マイクロサービスには通常、スキーマのバージョン管理が使用されるため、どのマイクロサービス インスタンスと通信しているかに関係なく、更新の適用時に一貫したバージョンがクライアントに表示されます。

したがって、マイクロサービス アプリケーションにはモノリシック アプリケーションよりも多くの利点があります。

  • 各マイクロサービスが比較的小さく、管理しやすく進化させやすい。
  • 他のサービスとは関係なく各マイクロサービスを開発して展開できます。
  • 各マイクロサービスを個別にスケールアウトできます。 たとえば、カタログ サービスまたはショッピング バスケット サービスを注文サービスよりも大きくスケールアウトすることが必要な場合があります。 そのため、結果として得られるインフラストラクチャでは、スケールアウト時にリソースがより効率的に消費されます。
  • 各マイクロサービスで問題が分離されます。 たとえば、サービスに問題がある場合、影響を受けるのはそのサービスだけです。 他のサービスは引き続き要求を処理できます。
  • 各マイクロサービスで最新のテクノロジを利用できます。 マイクロサービスは自律的であり並行して実行されるため、モノリシック アプリケーションで使用される可能性のある古いフレームワークを強制的に使用するのではなく、最新のテクノロジとフレームワークを使用できます。

ただし、マイクロサービス ベースのソリューションには潜在的な欠点もあります。

  • アプリケーションをマイクロサービスに分割する方法を選択するのは困難な場合があります。各マイクロサービスは、データ ソースに対する責任を含め、端から端まで完全に自律的である必要があります。
  • 開発者はサービス間通信を実装する必要がありますが、それによってアプリケーションに複雑さと待機時間が加わります。
  • 複数のマイクロサービス間のアトミック トランザクションは、通常は可能ではありません。 したがって、ビジネス要件は、マイクロサービス間の最終的な整合性を容認する必要があります。
  • 実稼働環境では、多数の独立したサービスからなるシステムの展開と管理には運用上の複雑さが伴います。
  • クライアントからマイクロサービスへの直接通信は、マイクロサービスのコントラクトをリファクターすることを困難にする場合があります。 たとえば、時間の経過に伴い、システムをサービスに分割する方法を変更することが必要になる場合があります。 1 つのサービスを 2 つ以上のサービスに分割することもあれば、2 つのサービスをマージすることもあります。 クライアントがマイクロサービスと直接的に通信している場合、このリファクタリングによって、クライアント アプリとの互換性が破綻する可能性があります。

コンテナー詰め

コンテナー化は、アプリケーションとそのバージョン管理された依存関係のセットに加えて、配置マニフェスト ファイルとして抽象化された環境構成をコンテナー イメージとしてまとめてパッケージ化し、1 つの単体としてテストしたうえで、ホスト オペレーティング システムに展開するソフトウェア開発のアプローチです。

コンテナーは、分離され、リソース制御された、ポータブル オペレーティング環境です。他のコンテナーやホストのリソースに触れることなくアプリケーションを実行できます。 したがって、コンテナーは、新しくインストールされた物理的なコンピューターまたは仮想マシンと同様に認識され、動作します。

次に示すように、コンテナーと仮想マシンの間には多くの類似点があります。

仮想マシンとコンテナーの比較。

コンテナーは物理または仮想マシンと同様に、オペレーティング システムを実行し、ファイル システムがあり、ネットワーク経由でアクセスできます。 ただし、コンテナーの背後にあるテクノロジおよび概念は仮想マシンとは大きく異なります。 仮想マシンには、アプリケーション、必要な依存関係、完全なゲスト オペレーティング システムが含まれます。 コンテナーの場合、アプリケーションとその依存関係が含まれますが、他のコンテナーとオペレーティング システムを共有し、ホスト オペレーティング システム上で分離されたプロセスとして実行されます (コンテナーごとに特殊な仮想マシン内で実行される Hyper-V コンテナーは除きます)。 したがって、コンテナーはリソースを共有し、通常は仮想マシンよりも必要なリソースが少なくなります。

コンテナー指向の開発と展開のアプローチの利点は、一貫性のない環境セットアップとそれらに伴う問題から発生する問題の大部分が排除されることです。 さらに、コンテナーでは、必要に応じて新しいコンテナーをインスタンス化することで、迅速なアプリケーション スケールアップの機能が可能になります。

コンテナーを作成および操作する際の主な概念は次のとおりです。

概念 説明
コンテナー ホスト コンテナーをホストするように構成された物理または仮想マシン。 1 つ以上のコンテナーがコンテナー ホストで実行されます。
コンテナー イメージ イメージは、相互に積み重ねて階層化したファイルシステムの和集合で構成され、コンテナーの基礎を形成します。 イメージには状態がなく、さまざまな環境に展開されても変更されることはありません。
コンテナー コンテナーは、イメージのランタイム インスタンスです。
コンテナー OS イメージ コンテナーはイメージから展開されます。 コンテナーのオペレーティング システム イメージは、コンテナーを構成する潜在的に多数のイメージ レイヤーのうち、最初のレイヤーです。 コンテナーのオペレーティング システムは不変であり、変更することはできません。
コンテナー リポジトリ コンテナー イメージが作成されるたびに、イメージとその依存関係がローカル リポジトリに格納されます。 これらのイメージは、コンテナー ホストで何度も再利用できます。 コンテナー イメージは、さまざまなコンテナー ホスト間で使用できるように、Docker Hub などのパブリックまたはプライベート レジストリにも格納できます。

企業の間では、マイクロサービス ベースのアプリケーションを実装する際にコンテナーを採用することが増えており、Docker は、ほとんどのソフトウェア プラットフォームとクラウド ベンダーに採用されている標準のコンテナー実装になっています。

次の図で示すように、eShop 参照アプリケーションは Docker を使って、コンテナー化された 4 つのバックエンド マイクロサービスをホストします。

eShop 参照アプリケーションのバックエンド マイクロサービス。

参照アプリケーションのバックエンド サービスのアーキテクチャは、マイクロサービスとコンテナーが連携する形の複数の自律的なサブシステムに分解されます。 各マイクロサービスには、ID サービス、カタログ サービス、注文サービス、バスケット サービスという 1 つの機能領域があります。

各マイクロサービスには独自のデータベースがあり、それによって、他のマイクロサービスから完全に切り離すことができます。 必要に応じて、アプリケーション レベルのイベントを使用して、異なるマイクロサービスのデータベース間の一貫性が実現されます。 詳細については、「マイクロサービス間の通信」を参照してください。

クライアントとマイクロサービスの間の通信

次に示すように、eShop マルチプラットフォーム アプリは、"クライアントからマイクロサービスへの直接" 通信を使って、コンテナー化されたバックエンド マイクロサービスと通信します。

クライアントからマイクロサービスへの直接通信。

クライアントからマイクロサービスへの直接通信では、マルチプラットフォーム アプリは、マイクロサービスごとに異なる TCP ポートを使用して、各マイクロサービスに対して直接、パブリック エンドポイントを介して要求を行います。 実稼働環境では、そのエンドポイントは通常、マイクロサービスのロード バランサーにマップされ、要求は使用可能なマイクロサービス インスタンス間に分散されます。

ヒント

API ゲートウェイ通信の使用を検討してください。

クライアントからマイクロサービスへの直接通信は、大規模で複雑なマイクロサービス ベースのアプリケーションを構築するときには欠点がありますが、小規模なアプリケーションでは十分すぎるほどです。 数十個のマイクロサービスを使用する大規模なマイクロサービス ベースのアプリケーションを設計する場合は、API ゲートウェイ通信の使用を検討してください。

マイクロサービス間の通信

マイクロサービス ベースのアプリケーションは分散システムであり、複数のマシン上で実行されている可能性があります。 通常、各サービス インスタンスはプロセスです。 そのため、サービスは、各サービスの性質に応じて、HTTP、TCP、Advanced Message Queuing Protocol (AMQP) などのプロセス間通信プロトコル、またはバイナリ プロトコルを使用して対話する必要があります。

マイクロサービス間通信の 2 つの一般的なアプローチは、データのクエリ時の HTTP ベースの REST 通信と、複数のマイクロサービス間で更新を通信するときの軽量な非同期メッセージングです。

非同期メッセージ ベースのイベントドリブン通信は、複数のマイクロサービス間で変更を反映するときに重要です。 このアプローチでは、ビジネス エンティティを更新した場合など、注目すべきことが発生したときにマイクロサービスによってイベントが発行されます。 その他のマイクロサービスは、これらのイベントにサブスクライブします。 その後、マイクロサービスはイベントを受信したときに独自のビジネス エンティティを更新し、それによってさらにイベントが発行される可能性があります。 この発行とサブスクライブ機能は、通常、イベント バスで実現されます。

イベント バスを使うと、次に示すように、コンポーネントが互いを明示的に認識する必要なく、マイクロサービス間の発行とサブスクライブ スタイルの通信が可能になります。

イベント バスでの発行とサブスクライブ。

アプリケーションの観点から見ると、イベント バスは、インターフェイスを介して公開される発行とサブスクライブのチャネルにすぎません。 ただし、イベント バスの実装方法は異なる場合があります。 たとえば、イベント バスの実装では、RabbitMQ、Azure Service Bus、または NServiceBus や MassTransit などの他のサービス バスを使用できます。 次の図は、eShop 参照アプリケーションでイベント バスがどのように使われるかを示したものです。

参照アプリケーションでの非同期イベントドリブン通信。

RabbitMQ を使って実装された eShop イベント バスは、1 対多の非同期パブリッシュ/サブスクライブ機能を提供します。 つまり、イベント発行後に複数のサブスクライバーが同じイベントをリッスンしている可能性があります。 この関係を次の図に示します。

一対多の通信

この一対多の通信アプローチでは、イベントを使用して複数のサービスにまたがるビジネス トランザクションを実装し、サービス間の最終的な一貫性を確保します。 最終的な整合性のあるトランザクションは、一連の分散ステップで構成されます。 そのため、ユーザー プロファイル マイクロサービスは UpdateUser コマンドを受信すると、データベース内のユーザーの詳細を更新し、UserUpdated イベントをイベント バスに発行します。 バスケット マイクロサービスと注文マイクロサービスの両方で、このイベントの受信をサブスクライブ済みであり、応答として、それぞれのデータベース内の購入者情報を更新します。

まとめ

マイクロサービスは、アプリケーションの開発と展開のアプローチを提供します。これは、最新のクラウド アプリケーションの機敏性、スケール、信頼性の要件に適したアプローチです。 マイクロサービスの主な利点の 1 つは、個別にスケールアウトできることです。つまり、需要の増加が発生していないアプリケーションの領域を不必要にスケーリングすることなく、需要をサポートするためにより多くの処理能力またはネットワーク帯域幅を必要とする特定の機能領域をスケールできます。

コンテナーは、分離され、リソース制御された、ポータブル オペレーティング環境です。他のコンテナーやホストのリソースに触れることなくアプリケーションを実行できます。 企業の間では、マイクロサービス ベースのアプリケーションを実装する際にコンテナーを採用することが増えており、Docker は、ほとんどのソフトウェア プラットフォームとクラウド ベンダーが採用している標準のコンテナー実装になっています。