BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル Microservices — 字義と精神

Microservices — 字義と精神

キーポイント

  • Microservices pattern doesn’t refer to the size of the services, decomposing your solution into ‘micro’ pieces is not the goal of the pattern, think of your solution as one whole then look at the requirements to guide you through what pieces to partition out. The article gives you an example of doing that. 
  • Central to the microservices pattern is the concept that services must be decoupled; the end goal of the pattern is to make a distributed solution easy to develop, operate and maintain which can be easier achieved when the services are decoupled.
  • Decoupling the services is twofold 1) Services don’t interact directly with each other, instead they use an integration service. 2) Services are versioned. 
  • Using an integration service (e.g. service bus) frees your microservices from depending on each other, a failure of one service shouldn’t cause the other services to fail.
  • Versioning shouldn’t be an afterthought in microservices, implement it at day one. If Versioning seems an overkill you’re probably prematurely ‘microservicing’ your solution.

原文(投稿日:2021/11/23)へのリンク

[この記事は個人的な意見であり、会社を代表するものではありません]

マイクロサービスパターンは強力で人気があります。最新の分散ソリューションの多くの課題を解決するため、人気が高まり続けるでしょう。一部の開発者は、マイクロサービスが約束するメリットを得るかわりに、パターンが投げかけるものに修正する絶え間ないループに陥ってしまい、マイクロサービスが非常に複雑になることに気付きます。いくつかのマイクロサービスプロジェクトに参加し、失敗したものもありましたが、「マイクロサービスの実装の良し悪しの特徴」(この記事の元のタイトル) という実践的なレッスンについて書くことは、コミュニティにとって役立つかもしれないと思いました。それぞれのプロジェクトを振り返って、失敗/成功の原因となった要因を抽象化できるかどうかを確認して、興味深い事実を見つけました。マイクロサービスを「スモールサービス」のパターンであると理解したチームは、複雑で保守が不可能なソリューションになりました。マイクロサービスが (パターンの「スモール」と「分離」の違いと開発者がこれらの目的と一致する特定の設計上の決定を行わなければならないことの理解の下) 「分離サービス」のパターンであると理解したチームは、それを最大限に活用することができました。そのことを念頭に置いて、実装の良し悪しの根本原因について説明する方がメリットがあることがわかりました。「スモールサービス」と「分離サービス」、またはここで「字義 (Letter)」と「精神 (Spirit)」と呼んでいるものです。その過程で、パターンに関するいくつかの重要な概念についても説明します。

字義 (Letter): マイクロサービスは小さく (small) しなければならない

Wikipedia によると「micro」の「接頭辞はギリシャ語の μικρ?ς (mikrós) に由来し、「小さい」を意味する」とあります。この「マイクロサービス」という言葉の不幸な選択は、多くの開発者がソリューションを「小さな」断片に切り取ることをパターンの最終目標にしたため、パターンについて非常に混乱を招きましたが、実際にはそれはパターンの目標を達成するための手段にすぎません。

