Kube-apiserver がまた OOM ですか?

Kube-apiserver がまた OOM ですか?

起源

前回の記事では、kube-apiserver へのリストおよび監視リクエストを開始する Informer の実装を紹介しました。大規模なクラスターでは、特にメモリの面で kube-apiserver がボトルネックになることがわかっています。 kube-apiserver OOM などの問題に遭遇した人も多いと思います (偶然にも、kube-apiserver OOM の問題は最近オンラインで 2 回発生しました)。この記事では主に、kube-apiserver の Informer に必要な 2 つのインターフェース list と watch の実装について説明します。

インターネットで検索すると、ソースコード分析に関する記事がたくさん見つかります。ここではコードについてあまり詳しく説明しません。主に原則とプロセスについてお話しし、最後に理論と実践を組み合わせて現在の問題を簡単に紹介します。この記事では主に現在の実装について説明します。現在の実装と問題が発生する理由を理解することによってのみ、問題を解決する方法がわかります。次の記事では、これらの問題をどのように解決するかを詳しく分析します。

原理

キャッシュ読み込み

写真

コアコンポーネント: Cacher、watchCache、cacheWatcher、reflector。このうち、watchCache はリフレクタのストアとして使用され、Etcd は listerWatcher のストレージとして使用され、store と listerWatcher はリフレクタを構築するためのパラメータとして使用されます。データフローはおおよそ次のようになります。

  1. kube-apiserver が起動し、各リソース タイプに対して、対応する cacher の startCaching を呼び出し、次に reflector.ListAndWatch を呼び出して、Etcd リストに対応する listerWatcher のリストと監視をトリガーし、次に監視します。監視時にwatchChanが作成され、Etcdから読み取られた結果はまずwatchChanのincomingEventChanに入り、その後リフレクタ消費のための変換処理を経てwatchChanのresultChanに送信されます。
  2. Reflector は、上記の resultChan、つまり watch.Event オブジェクトのデータを使用し、イベント タイプに応じてストアの add、delete、および modify メソッドを呼び出します。ここでのストアはwatchCacheです。 watchCache.processEvent によって処理された後、watchCacheEvent オブジェクトが組み立てられ、watchCache のキャッシュ (適応サイズで履歴イベントを保持するウェイクアップ バッファー) とストア (完全なデータ) が更新され、最後に eventHandler を通じてキャッシャーの受信チャネルに送信されます。
  3. cacher.dispatchEvents は、受信した chan のデータを消費し、処理後に各 cacheWatcher の入力 chan に送信します。
  4. kube-apiserver 監視リクエストが外部から呼び出されると、対応する cacheWachter オブジェクトが作成され、最終的に cacheWatcher の監視プロセッサに渡されます。監視プロセッサは入力チャネルを消費し、イベント配信のために watchCacheEvent を呼び出します。

写真

キャッシュデータフロー

データをキャッシュするために使用されるコア構造は watchCache です。この中には、cache (循環バッファ) と store (スレッドセーフ ストア) という 2 つの主要な構造があり、それぞれ履歴 watchCacheEvent と実際のリソース オブジェクトを格納するために使用されます。ストアにはすべてのオブジェクトが格納されます。キャッシュのサイズは適応的ですが、最大容量の制限があるため、格納される watchCacheEvent によって表されるオブジェクト セットは、必ずしもストア内のすべてのデータをカバーするわけではありません。

歴史的問題

kube-apiserver は、独自のメモリ使用量の最適化において多くの改善を行いましたが、まだ完全に解決されていない問題がいくつかあります。

kube-apiserver OOM

メモリ消費源

kube-apiserver のメモリ消費は、主に次の 2 つの原因で発生します。

  1. その理由の一部は、すべてのクラスター データ (k8s リソース タイプである Event を除く) をキャッシュし、各リソースの履歴 watchCacheEvent や、一部の内部データ構造や chan などをキャッシュするからです。この部分は避けられず、適切に最適化することはできますが、あまり効果はありません。
  2. 残りの部分はクライアントからのリクエスト、特にリストリクエストから発生します。 kube-apiserver は、メモリ内のデータのディープコピーとシリアル化を実行する必要があります。必要なメモリの量は、データおよびリクエストの量と正の相関関係にあります。データとリクエストの量が増加すると、より多くのメモリが必要になります。さらに、このメモリ部分は Golang GC では完全に回復できません。リスト要求の主なソースは Informer です。

