BT

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

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル マイクロサービスの依存関係管理における落とし穴とパターン

マイクロサービスの依存関係管理における落とし穴とパターン

キーポイント

  • When managing dependencies for distributed microservices, you must consider different types of growth when evolving the product, such as the number of users, user behavior, and the interactions between services and subsystems.
  • Stateless services are often easier to manage than stateful ones.
  • Colocate service components for greater performance, easier failure isolation, and SLO alignment.
  • Having isolated serving stacks may prevent a global outage of your service. This includes the dependencies of your service run by 3rd parties or in the Cloud.
  • Some architectural strategies may allow your service to have a graceful degradation during an outage instead of immediately returning errors to the user, e.g., having a cache between the API and the database.
  • When building an SLO, take into account the current SLO of all backends and the different user journeys, including edge cases for bad responses and degraded experiences.
  • Work with backend owners and allow extra time for resource allocation and architectural changes.

原文(投稿日:2021/08/31)へのリンク

昨年のQCon Plusで、私はGoogleで10年以上働いている間に遭遇したマイクロサービス依存関係管理の落とし穴とパターンをいくつか共有しました。この講演は、特定の製品やチームに焦点を当てたものではなく、Googleのソフトウェアエンジニアとしての私自身の経験と個人的な学びの共有でした。

それを念頭に置いて、私は発表しました。各シナリオには、重要なひらめきの瞬間があり、マイクロサービス環境内のさまざまな側面の重要性に気づきました。物事が失敗したり、うまくいかなかったりする瞬間もありました。私はこれらの厄介な問題を選び出して、将来起こり得る同じような状況を回避するために私が何をしたかを伝えるようにしました。そうすれば、皆さんも自身の環境内で同様の潜在的な障害につながる兆候を探ることができます。

これらのシナリオはすべて、私がエンジニアリングにおいて、他の役割の人々と一緒に働いていたときに実際に起こりました。ある時、私はソフトウェアエンジニアで、プロジェクトマネージャと共に働いていました。また、ある時には、私はサイト信頼性エンジニアで、他の開発者と協力していました。そして最後のシナリオでは、信頼できるサービスを構築することを目標に、基本的にチームの全員と仕事に取り組んでいました。

この各シナリオは、チームにおけるさまざまな役割に役立ちます。信頼性の高いマイクロサービス環境の構築の成功(または失敗)は、1人または1つの役割だけに依存するわけではありません。

システムを変更するたびに、変更は製品全体の多くの他の部品やコンポーネントに影響を与えます。これらのコンポーネントは、会社、クラウド、あるいはサードパーティプロバイダによって実行されます。システムの変更は、顧客、つまり常に意識したい人にまで及ぶ波及効果を生み出します。そのため、システムを全体論的な観点から見る必要があります。これは、私が講演中に伝えようとしたことの一部でもあります。

これらのシナリオ(そして、それらから学べるすべて)に入る前に、業界がサービスモノリスからマイクロサービスにどのように移行したかを簡単に見ましょう。そのあと、最後にもう一度ジャンプして、クラウドで実行されているサービスを見てみましょう。また、パターンとトラフィック増加、障害の切り分けについても見ていきます。そして、すべてのバックエンドに異なる要求がある状況で、どのように合理的なサービスレベル目標(SLO)を計画するかについても見ていきます。

モノリス、マイクロサービス、そしてクラウドへ

私たちの旅(そして、ここでは一般的なサービスを使用します)は単一のバイナリから始まります。最終的に(そして急速に)進化して、データベース、ユーザ認証、フロー制御、運用監視、HTTP APIなどのより複雑な機能が含まれるようになりました(そのため、顧客は私たちをオンラインで見つけることができます)。当初、この単一のバイナリは単一のマシンで実行することを前提としていました。しかし、ビジネスが成長するにつれて、このバイナリを複数の地理的場所に複製する必要が出てきました。また、トラフィック増加のために追加の余地を残すようになりました。