以前クライアントの git リポジトリをクローンしたとき、この誤解を直接目にしました。すべてのメソッド (GetName、GetEmail など) が個別のクラス (C#) にあり、すべてのクラスが個別のプロジェクトにあることがわかりました。すべてのプロジェクトを表示するには、ソリューションエクスプローラー (Visual Studio) でマウスホイールを使用してスクロールする必要がある、文字通り数十のプロジェクトでした。私は後で、マイクロサービスを実行する正しい方法であるという考えの下でこれを行ったことを知りました。

これは一見衝撃的かもしれませんが、オンラインの多くの定義にマイクロサービスの一部として「小さな」が含まれていることを知っていれば、当然のことです。結局のところ、マイクロサービスをスモールサービスを意味すると理解すると、「それをどれくらい小さくすべきか?」という固有の疑問に直面し、ソリューション/コードを、マイクロサービスのメリットを逃さないように小さくすることによって、明確な答えの必要性を抑えたくなるかもしれません。

これは極端な例ですが、他の多くのプロジェクトでは、より微妙なニュアンスがありますが、このアンチパターンが存在することは事実です、これを正しましょう。接頭辞「micro」は「決して小さい」を示しません。彼らは当初、全体の「一部」を意味する「モノリス」のコンテキストでそれを (いずれかの部分がその一部である全体よりも「小さい」と仮定して) マイクロ/スモールと呼んでいたのかもしれません、あるいは、単一の機能 (「単一の」機能は必ずしも「小さな」機能を意味しません) のモノリス、または「マイクロ」と呼ばれる理由について考えることができる他の理由に基づいて分割したものをおそらくマイクロと呼んだのです。これらの説明はすべてパターンに不可欠なものは何も捉えていないと思います。パターンの名前を変更することはできませんが、定義の一部として「小さい」の使用をやめることができます。パターンをより単純で簡単に実践するための最初のステップとして、パターンの定義と目的の実装を調整する必要があります。

マイクロサービスがスモールサービスではないと言っているという事実は、マイクロサービスがビッグサービスであるべきだという意味ではないことに注意してください。私が言っているのは、サイズはここでは無関係であり、実装を間違った方向に歪ませているパターンの間違った側面を捉えているということです。

マイクロサービスがスモールサービスではないとすると、それは何ですか?

Wikipedia には、マイクロサービスの適切な定義があります。「アプリケーションを疎結合サービスのコレクションとして配置する構造スタイルです

パターンの考え方は単純です。ソリューションのさまざまな部分でさまざまなニーズがある可能性があります。その場合、これらの部分を分離して、他の部分に影響を与えることなく、それぞれの部分のニーズに個別に対応します。これらのパーツを分離すると、他のパーツとの通信方法が標準化され、内部実装が抽象化されるため、これらのパーツは相互運用可能で再利用可能になります。

例を見てみましょう

図A は、モノリシックソリューションを示しています。このソリューションのすべてのサービス (歯車のアイコンで表されています) は緊密に連携しています。このモノリスをベースラインとして、マイクロサービスが必要な理由と時期を理解しましょう。

注: 私はこれらの歯車に、さまざまなサービスタイプ/機能を示すためにさまざまな形状/色を付けたくなりましたが、モノリスはさまざまなサービスタイプをミックスしたものである必要はないことを強調したいので、すべて同じに保つことにしました。マイクロサービスパターンを利用すると、モノリスはこれらのサービスの1つまたはサブセットに他のサービスとは異なる特定のニーズがある場合 (たとえば、異なる復元力またはスケール目標) に完全に1つのサービスタイプ (たとえば、すべてのバッチジョブ) で構成されます。これは、パターンについて理解するための微妙ですが重要なことです。

例に戻って、このソリューションの歯車の1つに大きな負荷がかかっていて、それをスケーリングする必要があるとしましょう。図A から歯車のいずれかを選択して、「GearX」と呼びましょう。GearX はモノリスの一部であるため、GearX をスケーリングする要件を満たすためには、モノリス全体をスケーリングする必要があります。

任務完了! 図B は GearX のスケーリングに成功し、私たちのソリューションはより大きな/より多くのワークロードを処理できるようになりました。ただし、スケーリング要件を満たすために作業しているときに、誤った問題が発生したことに注意してください (実際には大きな問題です!)。図B からわかるように、モノリスで GearX をスケーリングすることの副作用は、すべての歯車もまたスケールされリソースの膨大な浪費になります! オンプレミスの時代には、データセンタに資本コストを投資したため、これは大きな問題ではなかったかもしれません。そのため、通常、アプリをスケーリングするときに余分なお金を払うことはありませんでした。ただし、クラウドの世界では、従量コストモデルでは、GearX をスケーリングすることだけを目的とした場合、これらすべての歯車をスケーリングして使用量に対して支払う余裕はありません。ようこそマイクロサービスへ、これで、パターンを実装するための最初の有効なユースケースができました。

この問題を解決するために、GearX を分離して、他の歯車から独立してスケーリングできるようにしましょう – 図C:

リソースを無駄にすることなく GearX をスケールアップ/スケールアウトできるようになり、ソリューション全体を実行するための最適なコストを維持しながら、初期のスケーリング要件を満たすことができるようになりました。–

注: 資本コストと従量コストモデルのスケーリングは、マイクロサービスアーキテクチャがクラウドよりもずっと前からあり、クラウドだけで有効なアーキテクチャではないにもかかわらず、クラウドに関連付けられたマイクロサービスがよく見られる根本的な理由です。

これがどこに向かっているのか理解できると思います。スケール/コストを最適化するために歯車の1つを分割できる場合、これと同じ方法論を利用して他の目標が達成できないと思いますか? たとえば、モノリスの他の歯車は、世界の異なる地域で活動する別のチームによって処理される広告メーカであり、私たちのモノリスのリリーススケジュールに依存するのではなく、毎日の広告キャンペーンを展開するために頻繁にリリースするスケジュールがあります。私たちはサービスを分割し、広告チームに完全に制御させることができます。彼らはどの言語/DB/リリーススケジュールを使用することもできます。サービスは分離/独立するようになったため、気にしません。

通信方法を標準化して、これらのサービスを分離し、(可能な時に) ステートレスにし、ダウンタイムがない/最小限に抑えた拡張と置換を容易にすることを目指す必要があります。

要約すると、ソリューションを事前に小さく分割しないでください。ソリューションの保守が困難になる可能性が高くなります。プロセスはまったく逆です。ソリューションを1つ (モノリス) として考え、要件を確認して、要件を満たすために、モノリスの一部を分割できます。ソリューションの一部を分割することで解決できるという漠然とした要件がある場合は、尚早に分割するのではなく、グッドプラクティス (SOLID / 12ファクター ... など) に従ってソリューションを作成することをお勧めします。要件が具体的になった場合に、将来このパーティションを簡単に抽出できるようにします。図E は絶対に避けてください!

精神 (Spirit): マイクロサービスは分離しなければならない

Wikipedia のマイクロサービスの定義に戻ります。「アプリケーションを疎結合サービスのコレクションとして配置する構造スタイルです」– これがパターンの精神であるため、「疎結合」部分を解きましょう。サービスを疎結合にすることに寄与する (各サービス専用のストレージ、専用のソースリポジトリなど) 多くのことがありますが、私たちがしっかりとした基盤の上に立っていることを確認し、2つの主要なものに言及したいと思います (つまり、2つの原則なしでは実行できず、追加することはできる) :

  1. サービス間の相互作用をイベント駆動にする
  2. サービスをバージョニングする

原則 1: サービス間の相互作用をイベント駆動にする

簡単に言えば、ServiceA への呼び出しが ServiceB と C および D への一連の呼び出しをトリガし、ServiceA がレスポンスを返すためにそれらすべてが成功する必要がある場合、マイクロサービスは正しい方法で実装されていません。

理想的には、サービスは互いには直接相互作用しません。代わりに、統合サービスを使用して相互に通信します。これは通常、サービスバスで実現されます。ここでの目標は、各サービスを他のサービスから独立させて、各サービスがジョブを開始するために必要なすべてのものを持ち、このジョブの完了後に何が起こるかを気にしないようにすることです。サービスが別のサービスを直接呼び出すという例外的なケースでは、2番目のサービスが失敗した場合の状況を処理しなければなりません。

これがモノリスを典型的なマイクロサービスソリューションに変えたものです:

原則 2: サービスをバージョニングする

簡単に言えば、ServiceA チームが ServiceA で何かを変更できるようにするために ServiceB チームとのミーティングが必要な場合、マイクロサービスは正しい方法で実装されていません。

マイクロサービスは興味深い課題を提示します – 一方では、サービスを分離する必要があり、他方では、ソリューションが正常に機能するためにはすべてが正常である必要があるため、ソリューションを壊すことなく適切に進化する必要があります。簡単な例を見てみましょう:

ServiceA は、username フィールドを文字列として持つ json メッセージを生成するサービスであり、このメッセージをキューに入れます。ServiceB は、文字列 username フィールドを持つ json メッセージでユーザを検索し、何らかの処理を行うことを期待するサービスです。ここで、ServiceA が username とその email address を配列にして json メッセージに含める要件があると想定します。ServiceB は、username フィールドが文字列であることを予定しているため、この json ペイロードの処理に失敗します。ServiceA のチームは、迅速に変更に対応する必要がありますが、同時に、このようなバグを導入しないように注意する必要があります。

これらのサービスに取り組んでいるチームを調整し、すべてのサービス (ServiceA と ServiceB の両方が変更を完了したとき) に対して1つのリリースを作成することで解決したくなるかもしれませんが、これは保守できないことがすぐにわかります。これは本当に有名なバグの多い家庭料理のレシピです。さらに重要なことに、このアプローチを採用する場合は、基本的な前提を再検討し、そもそもこれら2つのサービスを分離することのメリットは何であったかを自問する価値があります。– サービス疎結合にできない場合は、それらを一緒に持ち帰ることが正しいことかもしれません。

この課題を解決するための最善の策は、これらのサービスを独立して開発し続けることで、変更が発生したときにそれを認識できるようにバージョン管理することです。(したがって、すべてのサービスがレガシーバージョンの使用を停止するまで、下位互換性を維持します)。この簡単な例では、ServiceA は username を文字列 (バージョン 1) として使用してペイロードを生成し続けますが、username を配列として使用するバージョン 2 を追加します。ServiceB はバージョン番号をチェックし、バージョン 2 に対応するコードの開発に取り組んでいる間、処理できるメッセージ (バージョン 1) を処理します。

バージョニングには複数の方法がありますが、どのような慣習でもかまいません。私はほとんどの開発者に広く理解されている3桁のセマンティックバージョニング 0.0.0 を好んでいます。3桁のどの桁が更新されたかを確認するだけで、サービスがどのような種類の変更を行ったかを簡単に判断できます。より包括的なバージョン管理戦略が必要な場合は、Microsoft が NuGet パッケージをどのように行っているかを確認することもできます。

Day 1 でのバージョン管理は、マイクロサービスの時期尚早な最適化でしょうか?

これは私たち自身に尋ねる正常な質問です。私は「Day 1」に追加するものに懐疑的です。ここでの場合も例外ではありませんが、Day 1 にバージョン管理を追加することをお勧めしますが、Day 1 にマイクロサービスを実装することはそうすることのメリットが明らかになるまでお勧めしません。ただし、私たちは、マイクロサービスが適切な設計であると判断したら、一連の既知の分散システムの課題に自動的にサインアップしました。ソリューションをさまざまなサービスに分割することは、必然的にこれらのサービスの進化が異なることを意味します。バージョン管理を導入することで、この課題を解決しています。さらに、開発フェーズの後半でバージョン管理を導入することは困難です。Day 1 に導入すると、多くの労力を節約できます。

結論

マイクロサービスを「スモールサービス」として定義することは完全に間違っているわけではありませんが、パターンの間違った側面を捉え、誤った実装につながります。「サービスの分離」は、パターンの精神であり、中心的な考え方です。2つの重要な分離原則があります。1) サービスは互いに直接通信してはいけません。2) サービスは互いに独立して成長できるようにバージョン管理する必要があります。

著者について

Alaa Tadmori 氏 は、Microsoft のクラウドソリューションアーキテクトです。彼はクラウドコンピューティングモデルのアーリーアダプタであり、熱狂的ファンです。Alaa は、責任ある IT を信じています。今日構築されるソリューションは、世界を形作り、子供たちの世界になるため、私たちには世界をより良い場所にするための責任と選択があります。仕事をしていないとき、Alaa は家族と一緒に時間を過ごし、ノンフィクションの本を読むのが好きです。

 

作者について

この記事に星をつける

おすすめ度
スタイル

BT