リスト要求がより多くのメモリを消費する理由は次のとおりです。

  1. デフォルトでは (resourceversion を指定しない場合)、etcd から直接データを取得すると、データ ストアの完全な応答サイズよりも数倍多い大量のメモリが必要になる場合があります。
  2. キャッシュからデータを取得するために ResourceVersion パラメータを明示的に指定するリクエスト (たとえば、ResourceVersinotallow="0") は、実際にはパフォーマンス上の理由から、ほとんどのクライアント Go ベースのコントローラで使用されるアプローチです。メモリ使用量は最初のケースよりもはるかに少なくなります。しかし、これは完璧ではありません。シリアル化されたオブジェクトを保存し、送信されるまで完全な応答を保存するためのスペースがまだ必要だからです。

一般的なシナリオ

kube-apiserver OOM を簡単に引き起こす可能性のある一般的なシナリオが 2 つあります。

  1. Informer は DaemonSet などの一部のプログラムで使用されます。変更を加えたり、障害により再起動したりすると、クラスターのサイズが大きくなるため、リクエストの数も増え、kube-apiserver のメモリへの負荷も増加します。保護対策(電流制限)がない場合、kube-apiserver の OOM が発生しやすくなります。 OOM 後、異常な接続が他のマスター ノードに転送され、雪崩が発生します。理論的にはこれも容量の問題であり、対策としては容量拡張(マスターノードの追加)と電流制限(サーバー側電流制限:APF、MaxInflightRequestなど、クライアント側電流制限)が挙げられます。
  2. 特定の種類のリソースのデータ量が大きく、kube-apiserver で設定されたタイムアウト パラメータがリスト要求をサポートするには小さすぎる場合、Informer はリスト操作の実行を試行し続けます。この状況は、コントロール プレーン コンポーネントで頻繁に発生します。これは、コントロール プレーン コンポーネントが全量のデータを取得する必要があることが多いためです。

リソースバージョンが古すぎる

原理

厳密に言えば、これは問題ではありません。仕組みはこのようになっており、理論的には1台のマシンのリソースが無制限であればこの現象は回避できる。説明の便宜上、リソースバージョンを表すために RV が使用されます。

その本質は、クライアントがウォッチ API を呼び出すときにゼロ以外の RV を運ぶことです。サーバーがキャッシャーのウォッチ実装ロジックに到達すると、渡された RV に基づいて、RV より大きいすべての watchCacheEvents を循環バッファーでバイナリ検索し、それらを初期イベントとしてクライアントに返す必要があります。循環バッファの最小 RV が受信 RV より大きい場合、つまり、サーバーによってキャッシュされたイベントの最小 RV がクライアントによって送信されたイベントの最小 RV より大きい場合、キャッシュされた履歴イベントが不完全であることを意味します。これは、イベントが多すぎてキャッシュ サイズが制限されているため、古い watchCacheEvent が上書きされたことが原因である可能性があります。

一般的なシナリオ

  1. この状況は、kubelet が apiserver に接続されている場合、またはウォッチに labelselector または fieldselector がある場合によく発生します。各 kubelet は自身のノードの Pod のみを考慮するため、自身のノードの Pod が変更されておらず、他のノードの Pod が頻繁に変更されている場合、kubelet のローカル Informer によって記録された最後の RV は、循環バッファー内の最小の RV よりも小さくなる可能性があります。このとき、再接続が発生すると (ネットワークが切断されるか、Informer 自体がタイムアウトして再接続する)、kube-apiserver ログに「リソース バージョンが古すぎます」という文字が表示されます。
  2. kube-apiserver が再起動されるシナリオでは、クラスター内の一部のリソース タイプが頻繁に変更され、一部のリソース タイプが頻繁に変更されない場合、頻繁に変更されないリソース タイプを監視するインフォーマーのローカルの最後の RV は、最新の RV よりも小さくなるか、大幅に小さくなります。 kube-apiserver が再起動されると、小さなローカル RV で監視されますが、それでもこの問題が発生する可能性があります。