モノリスを複製するようになってから間もなく、いくつかの理由により、モノリスを別々のバイナリに分離する必要が出てきました。おそらく、皆さんは理由をいくつかご存じでしょう。共通の理由の1つは、バイナリの複雑さに関連します。より複雑な機能を追加すると、コードベースを維持することはほぼ不可能になりました(他の新機能の追加は言うまでもありません)。モノリスを個別のバイナリに分割するもう1つの共通の理由は、独立した論理コンポーネントの個々の要件に関連しています。たとえば、他のコンポーネントのパフォーマンスに影響を与えることなく、特定のコンポーネントのハードウェアリソースを増やす必要がある場合があります。

このようなシナリオは、最終的にマイクロサービスの誕生を動機付けることになりました。これは、緩く結合されたサービスのコレクションで、独立して展開可能で、高度に保守可能で、複雑なアプリケーションを形成(または提供)するように編成されます。実際には、これは、複数のバイナリがネットワークを介してデプロイされ、通信することを意味します。これらのバイナリはそれぞれ異なるマイクロサービスを実装していますが、それらはすべて単一の製品を提供し、表現するものになります。図1は、ネットワークを介して相互に通信する5つの独立したマイクロサービス(API、Auth、Control、Data、Ops)に分離されたAPI製品の例を示しています。マイクロサービスアーキテクチャでは、ネットワークも製品の重要な部分であるため、常にそれを念頭に置く必要があります。各サービス(現在は単一のバイナリとアプリケーションのコンポーネントの両方)は、ハードウェアリソースを個別に拡張でき、そのライフサイクルはエンジニアリングチームによって簡単にコントロールできます。

図1:5つの独立したマイクロサービスに分離されたAPI製品

マイクロサービスのメリット

マイクロサービスアーキテクチャで製品を実行することにはメリットがある。全体としては、緩く結合されたバイナリをさまざまな場所にデプロイできるため、プロダクトオーナーは、費用効果が高く可用性の高いデプロイシナリオから選択し、クラウドや自身のマシンで各サービスをホストすることができます。また、独立に垂直スケーリングや水平スケーリングも可能です。各コンポーネントのハードウェアリソースを増やしたり、コンポーネントを複製したりすることで、さまざまな独立したリージョンを使用できるという利点があります。

もう1つのメリットは、開発ライフサイクルに関するものです。各サービスは他のサービスから論理的に分離されており、内部の複雑さを抑えられます。そのため、開発者は容易に、実装の変更について推測し、新しい機能が予測可能な結果となることを保証することができるようになります。これは、各コンポーネントの独立した開発も意味し、他のサービスを邪魔することなく、1つ以上のサービスに対して、そのローカルに閉じた変更ができるようになります。リリースは個別にプッシュまたはロールバックできるため、機能停止へのより迅速な対応と、より焦点を絞った本番環境の変更が促進されます。

マイクロサービスの課題

このようなメリットにもかかわらず、マイクロサービスに基づくアーキテクチャを使うと、一部のプロセスの処理が難しくなる可能性もあります。以降のセクションでは、前に述べたシナリオを紹介します(ただし、関連する実際の名前をいくつか変更しました)。マイクロサービスの管理に関連する記憶に残る痛みなど、各シナリオを詳細に説明します。マイクロサービス管理とは、例えば、フロントエンドとバックエンド間のトラフィックとリソースの増加を調整することです。また、障害ドメインの設計、そして、すべてのマイクロサービスの結合されたSLOに基づく製品SLOの計算についても説明します。最後に、時間を節約し、最終的には顧客の機能停止を防ぐのに役立つヒントをいくつか紹介します。

シナリオ#1:PetPic

最初のシナリオは、PetPicと呼ばれる架空の製品を中心に展開していきます。図2に示すように、PetPicは、HappytailsとFurlandという2つの異なる地理的地域の愛犬家に犬の写真を提供するグローバルサービスです。このサービスには現在、各地域に100人の顧客がいて、合計200人の顧客がいます。フロントエンドAPIは、それぞれがリージョンの1つにある独立したマシンで実行されます。複雑なサービスとして、PetPicにはいくつかのコンポーネントがありますが、この最初の学びでは、そのうちの1つであるデータベースバックエンドのみを考慮します。データベースはグローバルリージョンのクラウドで実行され、HappytailsとFurlandの両方の地域にサービスを提供します。

