paint-brush
シナモンの開梱 — Uber における新しい回復力アプローチ@bmarquie

シナモンの開梱 — Uber における新しい回復力アプローチ

Bruno Marquié10m2024/01/22
Read on Terminal Reader

長すぎる; 読むには

Uber が何世紀にもわたる概念を利用して、マイクロサービス アーキテクチャの復元力をどのように再定義しているかを知りたいですか?当面の問題をより深く理解するために、基本的な要素を一緒に検討しましょう。
featured image - シナモンの開梱 — Uber における新しい回復力アプローチ
Bruno Marquié HackerNoon profile picture
0-item
1-item



昨年、Uber Engineering チームは、マイクロサービス アーキテクチャ向けに設計された新しい負荷軽減メカニズムに関する記事を公開しました


この記事はさまざまな観点から非常に興味深いです。そこで、読みながらメモを取り、自分の理解を記録し、最後までに答えが見つからなかった場合に後でさらに深く掘り下げたいことを書き留めました。新しいことを学ぶにはこれが最善の方法だと何度も気づきました。


私が最初に思いついたのは、このソリューションを構築するために使用された 100 年前の原則への言及でした。それは私が大好きなことです。さまざまな分野から概念やアイデアを借用し、それらを応用して別の領域の問題を解決することです。


システムの回復力と安定性に興味がある場合は、優れた書籍「Release It!」も読むことをお勧めします。マイケル・T・ナイガード著。


これは古い本ですが、優れた本です。障害に効果的に対処する方法に重点を置き、回復力と安定したソフトウェア システムを構築するための戦略、パターン、実践的なガイダンスを詳しく解説しています。




Uber は、Cinnamon と呼ばれる新しい負荷制限ソリューションを実装しました。これは、PID コントローラー (何世紀も前のメカニズム) を利用して、サービスの現在の負荷とリクエストの優先順位に基づいて、サービスによってどのリクエストを処理するか破棄するかを決定します。


これにはサービス レベルでの調整は必要なく (ただし、それについて質問がありました)、自動的に適応可能で、以前のソリューション QALM よりもはるかに効率的です。また、Uber のマイクロサービス アーキテクチャは気の弱い人向けではないことも忘れないでください。



Cinnamon が Uber のサービス メッシュにどのように適合するかを示す図。 (前述の Uber の記事より)。




この有名な PID コントローラーとは何ですか?またどのように使用されますか?


PID コントローラーは、温度、流量、圧力、速度、その他のプロセス変数を制御するために産業用制御アプリケーションで使用される機器です。 PID (比例積分微分) コントローラーは、制御ループ フィードバック メカニズムを使用してプロセス変数を制御し、最も正確で安定したコントローラーです。

— https://www.omega.co.uk/prodinfo/pid-controllers.html


この何世紀にもわたる概念についてさらに詳しく知りたい場合は、Wikipedia を参照してください。


さて、記事に戻ります。 PID は、比例、積分、微分を表します。彼らの場合、PID コントローラーとして知られるコンポーネントを使用して、3 つのコンポーネント (または測定値) に基づいてサービス (入力リクエスト) の健全性を監視します。

比例

「比例」という用語は、実行されるアクションが現在のエラーに比例することを示します。簡単に言えば、これは、適用される補正が望ましい状態と実際の状態の差に正比例することを意味します。誤差が大きい場合、修正措置もそれに比例して大きくなります。


エンドポイントが過負荷になると、バックグラウンドの goroutine が優先キューへのリクエストの流入と流出の監視を開始します。


したがって、ロード シェッダーの比例 (P) コンポーネントは、現在のキュー サイズがターゲットまたは目的のキュー サイズからどれだけ離れているかに基づいて制限レートを調整します。キューが希望よりも大きい場合、より多くのシェディングが発生します。それが小さい場合、脱落は減少します。


それが私の理解です。

積分

PID コントローラーの仕事は、キューに入れられるリクエストの数を最小限に抑えることですが、自動チューナーの仕事は、応答遅延を (過度に) 犠牲にすることなく、サービスのスループットを最大化することです。


本文ではキュー サイズの文脈で「積分 (I)」について明示的に言及していませんが、PID コントローラーの役割がキューに入れられたリクエストの数を最小限に抑えることであることを示しています。キューに入れられたリクエストを最小限に抑えることは、時間の経過とともに蓄積されたエラーに対処するという Integral コンポーネントの目標と一致します。


エンドポイントが過負荷かどうかを判断するために、リクエスト キューが最後に空になった時間を追跡し、最後のたとえば 10 秒以内に空になっていない場合は、エンドポイントが過負荷になっていると見なします (Facebook からインスピレーションを受けています)。


ロード シェッダーでは、リクエスト キューが最後に空になってからの時間など、リクエスト キューの履歴動作に関連する決定に関連付けられる場合があります。