クライアント Informer がこのエラーに遭遇すると、ListAndWatch を終了して LIstAndWatch を再起動します。これにより、kube-apiserver のメモリが増加したり、OOM になったりする可能性があります。問題の根本的な原因: RV はグローバルです。シナリオ間の本質的な違いは、シナリオ 1 は 1 つのリソースでのスクリーニングによって発生するのに対し、シナリオ 2 は複数のリソース タイプ間の RV の大きな違いによって発生することです。

最適化

上記の分析の結果、この問題には 2 つの原因が考えられます。

  1. 循環バッファの長さには制限があります。
  2. クライアント Informer が保持している最後の RV が古すぎます。

コミュニティでは、この問題が発生する可能性を減らすために、数バージョン前に最適化も行いました。

問題 1 については、適応型ウィンドウ サイズを採用しました。問題は依然として発生しますが、以前に値をハードコーディングした場合よりも問題が発生する可能性は低くなります。同時に、メモリリソースの無駄を避けるために、必要のない場合には長さを短縮します。

2 番目の問題には、2 つの最適化があります。 BOOKMARK メカニズムは、同じリソースに対する異なるフィルタリング条件によって発生する問題を最適化するために導入されました。 BOOKMARK は、最新の RV を定期的にクライアントに返すイベント タイプです。 ProgressNotify は、さまざまなリソース タイプの RV がまったく異なるという問題を解決するために導入されました。 kube-apiserver を再起動した後、Informer resume によって発生する問題は、基本的に Etcd の clientv3 ProgressNotify メカニズムの使用に起因します。 Etcd を監視する場合、kube-apiserver はこの機能を有効にするための特定のオプションを持ちます。 ProgressNotifyはEtcdの公式ドキュメント[1]を参照しています。

WithProgressNotify を使用すると、受信イベントがない場合でも、監視サーバーは 10 分ごとに定期的な進行状況の更新を送信します。進行状況の更新には、WatchResponse にイベントが 0 個あります。

詳細については、次のKEPを参照してください:956-watch-bookmark[2]および1904-efficient-watch-resumption[3]

古くなった読み物

これは長年の課題です。これは、watchCache の登場以来存在しています。本質的には、以前は Etcd に直接アクセスするときに使用されていた線形一貫性読み取り (Etcd によって提供される機能) は、kube-apiserver キャッシュを読み取るシーケンシャル一貫性にダウングレードされました。

シナリオ

  1. T1: StatefulSet コントローラーは、ノード 1 にスケジュールされた pod-0 (uid 1) を作成します。
  2. T2: ローリングアップグレードの一環として pod-0 が削除されました
  3. node-1はpod-0が削除されたことを確認してクリーンアップし、APIでpodを削除します。
  4. StatefulSetコントローラは、node-2に割り当てられる2番目のポッドpod-0(uid 2)を作成します。
  5. node-2はpod-0がスケジュールされていることを確認し、pod-0を起動します。
  6. ノード 1 上の kubelet がクラッシュして再起動し、マスターから分割された HA セットアップ (複数の API サーバー) 内の API サーバーに対して、スケジュールされたポッドの初期リストを実行します (ウォッチ キャッシュは任意に遅延されます)。ウォッチキャッシュはT2より前のポッドのリストを返します。
  7. ノード1はT2より前のポッドのリストをローカルキャッシュに格納します。
  8. node-1 は pod-0 (uid 1) を起動し、node-2 はすでに pod-0 (uid 2) を実行しています。

詳細については、第59848号[4]を参照してください。

考える

私たちは、さまざまなソースコード解析や原理解析の記事をよく目にしますが、その内容を安易に信じてしまいがちです。ただし、バージョンの反復と一部の詳細の処理により、それらを完全に理解できなかったり、完全に習得できなかったりする場合があります。たとえば、リスト要求で RV=0 が渡された場合、kube-apiserver キャッシュが使用されますか?オンラインで検索すると動作すると表示されますが、コードを見るとそうではないことがわかります。たとえば、kube-apiserver の再起動後にデータが完全にロードされていない場合、RV=0 のリスト要求が発生すると、Etcd に直接アクセスしてデータを取得します。一見重要でない詳細が、問題に対処する際の私たちの考え方に影響を与える可能性があります。たとえば、Etcd の負荷が高い理由を知りたい場合、この詳細がわかっていれば、RV != 0 のリクエストだけでなく、すべてのリスト リクエストを意識的に確認することになります。