図2:グローバルPetPicサービス

>問題:トラフィック増加の調整

データベースは現在、ピーク時には、すべてのリソースの50%を使用しています。これを考慮しつつ、プロダクトオーナーは、PetPicに新機能を実装して、猫の写真を顧客に提供できるようにすることを決定しました。新機能が実装されると、エンジニアは最初にHappytailsで新機能を開始することを決定しました。そうすれば、新しい機能をすべての人が利用できるようにする前に、予想外に熱狂的なユーザトラフィックやリソース使用量の変化を探ることができます。ユーザベースが両方の地域で同じサイズであることを考えると、その時点では、非常に合理的な戦略であるように思われました。

立ち上げの準備として、エンジニアはHappytailsのAPIサービスの処理リソースを2倍にしデータベースリソースを10%増やしました。立ち上げは成功しました。顧客は10%増加しました。これは、猫愛好家がいくらかPetPicに参加したことを示している可能性があります。データベースリソースの使用率はピーク時に50%でした。これも、追加のリソースが実際に必要であることを示しています。

すべてのシグナルは、ユーザの10%の増加は、データベースリソースの10%の増加が必要であることを示しています。Furlandで新機能を開始する準備として、PetPicのエンジニアはデータベースにリソースの10%を追加しました。また、新規顧客の要求に対応するために、FurlandのAPIリソースを2倍にしました。これは、Happytailsで新機能を立ち上げられたときに行ったこととまったく同じ変更でした。

彼らは水曜日にFurlandユーザ向けの新機能を発表しました。その後、ランチタイムに、エンジニアはユーザがサービスでHTTP 500エラーコードを見ているという多くのアラートを受信し始めました。これは、ユーザがサービスを使用できなかったことを意味します。これは、Happytailsの立ち上げとはまったく異なる結果でした。この時点で、データベースチームはエンジニアリングに連絡し、データベースリソースの使用率が2時間前(リリース直後)に80%に達したと述べました。彼らは追加のトラフィックを処理するためにより多くのCPUを割り当てようとしていましたが、その変更は今日は適用されそうにありませんでした。同時に、APIチームはユーザの成長グラフを確認し、予想と異なる変化はないと報告しました。サービスを利用していた顧客は合計220人でした。エンジニアリングチームの誰も停止の明らかな理由を理解できなかったため、立ち上げを中止し、Furlandの機能をロールバックすることにしました。

図3:トラフィック増加の予期しない影響

Happytailsでの機能の立ち上げでは、データベースへのトラフィックが10%増加し、顧客が10%増加しました。ただし、ログを分析した後、エンジニアリングチームは、Furlandで機能が起動されると、新しいユーザが1人も登録されていなくても、データベースへのトラフィックが60%増加したことを確認しました。ロールバック後、昼休みに猫の写真を見たかった不幸な顧客の何人かは、カスタマーサポートへ問い合わせをしてきました。エンジニアは、Furlandの顧客は実は猫愛好家であり、犬の写真しか入手できないときにPetPicの利用にあまり興味がなかったことを知りました。

>ヒント

上記のシナリオは、猫の写真機能がFurlandの既存顧客を引き付けるのに大成功であったことを示しています。しかし、新しい機能のために実施されている展開戦略は、その圧倒的な成功を予測することはできませんでした。ここでの重要な教訓は、すべての製品がさまざまなタイプの成長を経験するということです。このシナリオで見たように、顧客数の増加は既存の顧客からのエンゲージメントの増加とは異なり、さまざまなタイプの成長が常に相互に関連しているとは限りません。ユーザの要求を処理するために必要なハードウェアリソースは、ユーザの行動によって異なる場合があります。また、地理的な地域など、さまざまな要因によっても異なる場合があります。

さまざまな地域で製品を発売する準備をするときは、すべての地域で機能の実験を実行して、新しい機能がユーザの行動(そして、その結果としてリソースの使用率)にどのように影響するかを完全に把握することをお勧めします。また、新しい立ち上げで追加のハードウェアリソースが必要な場合は常に、バックエンドのオーナーが実際にそれらのリソースを割り当てるためのリードタイムを確保することをお勧めします。新しいマシンを割り当てるには、発注書、輸送、ハードウェアの物理的な設置が必要です。ロールアウト戦略では、このリードタイムを考慮する必要があります。

