ListWatchからWatchListへ

ListWatchからWatchListへ

分析する

まず、自分で実装する場合にどのように設計するかを想像することができます。 Informer は現在 ListWatch メカニズムであり、サーバーはストリーミング List をサポートしていないため、クライアントとサーバーの両方が適応する必要があるのは避けられません。したがって、予備的な方向性は次のようになります。

  1. サーバーはストリーミングリストリクエストをサポート
  2. Informerはサーバー側APIの変更に適応します

クライアントの適応は比較的単純であり、サーバー側でそれをどのように実装するかに依然として焦点が当てられています。まず、前回の Stale Read で紹介した以前のリストのロジックを確認しましょう。

便宜上、以下のテキストでは Resourceversion を表すために RV を使用します。このセクションのロジックはバージョン v1.26.9 に基づいており、ページングは​​ Etcd を通じて直接実行されるため、ページング クエリは無視されます。

List リクエストでも Watch リクエストでも、そのクエリは RV での受け渡しをサポートします。サーバーは、要求されたさまざまな RV に応じて対応する処理を行います。 RV の値に応じて、次の 3 つのケースに分けられます。

  1. 設定されていないか、RV="" が明示的に設定されている
  2. RV = "0"
  3. RV = 「ゼロ以外の値」

最初の 2 つのケースでは、List は WatchCache Store のコンテンツ、つまりサーバーによってキャッシュされた Etcd の関連データすべてを直接返します。

3 番目のケースでは、サーバーはキャッシュされたデータの最大バージョンが渡された RV を超えるまで待機してから、キャッシュ内のデータを返します。一定時間 (3 秒) 待ってもキャッシュ内のデータが指定されたバージョンに到達しない場合は、「リソース バージョンが大きすぎます」というエラー メッセージが返され、クライアントは 1 秒後に再試行するように指示されます。

新しいバージョンでは、リストの古い読み取りの問題が修正されました。最初の 2 つのケースでは、まず kube-apiserver から Etcd の最新の RV を取得し、WatchCache Store のコンテンツが RV に追いつくのを待ってから、すべてを一度に返します。

つまり、サーバーは最新の完全なデータがすでにあるかどうかを認識し、それに基づいてストリーミング形式でデータを返すことができます。既存のストリーミング API は Watch なので、これに基づいて List 効果をサポートできます。リストのリクエストに基づいて変更してみませんか?リストを変更すると、クライアント側の調整が過度に必要になるためです。 List は単独で使用されることが多いですが、Watch は基本的に Informer で使用されます。

したがって、最終的な作業は、Watch API を使用して List 効果を実現する方法になりますが、データは引き続きストリーミング形式でクライアントに返され、同時に、Informer は ListWatch メソッドを変更して、Watch API のみを使用して以前の効果を実現します。以下の記事では主にサーバーの実装を詳しく紹介し、クライアントの適応部分については簡単に紹介します。

原理

Watch API に SendInitialEvents=true パラメータを追加することで、リスト効果をサポートします。 Watch リクエストを受信した後、サーバーは InitEvents としてクライアントに送信するデータを決定します。これらのデータを送信した後、サーバーは、InitEvents が送信されたことをクライアントに通知するサインとして、特定の BOOKMARK イベント (RV が以下の bookmarkAfterRV に対応する特定の注釈を持つ BOOKMARK) をクライアントに送信します。指定された BOOKMARK イベントを受信した後、クライアントは以前に受信したすべての InitEvents を List の結果として処理します。

タイミング図

以下はv1.29コードに基づいた分析です。現時点では、v1.29 はまだアルファ状態です。記載されている古いバージョンは 1.27 より前のバージョンを指し、新しいバージョンは v1.29 を指します。表示されるコードが以下の説明と一致しない場合は、コードのバージョンが原因である可能性があります。

写真

WatchCache から始まり、右側の青い 4 つは kube-apiserver の起動時に実行されます。 G1 と G2 は、Etcd からデータを取得し、クライアントの CacheWatcher 入力チャネルにデータを送信するために使用される 2 つの goroutine を表します。

  1. G1.1 各リソース タイプは、リフレクターを含むキャッチャーに対応します。 WatchCache は、Etcd から取得したデータを保存する Reflector のストアとして機能します。
  2. G1.2 Reflector はデータを取得するために Etcd List および Watch API の呼び出しを開始します。
  3. G1.3 Reflector は取得したデータを使用して、WatchCache のストアと循環バッファを更新します。これらのストアと循環バッファは、それぞれオブジェクトの全量とオブジェクトの最新の更新イベントを保存するために使用されます。
  4. G1.4 WatchCache を更新した後、Cacher の着信チャネルにイベントを送信します。
  5. G2.1 は、Cacher の受信チャネルからデータを消費し、それをすべての CacheWatcher の入力チャネルに送信するか、RV > bookmarkAfterRV の BOOKMARK イベントをすべての CacheWatcher の入力チャネルに定期的 (1 ~ 1.25 秒) に送信します。