正直に言うと、それは私には完全にはわかりません。ちょっともどかしいと言わざるを得ません。彼らは何世紀にもわたるメカニズムを利用することに言及していますが、どの部分が何に対応し、どのように動作するかを明示的に述べていれば役に立ったでしょう。私は彼らの素晴らしい記事の価値を貶めるつもりはありません。それはここでの単なる私の暴言です… 結局のところ、私はフランス人です… ;)

デリバティブ

こちらの方が識別しやすいと思います。


従来の PID (比例-積分-微分) コントローラーでは、現在の誤差の変化率に基づいてコントローラーにシステムの将来の動作を予測させる場合、「微分 (D)」アクションが特に役立ちます。振動を抑え、システムの安定性を向上させるのに役立ちます。


この記事で言及されているロード シェッダーと PID コントローラーのコンテキストでは、Derivative コンポーネントは、リクエスト キューがどれくらいの速さでいっぱいになるかを評価するために使用される可能性があります。そうすることで、安定したシステムを維持し、突然の変化や予測不可能な変化を防ぐことを目的とした意思決定を支援します。


リジェクタ コンポーネントには 2 つの役割があります。a) エンドポイントが過負荷かどうかを判断すること、b) エンドポイントが過負荷である場合、リクエストの一定の割合を除外してリクエスト キューが可能な限り小さくなるようにすることです。エンドポイントが過負荷になると、バックグラウンドの goroutine が優先キューへのリクエストの流入と流出の監視を開始します。これらの数値に基づいて、PID コントローラーを使用して、制限するリクエストの比率を決定します。 PID コントローラーは、正しいレベルを見つけるのが非常に速く (必要な反復が非常に少ないため)、リクエスト キューが空になると、PID は比率をゆっくりと下げるだけであることを保証します。


前述のコンテキストでは、PID コントローラーは、エンドポイントが過負荷になったときに制限するリクエストの比率を決定するために使用され、リクエストの流入と流出を監視します。変化率に応答する PID コントローラーの派生コンポーネントは、リクエスト キューがどれくらいの速さで満たされるか空になるかを評価することに暗黙的に関与します。これは、システムの安定性を維持するために動的な決定を下すのに役立ちます。



Integral コンポーネントと Derivative コンポーネントの両方が、時間の経過に伴うリクエスト キューの動作の監視に関与します。

  • 一体型コンポーネント


過負荷を判断するというコンテキストでは、統合コンポーネントは、要求キューが空ではない状態になっている時間を追跡することに関連している可能性があります。これは、誤差信号の積分を時間の経過とともに累積するという考えと一致します。

「統合 — リクエストがキューに入っている時間に基づいて…」


  • 派生コンポーネント


一方、微分要素は変化率に関係します。リクエスト キューの状態の変化の速さに応答します。


「微分 — キューが埋まる速度に基づく拒否…」


積分と導関数の両方の側面にはリクエスト キューの観察が含まれますが、その動作のさまざまな側面に焦点を当てています。

積分コンポーネントは空ではない状態の継続時間を強調しますが、微分コンポーネントはキューの変化速度を考慮します。


ゲームの終了時に、これら 3 つの尺度を利用して、リクエストに対する行動方針を決定します。


私が疑問に思っているのは、これら 3 つの要素をどのように組み合わせているのかということです。彼らがどのように監視しているのかも知りたいです。


それでも、アイデアは得られたと思います...


これら 3 つの要素に応じて、どのリクエストを保留するか、またどのように決定するのでしょうか?

エッジのエンドポイントにはリクエストの優先度の注釈が付けられ、これは、 Jaeger を介してエッジからすべての下流の依存関係に伝播されます。この情報を伝播することにより、リクエスト チェーン内のすべてのサービスはリクエストの重要性と、それがユーザーにとってどれほど重要であるかを知ることができます。


最初に思い浮かぶのは、サービス メッシュ アーキテクチャにシームレスに統合されるということです。


分散サービス トレースとヘッダーを使用してリクエストの優先順位を伝達するというコンセプトを高く評価しています。これらの方針に沿って、おそらく Istio プラグインとしてサービスの外部に配置するのではなく、各マイクロサービスにこの依存関係が追加された共有ライブラリを選択するのはなぜでしょうか?独立したリリース/展開サイクル、多言語サポートなど、それが提供する利点を考慮します。


以下に追加の考えをいくつか示します。


  • 時間の経過とともに、計画外の優先度の高いバッチ プロセスやタスクの開始、タイムゾーン/地域レベルでの変更など、状況の変化に基づいてリクエストの優先度が低下したり変更される可能性はありますか?


  • このアプローチは、サーキット ブレーカー、タイムアウト、リース、バルクヘッドなどの他の「標準」復元力管理アプローチ/パターンとどのように連携しますか?


  • サービスが入力リクエストの優先順位に基づいてリクエストをどの程度拒否するかを決定する(決定を所有する)ことを理解することに興味があります。彼らは構成の欠如について言及しています (この点についてはさらに検討したいと思います)。そのため、これはサービスに固有のものではなく、サービスの実装から完全に独立しており、一種の透過的です。これにより、サービス メッシュ/独立リリース サイクルと依存ライブラリのポイントが戻ります。