シナリオ#2:障害の切り分け

アーキテクチャの観点からは、今調査したシナリオには、単一障害点として動作するグローバルサービスと、2つの異なる地域で停止を引き起こしたローカルでのロールアウトが含まれていました。モノリスの世界では、コンポーネント間で障害を分離することは、不可能ではないにしても、非常に困難です。この困難の主な理由は、すべての論理コンポーネントが同じバイナリで、したがって同じ実行環境で共存することです。マイクロサービスを使うことの大きな利点は、独立した論理コンポーネントを単独で失敗させることができ、障害がシステム全体に広まり、他のコンポーネントを危険にさらすことを防ぐことができることです。サービスがどのように一緒に失敗するかを分析する設計プロセスは、多くの場合、障害分離と呼ばれます。

この例では、PetPicはHappytailsとFurlandの2つの異なるリージョンに個別にデプロイされています。ただし、これらのリージョンのパフォーマンスは、両方のリージョンにサービスを提供するグローバルデータベースのパフォーマンスと強く結びついています。これまで見てきたように、HappytailsとFurlandの顧客は全く異なる関心を持っています。これが両方の地域に効率的にサービスを提供するようにデータベースをチューニングすることを難しくしています。Furlandの顧客によるデータベースアクセスの変化からでは、Happytailのユーザのユーザエクスペリエンスをうまく捉えるには不十分であり、その逆も同じです。

図4に示すように、上限を設けたローカルキャッシュを使用するなどで、この問題を回避する方法があります。ローカルキャッシュは、応答の待ち時間とデータベースリソースの使用量も削減するため、ユーザエクスペリエンスの向上を保証できます。キャッシュサイズは、グローバルな使用率ではなく、ローカルトラフィックに適合させることができます。また、バックエンドで停止が発生した場合に保存されているデータを提供できるため、サービスが低下する場合にでも適切なレベルに抑えることができます。

キャッシュは、アプリケーションやビジネスの要件に固有の問題を引き起こす可能性もあります。たとえば、データの鮮度やスケーリングの要件が高い場合です。共通の問題として、さまざまなキャッシュをクエリするときのリソースの制限と一貫性のために、キャッシュのレイテンシがゆっくりと増加する問題もあります。また、サービスは、キャッシュされたコンテンツに依存する形で、サービスを提供できるようにするべきではありません。

図4:上限を設けたローカルキャッシュを使った障害の切り分け

製品アーキテクチャ内の他のコンポーネントはどうででしょうか。すべてにキャッシュを使用するのは合理的ですか。クラウドで実行されているサービスを特定のリージョンに分離できますか。両方の質問に対する答えが「はい」であれば、可能であれば、これらの戦略を実装すべきです。クラウドでサービスを実行しても、サービスがグローバルな停止の原因になることを防ぐことはできません。異なるクラウドリージョンで実行されているサービスは、引き続きグローバルサービスとして動作するため、単一障害点となる可能性があります。サービスを障害ドメインに分離することはアーキテクチャ上の決定であり、サービスを実行しているインフラストラクチャによってのみ保証されるわけではありません。

PetPicを使った別のシナリオを考えてみましょう。ただし、今回は制御コンポーネントに焦点を当てます。このコンポーネントは、コンテンツ品質検証を実施します。開発チームは最近、機械学習(ML)に基づいて、自動不正使用検出ルーチンを制御コンポーネントに統合しました。これにより、すべての新しい画像が、サービスにアップロードされるとすぐに検証できます。Happytailsの新規の顧客が、犬と猫の写真のみを提供することを想定していたPetPicに、さまざまな動物の写真を多数アップロードし始めると、問題が発生し始めます。アップロードのストリームにより、制御コンポーネントの自動不正使用検出がアクティブになります。しかし、新しいMLルーチンはリクエスト数に追いつくことができません。