上記のプロセスは、サーバー起動時のデータ処理フローを説明しています。次に、クライアントからのリクエストがあった場合の処理​​フローを見てみましょう。

  1. Reflector は初めて Watch リクエストを開始し、クエリで RV=""&sendInitialEvent=true&resourceVersinotallow=NotOlderThan&AllowWatchBookmarks=true を指定します。ここで、RV="" と RV="0" の両方で List 効果を実現できますが、旧バージョンと比較すると、新バージョンの Watch リクエストでは RV="" に対して特別な処理が行われ、Watch API Stale Read の問題が解決されています (List Stale Read については前回の記事で紹介しました。List には Consistent Read を有効にするかどうかを制御するための FeatureGate が用意されていますが、Watch には対応する FeatureGate がないため、新バージョンの RV="" のリクエストは Consistent Read である必要があります)。リクエストを受信すると、サーバーはこのリクエストに対応する CacheWachter オブジェクトを作成します。
  2. リクエストを受信すると、サーバーは bookmarkAfterRV の値を計算します。 RV="0" の場合、bookmarkAfterRV は WatchCache RV (WatchCache ストア データ内の最大 RV) になります。 RV="" の場合、Etcd から最大 RV を bookmarkAfterRV として取得し、bookmarkAfterRV を CacheWatcher に渡します。最後に、CacheWatcher は WatchCache Store のデータと独自の入力チャネルを組み合わせて InitEvents を準備します。

2a WatchCache ストアから返されるデータの取得を開始します。このときの処理ロジックは旧バージョンと同じです。ストア内のすべてのデータが返され、次のステップのためにストア データの最大 RV が記録されます。

2b 入力チャネル内のイベントを消費し、その RV が 2a で渡された RV より大きいかどうかを比較します。それが BOOKMARK タイプであり、その RV が 2a で渡された RV と等しく、bookmarkAfterRV イベントが送信されていない場合、この BOOKMARK イベントはリストの終了と見なされ、Annotation: k8s.io/initial-events-end が設定され、最終的にクライアントに送信されます。

これまで、サーバーの主なプロセスが紹介され、クライアント Informer も対応する適応を行いました。 WatchList 機能がオンになっている場合は、完全なデータを取得するために Watch 要求が送信されます。アノテーション k8s.io/initial-events-end を含む BOOKMARK イベントを受信すると、その RV が記録され、この期間中に受信および処理されたオブジェクトがリスト結果として使用されます。最後に、上記の RV をパラメータとして Watch リクエストが再度呼び出されます。このステップからは、Informer の従来の Watch ロジックになります。

データフロー

写真

この画像は KEP 3157 ウォッチリストから取得したもので、実際にはタイミング図も含まれています。しかし、本のシーケンス図のタイミング図には、コードと一致しない問題がいくつかあります。したがって、タイミング図はここでは直接使用されず、再描画されます。

上記の 2 つの図を組み合わせると、全体のプロセスを理解できます。上図の a はタイミング図の 2a に対応し、b はタイミング図の 2b に対応し、c はタイミング図の G2.1 に対応します。下部の白い部分はタイミング図のG1のロジック、つまりEtcdからデータを取得することに対応しています。クライアント要求の処理は上から下へ行われ、データの返送は下から上へ行われます。

知らせ

上記の処理ロジックには多くの詳細があり、特別な注意が必要です。

  1. Watch API (RV="" WatchList 関数) の古い読み取り問題を修正しました。これは基本的にリストの古い読み取りを排除するためのものですが、Watch API に実装されています。前の記事と組み合わせると、List API を直接使用するか WatchList を使用するかに関係なく、Stale Read の問題を回避できます。
  2. WatchCache Store のデータと Cacher の imcomming chan データは絡み合っているため、2a がすべての Store データを処理した後、最大の RV が記録され、imcomming chan データの処理時に使用するために 2b に渡されます。時間の逆流を避けるために、イベント RV > RV の非 BOOKMARK イベントのみがクライアントに送り返されます。
  3. CacheWatcher の入力チャネルに RV < bookmarkAfterRV のイベントはありません。 G2.1 が Cacher から着信チャネルを消費し、それをすべての CacheWatcher 入力に送信すると、イベント タイプが BOOKMARK であり、RV < bookmarkAfterRV である場合、入力チャネル バッファ サイズが制限されているため、イベントは直接破棄されます。 Cacher は作成された後、入力にデータの書き込みを開始し、2a がストア内のすべてのデータを処理した後、入力チャネルの消費を開始します。その間には時差があります。イベントの長さはストア内のデータの量に関係します。不要な BOOKMARK イベントを破棄すると、入力チャネルの負荷を軽減できます。これには、入力チャネルへのイベント処理ロジックの追加が含まれます。これには、単一の CacheWatcher によってプロセス全体がブロックされるのを回避するためにバッファーがいっぱいになったときの処理方法や、データ例外の処理方法など、さまざまな特殊な状況の処理が含まれます。
  4. 最終的にクライアントに送り返される特定の注釈を運ぶ BOOKMARK イベントの RV は >= bookmarkAfterRV です。 bookmarkAfterRV と同じではないことに注意することが非常に重要です。元の KEP タイミング図のここ (2c) の説明は間違っています。根本的な原因は、ブックマーク タイマーの周期が 1 ~ 1.25 秒であること、つまり BOOKMARK イベントが 1 ~ 1.25 秒ごとに生成され、その RV が着信チャネルの最大 RV であることです。まさにこの時間間隔と 3 の説明を組み合わせることで、G2.1 によって送信される最初の有効な BOOKMARK イベント (CacheWatcher 入力チャネルに入る) の RV は >= bookmarkAfterRV になります。これはまた、bookmarkAfterRV BOOKMARK イベントを返す前に返された有効な負荷を運ぶすべてのイベント セットの最大 RV も >= bookmarkAfterRV であることも間接的に示しています。つまり、マークは bookmarkAfterRV ですが、リストの結果には bookmarkAfterRV よりも大きいデータが含まれています。個人的には、これはまだ最適化できると思います。リストの時間消費は、ブックマーク タイマー サイクル (1 ~ 1.25 秒) によって短縮できます。 2b で非 BOOKMARK イベントを処理する場合は、RV == bookmarkAfterRV であり、bookmarkAfterRV BOOKMARK イベントが送信されていないと判断するだけで済みます。このとき、bookmarkAfterRV BOOKMARK をクライアントに直接返すことができます。大量のデータの場合、すべてのデータを返すのにかかる時間が Watch のタイムアウト時間 (約 1 秒) を超えると、タイムアウトの可能性が減り、WatchList プロセスの繰り返し実行が回避され、メモリ消費をある程度削減できます。

