2023 年 3 月の Microservices の登録が開始されました。 議題をご覧になり、こちらからお申し込みください。
編集者– この 7 部構成の記事シリーズはこれで完了です。
また、記事の完全なセットと、NGINX Plus を使用したマイクロサービスの実装に関する情報を電子書籍「マイクロサービス」としてダウンロードすることもできます。 設計から展開まで。
マイクロサービスソリューション ページもご覧ください。
マイクロサービスは現在、記事、ブログ、ソーシャル メディアでの議論、カンファレンスのプレゼンテーションなどを通じて、多くの注目を集めています。 彼らは、ガートナーのハイプサイクルにおける過大な期待のピークへと急速に向かっています。 同時に、ソフトウェア コミュニティの中には、マイクロサービスは新しいものではないと否定する懐疑論者もいます。 反対派は、このアイデアは単に SOA のブランド変更に過ぎないと主張しています。 しかし、誇大宣伝と懐疑論の両方にもかかわらず、マイクロサービス アーキテクチャ パターンには、特に複雑なエンタープライズ アプリケーションのアジャイル開発と配信を可能にするという点では大きな利点があります。
このブログ投稿は、マイクロサービスの設計、構築、およびデプロイに関する 7 部構成のシリーズの最初の投稿です。 このアプローチについて、またそれが従来のモノリシック アーキテクチャ パターンとどのように比較されるかについて学習します。 このシリーズでは、マイクロサービス アーキテクチャのさまざまな要素について説明します。 マイクロサービス アーキテクチャ パターンの利点と欠点、それがプロジェクトに適しているかどうか、そしてそれを適用する方法について学習します。
まず、マイクロサービスの使用を検討する必要がある理由を見てみましょう。
Uber や Hailo と競合することを目的とした、まったく新しいタクシー配車アプリケーションの構築を開始したと想像してください。 事前の会議と要件の収集が終わったら、手動で、または Rails、Spring Boot、Play、Maven に付属するジェネレーターを使用して、新しいプロジェクトを作成します。 この新しいアプリケーションは、次の図のようなモジュラー六角形アーキテクチャを持ちます。
アプリケーションの中核となるのはビジネス ロジックであり、これはサービス、ドメイン オブジェクト、およびイベントを定義するモジュールによって実装されます。 コアの周囲には、外部世界とインターフェースするアダプタがあります。 アダプタの例には、データベース アクセス コンポーネント、メッセージを生成および消費するメッセージング コンポーネント、API を公開するか UI を実装する Web コンポーネントなどがあります。
論理的にモジュール化されたアーキテクチャを備えているにもかかわらず、アプリケーションはモノリスとしてパッケージ化され、展開されます。 実際の形式は、アプリケーションの言語とフレームワークによって異なります。 たとえば、多くの Java アプリケーションは WAR ファイルとしてパッケージ化され、Tomcat や Jetty などのアプリケーション サーバーにデプロイされます。 その他の Java アプリケーションは、自己完結型の実行可能 JAR としてパッケージ化されます。 同様に、Rails および Node.js アプリケーションはディレクトリ階層としてパッケージ化されます。
このスタイルで書かれたアプリケーションは非常に一般的です。 当社の IDE やその他のツールは単一のアプリケーションの構築に重点を置いているため、開発は簡単です。 こうした種類のアプリケーションはテストも簡単です。 アプリケーションを起動し、Selenium を使用して UI をテストするだけで、エンドツーエンドのテストを実装できます。 モノリシック アプリケーションの展開も簡単です。 パッケージ化されたアプリケーションをサーバーにコピーするだけです。 ロードバランサーの背後で複数のコピーを実行することで、アプリケーションを拡張することもできます。 プロジェクトの初期段階ではうまく機能します。
残念ながら、この単純なアプローチには大きな制限があります。 成功したアプリケーションは、時間の経過とともに成長し、最終的には巨大になる傾向があります。 各スプリント中に、開発チームはさらにいくつかのストーリーを実装します。当然、これは多くのコード行を追加することを意味します。 数年後には、小さくてシンプルなアプリケーションが巨大なモノリスに成長しているでしょう。 極端な例を挙げると、私は最近、数百万行のコード (LOC) アプリケーション内の何千もの JAR 間の依存関係を分析するツールを作成している開発者と話をしました。 このような素晴らしいものを作るには、長年にわたる多数の開発者の共同の努力が必要だったに違いありません。
アプリケーションが大規模で複雑なモノリスになると、開発組織はおそらく大変な苦労を強いられることになります。 アジャイル開発と配信の試みはすべて失敗するでしょう。 大きな問題の一つは、アプリケーションが極めて複雑であることです。 一人の開発者が完全に理解するには規模が大きすぎます。 その結果、バグを修正したり、新しい機能を正しく実装したりすることが困難になり、時間がかかります。 さらに、これは下降スパイラルになる傾向があります。 コードベースが理解しにくいと、変更が正しく行われません。 最終的には、怪物のような、理解不能な大きな泥の塊ができあがります。
アプリケーションのサイズが大きいと、開発も遅くなります。 アプリケーションが大きくなるほど、起動時間は長くなります。 たとえば、最近の調査では、一部の開発者は起動時間が 12 分にも及ぶと報告しています。 アプリケーションの起動に40分もかかるという話も聞きました。 開発者がアプリケーション サーバーを定期的に再起動する必要がある場合、1 日の大部分が待機に費やされ、生産性が低下します。
大規模で複雑なモノリシック アプリケーションのもう 1 つの問題は、継続的なデプロイメントの障害となることです。 現在、SaaS アプリケーションの最先端技術は、1 日に何度も変更を本番環境にプッシュすることです。 複雑なモノリスでは、アプリケーションの一部を更新するにはアプリケーション全体を再デプロイする必要があるため、これを実行するのは非常に困難です。 先ほど述べた起動時間の長さも問題です。 また、変更の影響は通常十分に理解されていないため、広範囲にわたる手動テストを実行する必要がある可能性があります。 その結果、継続的なデプロイメントを実行することはほぼ不可能になります。
異なるモジュールのリソース要件が競合する場合、モノリシック アプリケーションの拡張も困難になる可能性があります。 たとえば、1 つのモジュールは CPU を集中的に使用する画像処理ロジックを実装し、理想的には Amazon EC2 Compute Optimized インスタンスにデプロイされます。 別のモジュールはインメモリ データベースであり、 EC2 メモリ最適化インスタンスに最適です。 ただし、これらのモジュールは一緒に展開されるため、ハードウェアの選択については妥協する必要があります。
モノリシック アプリケーションのもう 1 つの問題は信頼性です。 すべてのモジュールが同じプロセス内で実行されるため、メモリ リークなどのいずれかのモジュールのバグによって、プロセス全体が停止する可能性があります。 さらに、アプリケーションのすべてのインスタンスは同一であるため、そのバグはアプリケーション全体の可用性に影響を及ぼします。
最後に、モノリシック アプリケーションでは、新しいフレームワークや言語を導入することが非常に困難になります。 たとえば、XYZ フレームワークを使用して記述された 200 万行のコードがあるとします。 新しい ABC フレームワークがかなり優れているとしても、アプリケーション全体を書き直して新しい ABC フレームワークを使用するのは、非常に高価 (時間とコストの両方) になります。 その結果、新しいテクノロジーを導入する上で大きな障壁が生じます。 プロジェクトの開始時に選択したテクノロジーに縛られてしまいます。
要約すると、ビジネスクリティカルなアプリケーションは成功を収めていますが、それが巨大なモノリスに成長し、開発者のほとんど、あるいはまったく理解できなくなっています。 時代遅れで非生産的なテクノロジーを使用して作成されているため、優秀な開発者を雇うことが困難です。 アプリケーションは拡張が難しく、信頼性が低いです。 その結果、アプリケーションのアジャイル開発と配信は不可能になります。
それで、あなたは何ができるでしょうか?
Amazon、eBay、 Netflixなどの多くの組織は、現在マイクロサービス アーキテクチャ パターンとして知られているものを採用することでこの問題を解決しました。 巨大なモノリシック アプリケーションを 1 つ構築するのではなく、アプリケーションを相互接続された小さなサービスのセットに分割するという考え方です。
サービスは通常、注文管理、顧客管理などの一連の個別の機能を実装します。 各マイクロサービスは、ビジネス ロジックとさまざまなアダプターで構成される独自の六角形アーキテクチャを持つミニ アプリケーションです。 一部のマイクロサービスは、他のマイクロサービスまたはアプリケーションのクライアントによって使用される API を公開します。 その他のマイクロサービスでは、Web UI を実装する場合があります。実行時には、各インスタンスはクラウド VM または Docker コンテナであることが多いです。
たとえば、前述のシステムの可能な分解を次の図に示します。
アプリケーションの各機能領域は、独自のマイクロサービスによって実装されるようになりました。 さらに、Web アプリケーションは、より単純な Web アプリケーションのセット (タクシー配車の例では、乗客用と運転手用など) に分割されます。 これにより、特定のユーザー、デバイス、または特殊なユースケースに対して異なるエクスペリエンスを簡単に展開できるようになります。
各バックエンド サービスは REST API を公開し、ほとんどのサービスは他のサービスによって提供される API を使用します。 たとえば、ドライバー管理では、通知サーバーを使用して、利用可能なドライバーに潜在的な旅行について通知します。 UI サービスは、Web ページをレンダリングするために他のサービスを呼び出します。 サービスでは、非同期のメッセージベースの通信も使用される場合があります。 サービス間通信については、このシリーズの後半で詳しく説明します。
一部の REST API は、ドライバーや乗客が使用するモバイル アプリにも公開されます。 ただし、アプリはバックエンド サービスに直接アクセスすることはできません。 代わりに、通信はAPI ゲートウェイと呼ばれる仲介者によって仲介されます。 API ゲートウェイは、負荷分散、キャッシュ、アクセス制御、API メータリング、監視などのタスクを担当し、 NGINX を使用して効果的に実装できます。シリーズの後の記事では、 API ゲートウェイについて説明します。
マイクロサービス アーキテクチャ パターンは、優れた書籍『The Art of Scalability』に掲載されているスケーラビリティの 3D モデルであるScale Cubeの Y 軸スケーリングに対応しています。 他の 2 つのスケーリング軸は、ロード バランサーの背後でアプリケーションの複数の同一コピーを実行する X 軸スケーリングと、リクエストの属性 (行の主キーや顧客の ID など) を使用してリクエストを特定のサーバーにルーティングする Z 軸スケーリング (またはデータ パーティション分割) です。
アプリケーションでは通常、3 種類のスケーリングを組み合わせて使用します。 Y 軸のスケーリングにより、このセクションの最初の図に示すように、アプリケーションがマイクロサービスに分解されます。 実行時に、X 軸スケーリングは、スループットと可用性を確保するために、ロード バランサーの背後で各サービスの複数のインスタンスを実行します。 一部のアプリケーションでは、サービスを分割するために Z 軸スケーリングを使用する場合もあります。 次の図は、Amazon EC2 上で実行される Docker を使用して Trip Management サービスを展開する方法を示しています。
実行時には、旅行管理サービスは複数のサービス インスタンスで構成されます。 各サービス インスタンスは Docker コンテナです。 高可用性を実現するために、コンテナは複数のクラウド VM 上で実行されます。サービス インスタンスの前には、インスタンス間でリクエストを分散するNGINX などのロード バランサがあります。 ロード バランサは、キャッシュ、アクセス制御、 API 計測、監視などの他の問題も処理する場合があります。
マイクロサービス アーキテクチャ パターンは、アプリケーションとデータベースの関係に大きな影響を与えます。 単一のデータベース スキーマを他のサービスと共有するのではなく、各サービスには独自のデータベース スキーマがあります。 一方で、このアプローチは、企業全体のデータ モデルという考え方と矛盾しています。 また、一部のデータが重複してしまうこともよくあります。 ただし、マイクロサービスからメリットを得たい場合、疎結合が保証されるため、サービスごとにデータベース スキーマを用意することが不可欠です。 次の図は、サンプル アプリケーションのデータベース アーキテクチャを示しています。
各サービスには独自のデータベースがあります。 さらに、サービスでは、そのニーズに最適なタイプのデータベース、いわゆるポリグロット永続性アーキテクチャを使用できます。 たとえば、潜在的な乗客の近くにいるドライバーを見つけるドライバー管理では、効率的な地理クエリをサポートするデータベースを使用する必要があります。
表面的には、マイクロサービス アーキテクチャ パターンは SOA に似ています。 どちらのアプローチでも、アーキテクチャは一連のサービスで構成されます。 ただし、マイクロサービス アーキテクチャ パターンについて考える 1 つの方法は、 Web サービス仕様(WS-*) とエンタープライズ サービス バス (ESB) の商用化と認識されている負担のない SOA であると考えることです。 マイクロサービス ベースのアプリケーションでは、WS-* ではなく、REST などのよりシンプルで軽量なプロトコルが好まれます。 また、ESB の使用をできるだけ避け、代わりにマイクロサービス自体に ESB のような機能を実装します。 マイクロサービス アーキテクチャ パターンは、標準スキーマの概念など、SOA の他の部分も拒否します。
マイクロサービス アーキテクチャ パターンには、いくつかの重要な利点があります。 まず、複雑さの問題に取り組みます。 巨大なモノリシック アプリケーションを一連のサービスに分解します。 機能の総量は変更されていませんが、アプリケーションは管理しやすいチャンクまたはサービスに分割されています。 各サービスには、RPC またはメッセージ駆動型 API の形式で明確に定義された境界があります。マイクロサービス アーキテクチャ パターンは、モノリシック コード ベースでは実際には実現が非常に難しいレベルのモジュール性を適用します。 その結果、個々のサービスの開発速度が大幅に向上し、理解と保守も容易になります。
2 番目に、このアーキテクチャにより、各サービスは、そのサービスに重点を置いたチームによって独立して開発できるようになります。 サービスが API 契約を遵守している限り、開発者は適切なテクノロジを自由に選択できます。 もちろん、ほとんどの組織は完全な無政府状態を避け、テクノロジーの選択肢を制限したいと考えています。 しかし、この自由は、開発者が新しいプロジェクトの開始時に存在していた、おそらく時代遅れの技術を使用する義務がなくなることを意味します。 新しいサービスを作成する場合、現在のテクノロジーを使用するオプションがあります。 さらに、サービスが比較的小さいため、現在のテクノロジーを使用して古いサービスを書き換えることも可能になります。
3 番目に、マイクロサービス アーキテクチャ パターンにより、各マイクロサービスを個別にデプロイできるようになります。 開発者は、サービスにローカルな変更の展開を調整する必要がありません。 このような変更は、テストが完了するとすぐに展開できます。 たとえば、UI チームは A/B テストを実行し、UI の変更を迅速に反復できます。 マイクロサービス アーキテクチャ パターンにより、継続的なデプロイメントが可能になります。
最後に、マイクロサービス アーキテクチャ パターンにより、各サービスを個別に拡張できるようになります。 各サービスの容量と可用性の制約を満たすインスタンスの数だけをデプロイできます。 さらに、サービスのリソース要件に最適なハードウェアを使用できます。 たとえば、CPU を集中的に使用する画像処理サービスを EC2 コンピューティング最適化インスタンスにデプロイし、インメモリデータベースサービスを EC2 メモリ最適化インスタンスにデプロイできます。
フレッド・ブルックスが約30年前に書いたように、特効薬は存在しない。 他のテクノロジーと同様に、マイクロサービス アーキテクチャにも欠点があります。 一つの欠点は名前そのものです。 マイクロサービスという用語は、サービスのサイズを過度に重視します。 実際、10~100 LOC の非常に細かい粒度のサービスを構築することを提唱する開発者もいます。 小規模なサービスが望ましいですが、それは目的を達成するための手段であり、主な目標ではないことを覚えておくことが重要です。 マイクロサービスの目標は、アジャイルなアプリケーション開発と展開を容易にするために、アプリケーションを十分に分解することです。
マイクロサービスのもう 1 つの大きな欠点は、マイクロサービス アプリケーションが分散システムであるという事実から生じる複雑さです。 開発者は、メッセージングまたは RPC に基づくプロセス間通信メカニズムを選択して実装する必要があります。さらに、リクエストの送信先が遅いか利用できない可能性があるため、部分的な障害を処理するコードも記述する必要があります。 これらはどれもロケット科学ではありませんが、モジュールが言語レベルのメソッド/プロシージャ呼び出しを介して相互に呼び出すモノリシック アプリケーションよりもはるかに複雑です。
マイクロサービスにおけるもう 1 つの課題は、パーティション化されたデータベース アーキテクチャです。 複数のビジネス エンティティを更新するビジネス トランザクションは、かなり一般的です。 モノリシック アプリケーションではデータベースが 1 つしかないため、このような種類のトランザクションを実装するのは簡単です。 ただし、マイクロサービス ベースのアプリケーションでは、異なるサービスが所有する複数のデータベースを更新する必要があります。 分散トランザクションの使用は通常は選択肢にありませんが、それはCAP 定理だけが理由ではありません。 これらは、今日の高度にスケーラブルな NoSQL データベースやメッセージング ブローカーの多くではサポートされていません。 最終的には、最終的な一貫性に基づくアプローチを使用する必要があり、これは開発者にとってより困難になります。
マイクロサービス アプリケーションのテストもはるかに複雑です。 たとえば、Spring Boot などの最新のフレームワークを使用すると、モノリシック Web アプリケーションを起動してその REST API をテストするテスト クラスを簡単に作成できます。一方、サービス用の同様のテスト クラスでは、そのサービスとそれが依存するすべてのサービスを起動する必要があります (または少なくともそれらのサービスのスタブを構成する必要があります)。 もう一度言いますが、これはロケット科学ではありませんが、これを実行する複雑さを過小評価しないことが重要です。
マイクロサービス アーキテクチャ パターンのもう 1 つの大きな課題は、複数のサービスにまたがる変更を実装することです。 たとえば、サービス A、B、C への変更を必要とするストーリーを実装しているとします。ここで、A は B に依存し、B は C に依存しています。モノリシック アプリケーションでは、対応するモジュールを変更し、変更を統合して、一度にデプロイするだけです。 対照的に、マイクロサービス アーキテクチャ パターンでは、各サービスへの変更のロールアウトを慎重に計画し、調整する必要があります。 たとえば、サービス C を更新し、次にサービス B を更新し、最後にサービス A を更新する必要があります。幸いなことに、ほとんどの変更は通常 1 つのサービスにのみ影響し、調整を必要とする複数のサービスの変更は比較的まれです。
マイクロサービス ベースのアプリケーションのデプロイもはるかに複雑です。 モノリシック アプリケーションは、従来のロード バランサーの背後にある同一のサーバー セットに単純にデプロイされます。 各アプリケーション インスタンスは、データベースやメッセージ ブローカーなどのインフラストラクチャ サービスの場所 (ホストとポート) で構成されます。 対照的に、マイクロサービス アプリケーションは通常、多数のサービスで構成されます。 たとえば、エイドリアン・コッククロフト氏によると、Hailoには160種類のサービスがあり、Netflixには600種類以上のサービスがあるそうです[編集者注:HailoはMyTaxiに買収されました] 。 各サービスには複数のランタイム インスタンスがあります。 構成、展開、拡張、監視が必要な可動部分がさらに多くなります。 さらに、サービスが通信する必要がある他のサービスの場所 (ホストとポート) を検出できるようにするサービス検出メカニズム (後の記事で説明) を実装する必要もあります。 従来のトラブル チケット ベースおよび手動の運用アプローチでは、このレベルの複雑さに対応できません。 したがって、マイクロサービス アプリケーションを正常にデプロイするには、開発者によるデプロイ方法のより高度な制御と、高度な自動化が必要になります。
自動化への 1 つのアプローチは、 Cloud Foundryなどの既製の PaaS を使用することです。 PaaS は、開発者にマイクロサービスのデプロイと管理の簡単な方法を提供します。 これにより、IT リソースの調達や構成などの懸念から解放されます。 同時に、PaaS を構成するシステムおよびネットワークの専門家は、ベスト プラクティスと企業ポリシーへの準拠を確保できます。 マイクロサービスのデプロイメントを自動化するもう 1 つの方法は、本質的に独自の PaaS を開発することです。 典型的な出発点の 1 つは、 Kubernetesなどのクラスタリング ソリューションを Docker などのテクノロジーと組み合わせて使用することです。 このシリーズの後半では、マイクロサービス レベルでのキャッシュ、アクセス制御、API 計測、監視を簡単に処理できる NGINX Plus などのソフトウェア ベースのアプリケーション配信アプローチが、この問題の解決にどのように役立つかについて説明します。
複雑なアプリケーションを構築するのは本質的に困難です。 モノリシック アーキテクチャは、シンプルで軽量なアプリケーションにのみ適しています。 複雑なアプリケーションに使用すると、大変な苦労をすることになります。 マイクロサービス アーキテクチャ パターンは、欠点や実装上の課題があるにもかかわらず、複雑で進化するアプリケーションにとってより適切な選択肢です。
今後のブログ投稿では、マイクロサービス アーキテクチャ パターンのさまざまな側面の詳細について説明し、サービス検出、サービス展開オプション、モノリシック アプリケーションをサービスにリファクタリングする戦略などのトピックについて説明します。
乞うご期待…
編集者– この 7 部構成の記事シリーズはこれで完了です。
また、記事の完全なセットと、NGINX Plus を使用したマイクロサービスの実装に関する情報を電子書籍「マイクロサービス」としてダウンロードすることもできます。 設計から展開まで。
ゲストブロガーの Chris Richardson 氏は、Amazon EC2 向けの初期の Java PaaS (Platform as a Service) であるオリジナルのCloudFoundry.comの創設者です。 彼は現在、アプリケーションの開発および展開方法の改善について組織にコンサルティングを行っています。
「このブログ投稿には、入手できなくなった製品やサポートされなくなった製品が参照されている場合があります。 利用可能な F5 NGINX 製品およびソリューションに関する最新情報については、 NGINX 製品ファミリーをご覧ください。 NGINX は現在 F5 の一部です。 以前の NGINX.com リンクはすべて、F5.com の同様の NGINX コンテンツにリダイレクトされます。"