コンポーネントは1000スレッドのプールで実行され、不正使用ルーチン専用のスレッド数をその半分、つまり500スレッドに制限しています。これは、ここでの例のように、大量の長時間処理要求が一緒に到着した場合にスレッドの枯渇を防ぐのに役立ちます。エンジニアが予期していなかったのは、スレッドの半分が使用可能なすべてのメモリとCPUを消費することになり、その結果、両方の地域の顧客がPetPicに画像をアップロードするときに長い遅延時間を体感し始めるということでした

このシナリオでユーザが経験している痛みをどのように軽減できるでしょうか。制御コンポーネントの操作を単一のリージョンに分離すると、このような悪い状況の影響をより抑えることができます。サービスがクラウドで実行されている場合でも、各リージョンに専用の制御インスタンスを用意することで、Happytailsの顧客しか不正な画像アップロードの影響を受けないことが保証されます。ステートレスサービスは、障害のあるドメインに簡単に制限できることに注意してください。データベースの分離が常に可能であるとは限りません。しかし、キャッシュからのローカル読み取りの実装と、場合によってはリージョン間の一貫性を大きな妥協点として検討することをお勧めします。処理スタックを地域的に分離しておくことができるほどよいのです。

>ヒント

サービススタック内のすべてのサービスを同じ場所に配置し、同じ障害ドメインに制限することで、広範囲に広がるグローバルな停止を防ぐことができます。多くの場合、ステートレスサービスを障害ドメインに分離する方が、ステートフルコンポーネントを分離するよりも容易です。リージョン間の通信を避けられない場合は、適切なデグレーションと結果整合性のための戦略を検討してください。

シナリオ#3:SLOの計画

この最後のシナリオでは、PetPic SLOを確認し、各パケットによって提供されるSLOの状況を確認します。一言で言えば、SLOはサービス提供の目的で、顧客とのSLA契約に結び付けるものです。図5の表を見てみましょう。

図5:PetPicのSLO

この表は、SLOを示しています。エンジニアは、これがPetPicの顧客に優れたユーザエクスペリエンスを提供すると考えています。ここでは、各内部コンポーネントによって提供されるSLOも確認できます。API SLOは、APIバックエンド(コントロールやデータなど)のSLOに基づいて構築する必要があることに注意してください。より優れたAPI SLOが必要であるが不可能な場合は、製品設計を変更し、バックエンドオーナーと協力して、より高いパフォーマンスと可用性の提供を検討する必要があります。PetPicの最新のアーキテクチャを考慮して、APIのSLOが筋が通っているどうかを見てみましょう。

運用バックエンド(私たちは「Ops」と呼びます)から始めましょう。これはPetPic APIに関するヘルスメトリックを収集するバックエンドの一部です。このAPIサービスでは、Opsを呼び出すだけで、操作のリクエスト、エラー、処理時間に関する監視データを提供します。Opsへのすべての書き込みは非同期で行われ、障害がAPIサービスの品質に影響を与えることはありません。これらの考慮事項を念頭に置いて、PetPicの外部SLOを設計するときにOps SLOを無視できます。

図6:読み取りSLOとデータベースの調整

それでは、PetPicから写真を読み取るためのユーザジャーニーを見てみましょう。コンテンツの品質は、新しいデータがPetPicに挿入されたときにのみ検証されるため、データの読み取りは制御サービスのパフォーマンスの影響を受けません。APIサービスは、画像情報を取得するだけでなく、リクエストを処理する必要があります。ベンチマークでは、約30ミリ秒かかることが示されています。画像を送信する準備ができたら、APIは応答を作成する必要があります。これには平均で約20ミリ秒かかります。これにより、APIだけでリクエストごとに最大50ミリ秒の処理時間が加算されます。

リクエストの少なくとも半分がローカルキャッシュのエントリにヒットすることを保証できる場合、100ミリ秒のSLOで50パーセンタイルを約束することは非常に合理的です。ローカルキャッシュがない場合、リクエストのレイテンシは少なくとも150ミリ秒になることに注意してください。他のすべてのリクエストでは、画像をデータベースにクエリする必要があります。データベースの応答には100~240ミリ秒かかり、APIサービスと同じ場所に配置されていない可能性があります。ネットワーク遅延は平均100ミリ秒です。これらの数値で最悪のシナリオを検討すると、リクエストにかかる可能性のある最長時間は、50ミリ秒(API処理)+ 10ミリ秒(キャッシュミスを考慮)+ 100ミリ秒(ネットワーク)+ 240ミリ秒(データ)です。合計400msとなります。図6の左側の列のSLOを見ると、数値がAPIバックエンド構造とよく合致していることがわかります。