そうですね、私は共有ライブラリの大ファンではないので、偏見がありますが、共有ライブラリがリリース/デプロイのプロセスを複雑にすると考えているからです。ただし、サービス固有の構成について考慮すべき点があるかどうかはわかりません。おそらく、クエリの処理を開始して完了するまでサービスが待機する時間を設定しているのではないでしょうか?


この図は、実際の処理のためにビジネス ロジックに送信される前に、リクエストがどのように Cinnamon を通過するかを示しています。 (前述の Uber の記事より)。


おそらく、テストする価値のある側面の 1 つは、エジェクターの意思決定プロセスです。


私が理解しているところによると、サービスにローカライズされた PID コントローラーに基づいてリクエストを拒否するかどうかが決定されます。よりグローバルなアプローチのオプションはあるのでしょうか?たとえば、パイプライン内のダウンストリーム サービスの 1 つが (独自の PID コントローラーが原因で) 過負荷になっていることがわかっている場合、この過負荷のサービスに到達する前に、アップストリーム サービスがリクエストを拒否することを決定できるでしょうか (これは、さらに n ステップ下にある可能性があります)パス)?


この決定は、PID コントローラーまたはダウンストリーム サービスの自動チューナーによって返された値に基づく可能性があります。



今、私は記事の締めくくりとして言及されたさまざまな側面について思索を巡らせ、システムの効率性を示すいくつかの数値を提供しています。これは非常に印象的です。


異なる受信 RPS での 3 つのセットアップに対する高優先度の層 1 リクエストの P50 レイテンシ。 (前述の Uber の記事より)。


彼らはある時点で「各リクエストのタイムアウトは 1 秒である」と述べています。


5 分間のテストを実行し、固定量の RPS (例: 1,000) を送信します。トラフィックの 50% が Tier 1、50% が Tier 5 です。各リクエストのタイムアウトは 1 秒です。


分散システムでは、リクエストを特定の有効期限または期限に関連付け、処理パス上の各サービスがこの期限を強制する責任を負うことが一般的です。リクエストが完了する前に有効期限に達した場合、チェーン内のサービスにはリクエストを中止または拒否するオプションがあります。


この 1 秒のタイムアウトがリクエストに付加されており、各サービスはこの期限のどの時点にいるかに応じて、リクエストの中止を決定できると思います。これはサービスを通じて集約されるため、グローバルな指標です。これは、システム全体の健全性や依存関係を全体的に把握して、サービスの 1 つがダウンしているためにリクエストを完了する機会がない場合に、できるだけ早くリクエストを中止することを決定するという、以前に述べた点と共鳴すると思います。パス。


ダウンストリーム サービス (ローカル PID コントローラーからのデータで構成される) の「健全性」を応答に添付されたヘッダーとして返し、より進化したサーキット ブレーカー/早期プリエンプティブ シェディング メカニズムの構築に使用できるでしょうか?




最後に、この論文に記載されている説明に基づくと、以前のアプローチが適切であると思われるため、以前のアプローチについて詳しく知りたいと思っています。


グッドプットとレイテンシーの尺度を調べると、QALM と Cinnamon のどちらが最も優れたパフォーマンスを発揮するかについては、疑いの余地がありません。記事内で QALM アプローチへのリンクについて言及していることに注意してください。おそらくそこから始めるべきです;)


いつものことですが、これらのアプローチはすべての人に適しているわけではありません。 Uber のアーキテクチャと負荷は一種の独自のものです。私は実際、このシリーズの次の記事を読みたくて、特に PID コントローラーと自動チューナーについて詳しく知りたいと思っています。


Cinnamon を使用して、1 世紀前の技術を使用して、サービスの容量を拒否および推定するためのしきい値を動的に設定する効率的な負荷シェッダーを構築しました。これにより、QALM (および CoDel ベースのロード シェッダー) で気づいた問題が解決されます。つまり、Cinnamon は次のことが可能になります。


- 安定した不合格率を迅速に見つける


- サービスの容量を自動的に調整します


- 設定パラメータを設定せずに使用できます。


- 発生するオーバーヘッドが非常に低い


このアプローチの興味深い点は、(優先) キューを使用するため、すべてのリクエストが処理されることを考慮して、新しい入力リクエストごとに何を行うかを決定することです。前述したように、このメカニズムで、同じ PID 測定に基づいて、依存するすべてのサービスの健全性も考慮できるかどうかに興味があります…


この記事には他にも、戦略の効果をどのように測定するか、以前のアプローチとの比較など、興味深い側面があります。ただし、すでに提示されているものよりも詳細なメモは必要ありません。したがって、 元の記事を読むことを強くお勧めします。



この記事は役に立ちましたか? LinkedinHackernoon Mediumで私をフォローしてくださいぜひ👏この記事をシェアしてください!