1. 問題の説明数年前、同僚が K8s スケジューラを 1.28.3 にアップグレードしたところ、メモリ異常が観察されました。ぜひ見てください。ビジネスの潮流に合わせてクラスター ポッドとノードが変化すると、メモリは OOM まで継続的に上昇傾向を示します。 (以下のデータはすべてコミュニティからのものです) 写真 トリガーとなるシナリオは 2 つあります (コミュニティには再現するための他の方法もあります)。 ケース1 ケース2 コミュニティでは類似したメモリ例外シナリオが多数見つかりましたが、再現方法はそれぞれ異なります。上記の問題に関する結論は次のとおりです。 Kubernetes コミュニティはバージョン 1.28 でスケジューリング機能 SchedulerQueueingHints をデフォルトで有効にしましたが、これによりスケジューリング コンポーネントでメモリ例外が発生しました。メモリやその他の問題を一時的に解決するために、コミュニティは 1.28.5 でこの機能をデフォルトで無効にするように調整しました。問題は完全には修正されていないため、この機能を慎重に有効にすることをお勧めします。 2. 技術的背景この章では以下の内容を紹介します。
スケジューラの紹介PriorityQueue は SchedulingQueue のインターフェース実装です。そのヘッドには、スケジュールされる優先度が最も高いポッドが格納されます。 PriorityQueue には次の重要なフィールドが含まれています。
K8s スケジューラの概要については、「Kubernetes スケジューリング (初心者から上級者まで): フレームワーク」を参照してください。最新の K8s スケジューラの概要は後で更新されます。 QueueingHint の紹介K8s スケジューラでは、QueueingHint 機能が導入されました。この機能は、各プラグインから Pod の再キューイングに関する提案を取得することで、不要なスケジューリングの再試行を減らし、スケジューリングのスループットを向上させます。同時に、Pod のスケジューリング効率をさらに向上させるために、適切な場合にはバックオフがスキップされます。 需要背景現在、各プラグインは、EventsToRegister を介してプラグインによって拒否された Pod のスケジュールを再試行するタイミングを定義できます。 たとえば、新しく追加または更新されたノードには、Pod の NodeAffinity と一致するラベルがある可能性があるため、NodeAffinity はノードが追加または更新されると Pod のスケジュールを再試行します。ただし、実際にはクラスター内で多数のノード更新イベントが発生するため、NodeAffinity によって以前に拒否された Pod が正常にスケジュールされるとは限りません。 この問題に対処するために、スケジューラは、無関係なイベントを除外するより洗練されたコールバック関数を導入し、正常にスケジュールされる可能性が高い Pod のみが次のスケジュール サイクルで再試行されるようにします。 さらに、DRA (Dynamic Resource Allocation) スケジューリング プラグインは、デバイス ドライバーからのステータス更新を待機するために Pod を拒否する必要がある場合があります。したがって、一部のポッドは、スケジュールされる前に複数のスケジュール サイクルを経る必要がある場合があります。この場合、フォールバックの待機時間は、デバイス ドライバーのステータス更新を待機する時間よりも長くなります。したがって、スケジュールのパフォーマンスを向上させるために、プラグインが特定の状況でフォールバックをスキップできるようにすることが望ましいでしょう。 目標を達成するスケジューリングのスループットを改善するために、コミュニティは次の改善を提案しました。
潜在的なリスク1) 実装上のエラーにより、unschedulablePods で Pod が長時間スケジュール不可能になる場合があります。プラグインが QueueingHint で構成されているが、Pod をスケジュール可能にするいくつかのイベントを見逃した場合、プラグインによって拒否された Pod は、長時間 unschedulablePods に留まる可能性があります。 ただし、スケジューリング キューは、unschedulablePods 内の Pod を定期的にクリーンアップします。 (デフォルトは5分、設定可能) 2) メモリ使用量の増加スケジューリング キューはスケジューリング プロセス中に発生したイベントを保持する必要があるため、kube-scheduler のメモリ使用量が増加します。したがって、クラスターがビジー状態になればなるほど、より多くのメモリが必要になる可能性があります。 メモリの増加を完全になくすことはできませんが、キャッシュされたイベントをできるだけ早く解放すれば、メモリの増加を遅くすることができます。
カスタム スケジューラ プラグインの開発者は、互換性をアップグレードする必要があります。 EnqueueExtension の EventsToRegister は、戻り値を ClusterEvent から ClusterEventWithHint に変更します。 ClusterEventWithHint を使用すると、各プラグインは QueueingHintFn というコールバック関数を通じて、より多くの無駄なイベントをフィルター処理できるようになります。 移行を簡素化するために、空の QueueingHintFn は常に Queue を返すものと見なされます。したがって、既存の動作を維持したいだけの場合は、ClusterEvent を ClusterEventWithHint に変更するだけでよく、QueueingHintFn を登録する必要はありません。 QueueingHints デザインEventsToRegisterメソッドの戻り値の型が[]ClusterEventWithHintに変更されました。 各 ClusterEventWithHint 構造には、ClusterEvent と QueueingHintFn が含まれます。イベントが発生すると、QueueingHintFn が実行され、イベントが Pod のスケジュールを満たすことができるかどうかが判断されます。 QueueingHintFn 型は、戻り値の型が (QueueingHint、error) である関数です。このうち、QueueingHintは列挙型であり、取り得る値はQueueSkipとQueueです。 QueueingHintFn は、Pod を unschedulableQ から backoffQ または activeQ に移動する前に呼び出されます。エラーが返された場合、呼び出し元によって返された QueueingHint は QueueAfterBackoff として処理されます。この処理により、返された結果に関係なく、Pod が unschedulableQ キューに永久に留まるのを防ぐことができます。 a.バックオフをスキップする/スキップしないタイミングBackoffQ は、「長期間スケジュールできない」ポッドがキューをブロックするのを防ぐことで、高いスループットを維持する軽量キューです。 スケジューリング サイクル中に Pod が拒否される回数が増えるほど、Pod が待機する必要がある時間が長くなり、つまり、BackoffQ に留まる時間が長くなります。 たとえば、NodeAffinity が Pod を拒否し、その後 QueueingHintFn でキューを返す場合、Pod はスケジューリングを再試行する前にバックオフを待つ必要があります。 ただし、一部のプラグインは、スケジュール サイクル中に何らかの障害が発生するように設計されています。たとえば、組み込みプラグイン DRA (Dynamic Resource Allocation) は、Reserve 拡張機能で、リソース ドライバーにスケジュール結果を伝え、リソース ドライバーからの応答を待機するために Pod を一度拒否します。この拒否は、スケジュール サイクルの無駄とは見なされません。特定のスケジューリング サイクルが失敗しても、このサイクルに基づくスケジューリング結果によって Pod のスケジューリングが促進される可能性があります。したがって、この理由で拒否されたポッドをバックオフする必要はありません。 この状況をサポートするために、新しい状態「保留中」を導入します。 DRA プラグインが Pending で Pod を拒否し、その後 QueueingHintFn で Queue を返すと、Pod はバックオフをスキップして再スケジュールされます。 b. QueueingHintの仕組みK8s クラスター イベントが発生すると、スケジューリング キューは、前回のスケジューリング サイクルで Pod を拒否したプラグインの QueueingHintFn を実行します。 次のシナリオでは、Pod の実行方法と移動方法について説明します。
ノードが 3 つあると仮定します。 Pod がスケジューリング サイクルに入ると、1 つのノードはリソース不足のために Pod を拒否し、他の 2 つのノードは Pod の NodeAffinity が一致しなかったために Pod を拒否しました。 この場合、Pod は NodeResourceFit および NodeAffinity プラグインによって拒否され、最終的に unschedulableQ に配置されます。 その後、登録されたプラグインでクラスター イベントが発生するたびに、ディスパッチ キューは QueueingHint を通じてプラグインに通知します。 NodeResourceFit または NodeAffinity のいずれかからの QueueingHintFn がキューを返す場合は、Pod を activeQ または backoffQ に移動します。 (たとえば、NodeAdded イベントが発生すると、Pod が新しいノードにスケジュール可能である可能性があるため、NodeResourceFit の QueueingHint は Queue を返します。) この Pod が unschedulableQ に留まっていた時間に応じて、activeQ または backoffQ に移動されます。 Pod が unschedulableQ に留まる時間が Pod の予想されるバックオフ遅延よりも長い場合、その Pod は activeQ に直接移動されます。それ以外の場合は、backoffQ に移動します。
DRA プラグインが予約拡張フェーズで Pod に対して Pending を返すと、スケジューリング キューは DRA プラグインを Pod の pendingPlugins 辞書に追加し、Pod はスケジューリング キューに返されます。 DRA プラグインが QueueingHint への後続の呼び出しでキューを返すと、スケジューリング キューはこの Pod を直接 activeQ に配置します。 紀元前スケジューリングキューで処理されているポッドを追跡するQueueingHint を導入することで、特定のイベントが発生したときのみスケジュールを再試行できるようになります。しかし、ポッドのスケジュール中にこれらのイベントが発生した場合はどうなるでしょうか? スケジューラはクラスター データのスナップショットを取得し、そのスナップショットに基づいてポッドをスケジュールします。スナップショットは、スケジュール サイクルが開始されるたびに更新されます。つまり、同じスケジュール サイクルでは同じスナップショットが使用されます。 たとえば、Pod がスケジュールされているが、Pod の NodeAffinity に一致するノードがないため拒否されるが、スケジュール プロセス中に Pod の NodeAffinity に一致する新しいノードが追加されるというシナリオを考えてみましょう。 前述のように、この新しいノードはこのスケジューリング サイクル中に候補ノードとは見なされないため、Pod はノード アフィニティ プラグインによって拒否されます。問題は、スケジューリング キューが Pod を unschedulableQ に配置する場合、Pod のノード アフィニティ要件に一致するノードがすでに存在する場合でも、Pod は別のイベントを待機する必要があることです。 スケジューリング中にポッドがイベントを見逃すシナリオを回避するために、スケジューリング キューはポッドのスケジューリング中に発生するイベントを記録し、これらのイベントと QueueingHint に基づいてポッドがキューに入れられる場所を決定します。 したがって、スケジューリング キューは、ポッドがスケジューリング キューを離れてから、ポッドがスケジューリング キューに戻るかスケジュールされるまで、すべてのイベントをキャッシュします。キャッシュされたイベントが不要になった場合、キャッシュされたイベントは破棄されます。 Golang 二重連結リスト*list.List は、Go 言語標準ライブラリの container/list パッケージ内のデータ構造であり、二重リンク リストを表します。 Go では、二重リンク リストは、要素の挿入、削除、トラバーサルなどの操作で効率的なパフォーマンスを提供するために使用される一般的なデータ構造です。 以下は *list.List 構造の簡単な紹介です。
次に例を示します。 PushBack メソッドは、リンク リストの末尾に新しい要素を追加し、新しい要素を表す *list.Element ポインターを返します。このポインターは、削除や変更など、要素に対する後続の操作に使用できます。 *list.Element 構造体には、リンク リスト内の前の要素と次の要素へのポインターと、要素の値を格納するフィールドが含まれています。 *list.Element ポインターを返すことで、さらなる操作が必要になったときに、新しく追加された要素に簡単にアクセスできます。二重リンクリストから要素を削除するには、list.Remove() メソッドを使用できます。このメソッドでは、リンク リスト要素を渡し、リンク リストから要素を削除する必要があります。 このコードは以下を出力します。 この例では、リンク リストの 2 番目の要素 (値は 2) を削除します。 3. 簡単な分析メモリ使用量を分析するには、直接 pprof にアクセスします。 pprof リストの一部は次のとおりです。 写真 ここで、メモリが主に protobuf の Decode に集中していることがわかります。 pprof を具体的に分析しなくても、次の 3 つのアイデアが考えられます。
最初の仮定については、grpc-go の関連する問題を調べて、関連するメモリ異常に関する最近の報告がないことがわかります。これは go 自体の問題ではないようですが、THP 関連の問題が見つかりました。これについては後ほど簡単に紹介します。そうなると残る結果は一つ、つまりK8s自体に問題があるということだが、(*FieldsV1).Unmarshalは5年間使われていないので、問題はない可能性が高い。それではpprofを簡単に分析してみましょう。 しばらくして: 継続的に増え続ける Pod のリストの中に、未公開のデータがいくつか見つかりました。これは、pprof を使用した以前の分析の結果と一致しているようです。継続的な変更の対象はポッドのみであることがわかりました。そこで、コミュニティがすでにこの問題を解決しているかどうかを確認するために、別のトラブルシューティング方法を試しました。トラブルシューティングのために、minikube を使用して Kubernetes 1.18.5 をローカルで起動しました。幸いなことに、この動作を再現できなかったため、バージョン 1.18.5 以降で問題が修正された可能性があります。 調査範囲をさらに絞り込むために、同僚にこれら 3 つのマイナー バージョン間のコミット レコードを確認するように依頼しました。ついに SchedulerQueueingHints 機能をオフにする PR を見つけました。技術的な背景で述べたように、SchedulerQueueingHints 属性はメモリ増加の問題を引き起こす可能性があります。 PriorityQueue 構造を見ると、isSchedulingQueueHintEnabled を通じて機能の論理処理を制御していることがわかります。 QueueingHint機能が有効になっている場合、PodをスケジュールするためにPopメソッドを実行するときに、inFlightPodsの対応するPodのUIDの同じinFlightEventsのリンクリストを入力する必要があります。 では、リンク リスト フィールドはいつ削除されるのでしょうか?削除されるのは、ポッドがスケジュール サイクルを完了したとき、つまり Done メソッドが呼び出されたときだけであることがわかります。 ここで、完了時間が遅くなるほど、メモリの増加がより顕著になることがわかります。 Pod イベントが無視されたり、見逃されたりすると、リンク リストのメモリも異常に増加します。上記のシナリオに対するいくつかの修正方法を確認できます。
著者の時間、視野、知識が限られているため、この記事には誤りや欠落が含まれる可能性があります。読者や業界の専門家が訂正したり意見を交換したりしてくれることを期待しています。 参考文献 1. https://github.com/kubernetes/kubernetes/issues/122725 2. https://github.com/kubernetes/kubernetes/issues/122284 3. https://github.com/kubernetes/kubernetes/pull/122289 4. https://github.com/kubernetes/kubernetes/issues/118893 4. https://github.com/kubernetes/enhancements/blob/master/keps/sig-scheduling/4247-queueinghint/README.md?plain=1#L579 5. https://github.com/kubernetes/kubernetes/issues/122661 6. https://github.com/kubernetes/kubernetes/pull/120586 7. https://github.com/kubernetes/kubernetes/issues/118059 この記事はWeChatの公開アカウント「DCOS」から転載したもので、著者は「DCOS」で、以下のQRコードを通じてフォローできます。 この記事を転載する場合は、「DCOS」公開アカウントまでご連絡ください。 |
<<: Kubernetes クラスターで Iptables を Ipvs に置き換える方法
>>: CKA 試験に合格する可能性を高めます。この記事では、RBAC 権限制御について包括的に理解できます。
現在、百度は多くの変化を遂げており、医療業界の病気用語のランキングを見ると、最適化されたサイトの痕跡...
中国の国力が増すにつれ、外国人は国内のIDC市場にますます注目するようになっています。fatcowの...
最近新しいウェブサイトを始めたのですが、過去の最も苦しい日々に戻ってしまったようです。毎日編集したり...
spinservers (MET のブランド、1994~) は現在、サンノゼ データ センターの専用...
過去2日間、何人かの友人がQQで私に苦情を言いました。彼らの会社のウェブサイトは他のウェブマスターに...
[51CTO.com からのオリジナル記事] 本日の AWS re:Invent カンファレンスで、...
最近、海外のウェブサイトへのアクセス速度が非常に遅いことに気づいた人は多いでしょう。海外の VPS ...
2018年最もホットなプロジェクト:テレマーケティングロボットがあなたの参加を待っています新しいメデ...
1. ナビゲーションウェブサイトとは何ですか?ナビゲーションウェブサイトはURLナビゲーションとも呼...
私は長年マーケティングプロモーションに携わってきました。毎年、商品プロモーションに対する理解は前年よ...
最近、「Secret」と呼ばれる匿名アプリケーションが友人の間で非常に人気があります。私がこのアプリ...
世界的に有名な高防御業者であるpath.net傘下のサーバーブランドであるTempestは現在、一部...
クラウド ゲームでは、ローカル ハードウェア操作の代わりにネットワークを使用して画像を送信します。一...
【IT Times Weekly編集部注】中国の電子商取引の漠然とした発展パターンは、JD.comと...
収益の96%を広告から得ているインターネット大手のGoogleは、モバイル時代において良い成績表を出...