図7:書き込みSLOとコントロールおよびデータベースの調整

同じロジックに従って、新しい画像をアップロードするためのSLOを確認しましょう。顧客がPetPicに新しい画像の読み込みをリクエストすると、APIはコンテンツを検証するための制御をリクエストする必要があります。これには150ミリ秒から800ミリ秒かかります。制御コンポーネントは、不正なコンテンツをチェックするだけでなく、画像がデータベースにすでに存在するかどうかも確認します。既存の画像は検証済みと見なされます(再検証する必要はありません)。過去のデータによると、FurlandとHappytailsの顧客は、両方の地域で同じ画像セットをアップロードする傾向があります。イメージがデータベースにすでに存在する場合、コントロールコンポーネントは、データを複製せずにそのイメージの新しいIDを作成します。これには約50ミリ秒かかります。このジャーニーは、書き込み要求の約半分に適合し、50パーセンタイルの遅延の合計は最大250ミリ秒になります。

悪いコンテンツを含む画像は、通常、処理により長い時間がかかります。制御コンポーネントが応答を返す時間制限は800ミリ秒です。また、画像が犬または猫の有効な画像であり、データベースにまだ存在していないと仮定すると、データコンポーネントは画像を保存するのに最大1000ミリ秒かかる場合があります。すべての数値を考慮すると、最悪のシナリオでは、応答を返すのに約2000ミリ秒かかる場合があります。図7の左側の列に示されているように、2000ミリ秒は書き込みに対してエンジニアが予測された現在のSLOをはるかに上回っています。SLOを提案する上で悪いシナリオを含めるのを忘れている可能性があることを示しています。このミスマッチを軽減するために、リクエスト期限を99パーセンタイルSLOに制限することを検討できます。この状況は、サービスパフォーマンスの低下または誤った結果につながる可能性もあります。たとえば、APIがクライアントに操作期限を超えたことを報告した後、データベースがイメージの書き込みを終了し、顧客側で混乱を引き起こす可能性があります。この場合、最善の戦略は、データベースチームと協力して、データベースのパフォーマンスを改善、あるいはPetPicの書き込みSLOを調整することです。

>ヒント

配布された製品が正しいSLOを顧客に提供していることを確認することは重要です。外部SLOを構築するときは、すべてのバックエンドの現在のSLOを考慮に入れる必要があります。すべての異なるユーザジャーニーと、応答を生成するために要求がたどる可能性のある異なるパスを考慮してください。より優れたSLOが必要な場合は、サービスアーキテクチャを変更するか、バックエンドの所有者と協力してサービスを改善することを検討してください。サービスとバックエンドを同じ場所に配置しておくことで、SLOの調整を簡単に保証できます。

著者について

Silvia Esparrachiari氏は、Googleで11年間ソフトウェアエンジニアを務めてきた。そこでユーザデータプライバシー、スパムと乱用の防止、最近ではGoogle Cloud SREに携わってきた。彼女は分子科学の学士号と、コンピュータービジョンとヒューマンコンピューターインタラクションの修士号を持っている。現在、彼女はGoogleで、人々が技術スキルを伸ばすことができる、敬意を持った多様な環境を促進することである。

Betsy Beyer氏は、サイト信頼性エンジニアリング(SRE)を専門とするニューヨークのGoogle担当のテクニカルライターだ。彼女は、サイト信頼性エンジニアリング:どのようにGoogleがプロダクションシステムを実行するか(2016)、サイト信頼性ワークブック:SRE実装の実践方法(2018)、安全で信頼性の高いシステムの構築(2020)の共著者である。現在のキャリアに至るまでに、Betsy氏は国際関係と英文学を学び、スタンフォード大学とチューレーン大学で学位を取得している。
 

この記事に星をつける

おすすめ度
スタイル

特集コンテンツ一覧

BT