要約する

この記事では、WatchList の実装原理とロジックを主に分析し、いくつかの詳細を説明します。詳細については後ほどコミュニティとさらに話し合う予定です。この KEP では、kube-apiserver のメモリ負荷を軽減するための 2 つの変更も導入されています。スペースの都合上、次回の記事で紹介させていただきます。同時に、すべての最適化作業が完了する前と完了した後の効果の比較も示します。乞うご期待〜

<<:  ガートナー: クラウドネイティブテクノロジーを導入してデジタル変革を加速する方法

>>:  5 種類のクラウド環境に対する包括的な防御ガイド

推薦する

Pacificrack: ロサンゼルス KVM シリーズ、VPS は年間 6.52 ドルから、512M メモリ/1 コア/10g SSD/500g 帯域幅

パンデミックの間、パシフィックラックは困難を乗り越え、ハードウェアを一括インストールしました。短期的...

クラウドコンピューティング2.0時代:産業のアップグレードを支えるクラウドビジネス

6年後、テンセントは大規模な社内構造調整を実施し、クラウドおよびスマート産業事業グループを設立しまし...

PKCシステムを深めるEasyStackが第1回Feitengエコシステムパートナーカンファレンスに登場

[[286258]] 12月19日、北京国際会議センターで第1回「飛天エコシステムパートナー会議」が...

企業は検索エンジンマーケティングを有効活用する必要がある

検索エンジンマーケティングとは、ユーザーが情報を検索する機会を利用して、ユーザーの検索エンジンの使用...

ウェブサイトが検索エンジンにブロックされているかどうかを確認し、対処する方法

多くのウェブマスターにとって、ウェブサイトの降格、掲載数の減少、ランキングの低下などは、悩ましい問題...

100人に対して1人で戦った「被告」周洪義。周洪義は「無責任」なのか?金を払ってでも謝罪は可能か?

周洪一氏は常に「被告」であり、その数と頻度はインターネット業界ではめったに匹敵しない。結局のところ、...

ASO 最適化: ユーザーが APP を検索するとき、何を検索しているのでしょうか?

はじめに:この記事は によって編集され、公開されています。転載する場合は、必ずこの記事へのリンクを含...

クラウドコンピューティングの未来はどうなるのか

クラウド コンピューティングはデジタル変革の重要な部分であり、企業は柔軟性と効率性を実現するためにク...

新しい顧客はどこにいますか?

ビジネスの初期の成功は、多くの場合、「ユニークなトリック」の結果です。例を挙げると、かつてテレビ広告...

エッジコンピューティングが次の大きなキャリアチャンスとなる理由

過去 10 年間で、接続されたデバイスの数とそれらが生成するデータの量は飛躍的に増加しました。一般的...

hostsailor-$14/年/256MB RAM/15GB HDD/256GB トラフィック/ルーマニア/オランダ/Windows

Hostsailor の 40% オフ VPS プロモーションは 1 週間前から実施されていますが、...

母子ブランドのマーケティング戦略を分析!

「三子政策」の実施に伴い、住民一人当たり可処分所得が増加し、育児費が家計支出の重要な部分を占めるよう...

SEO最適化におけるBaiduアルゴリズムの新しい調整への対処方法

少し前、国内の検索エンジン大手が再び大規模なアルゴリズム調整を行い、一部のSEO最適化機関と一部のS...

2018年グローバルクラウドコンピューティング市場分析概要

今日、クラウド コンピューティング プラットフォームとアプリケーションは、新しいデジタル ビジネスを...

クラウドネイティブアプリケーションを保護する方法

クラウド コンピューティングは、展開の流動性と自動化の向上という点で、非常に大きな機能をもたらします...