最後に、1 つ考えておきたいことがあります。kube-apiserver のメモリ負荷は、主にリスト リクエストによって発生します。では、リスト機能を実装するために、リスト要求の代わりにストリーミング処理を使用できるでしょうか?この方法では、メモリ消費を一定の空間複雑度に制限できますか?次の記事では、ストリーミングAPIを使用してリストによるメモリ爆発の問題を解決する方法を具体的に分析しますので、お楽しみに〜

参考文献

[1]etcd: https://pkg.go.dev/github.com/coreos/etcd/clientv3#WithProgressNotify

[2]ブックマーク: https://github.com/kubernetes/enhancements/blob/master/keps/sig-api-machinery/956-watch-bookmark/README.md

[3]ウォッチ再開: https://github.com/kubernetes/enhancements/blob/c63ac8e05de716370d1e03e298d226dd12ffc3c2/keps/sig-api-machinery/1904-efficient-watch-resumption/README.md

[4]古い記事: https://github.com/kubernetes/kubernetes/issues/59848

<<:  データセンターの進化: ローカルコンピューティングからクラウド、エッジコンピューティングへ

>>:  実証済みの分析戦略でデータの潜在能力を最大限に引き出します

推薦する

SEO 最適化を学ぶにはどうすればいいですか?

最近、弟をseowhyにトレーニングに行かせました。私の親友がそれを知ったとき、彼はとても困惑してい...

ウェブマスターの拡張は、視野を広げてより多くの機会を見ることができるようにすることです

2013年中国インターネット起業家(ウェブマスター)会議が終了して半月が経ちました。私は2010年か...

2021年中国クラウドコンピューティング市場の展望:業界変化の3つの鍵

クラウド コンピューティングの変革の瞬間が到来しました。それは、成熟した市場構造、広い将来の空間、テ...

Baidu入札マッチングのやり方

Baidu の入札では、マッチング方法は 3 つしかありません。1 つ目は部分一致、2 つ目は完全一...

Baidu Videoはすべての海賊版コンテンツを削除し、エンターテイメントプラットフォームに生まれ変わります

テンセントテクノロジーの羅宋は12月30日に報告した。最近、中国オンラインビデオ著作権侵害対策連盟か...

エッジコンピューティングへの投資はどこに向かうのでしょうか?

2020 年の初めには、エッジ コンピューティングは最高の盛り上がりを見せていたようです。では、この...

Parallels Desktop for Chromebook Enterprise が全世界でリリース、初めて Chromebook 上で Windows を直接実行可能に

Parallels は本日、企業向け Chromebook 上で Windows を直接実行できる世...

ウェブマスター向け情報サイトが復活しつつあるが、草の根ウェブマスターは持ちこたえなければならない

ウェブマスター業界の台頭に伴い、その派生品も多くの草の根ウェブマスターに歓迎されてきました。SEOト...

Ancestry.com が売却を検討中:情報筋

北京時間6月6日、外国メディアの報道によると、事情に詳しい情報筋は、米国の系図ウェブサイト「Ance...

ウェブサイトのコンテンツ構造に関する考察

ウェブサイト、機能。ウェブサイトに機能が 1 つもないとしたら、どうやってユーザーを維持できるでしょ...

対外貿易マーケティング

インターネット産業の出現と発展から現在に至るまで、インターネットの世界は次々と伝説を生み出してきまし...

Zhongshen Technology は、Web サイトの最適化において低レベルの不正行為手法を使用しましたか?

みなさんこんにちは。私はハルビンバーチャルアンドリアルウェブサイトデザインです。最近、クライアントの...

巨華軒の100億元補助金「狙撃兵」拼多多

コアリーディングJuhuasuan は Double 12 を選択してPinduoduo を直接ター...

#DoubleDanEvent# inxy: CDN プロモーションが 30% オフ (グローバル ノード 246 個)、専用サーバーが 30% オフ、クラウド ストレージが 28% オフ

inxyは、今から1月9日まで、クリスマスと元旦のスーパーセールを開始しました。(1) 6つの主要C...

エッジコンピューティングと AI 戦略が相互補完する必要がある理由

多くの企業は、コンピューティング能力をデータ ソースやエンド ユーザーの近くに配置できるため、エッジ...