Kube-scheduler は、Kubernetes のコア コンポーネントの 1 つです。主にクラスター リソース全体のスケジューリング機能を担当します。特定のスケジューリング アルゴリズムと戦略に従って、最適な作業ノードにポッドをスケジュールし、クラスター リソースをより合理的かつ完全に活用します。これは、Kubernetes を使用することを選択した非常に重要な理由でもあります。新しい技術が企業のコスト削減や効率性の向上に役立たなければ、それを推進するのは難しいと思います。 スケジュール作成プロセスデフォルトでは、kube-scheduler によって提供されるデフォルトのスケジューラは、ほとんどの要件を満たすことができます。これまでに示した例では、基本的にデフォルトの戦略を使用しており、これにより、実行に十分なリソースを持つノードにポッドを割り当てることができるようになります。ただし、実際のオンライン プロジェクトでは、Kubernetes よりも独自のアプリケーションをよく理解している場合があります。たとえば、ポッドが特定のノード上でのみ実行できるようにしたり、これらのノードが特定の種類のアプリケーションを実行するためにのみ使用できるようにしたりすることが考えられます。これには、スケジューラが制御可能であることが必要です。 kube-scheduler の主な機能は、特定のスケジューリング アルゴリズムとスケジューリング ポリシーに従って、適切なノードにポッドをスケジュールすることです。独立したバイナリプログラムです。起動後、API サーバーをリッスンし続け、空の PodSpec.NodeName を持つ Pod を取得し、各 Pod のバインディングを作成します。 kube-scheduler の構造 このプロセスは単純に思えますが、実際の運用環境では、考慮すべき問題が数多くあります。 - すべてのノードのスケジュールの公平性をどのように確保するのでしょうか?すべてのノードリソース構成が必ずしも同じであるとは限らないことを知っておく必要があります。
- 各ノードにリソースが割り当てられていることを確認するにはどうすればよいですか?
- クラスター リソースを効率的に使用するにはどうすればよいでしょうか?
- クラスター リソースを最大限に活用するにはどうすればよいですか?
- Pod スケジューリングのパフォーマンスと効率を確保するにはどうすればよいですか?
- ユーザーは実際のニーズに応じて独自のスケジュール戦略をカスタマイズできますか?
Kubernetes スケジューラは、実際の環境におけるさまざまな複雑な状況を考慮して、プラグインの形で実装されており、ユーザーによるカスタマイズや二次開発が容易になります。スケジューラをカスタマイズし、プラグインの形で Kubernetes と統合できます。 kubernetes スケジューラのソース コードは、kubernetes/pkg/scheduler にあります。 Scheduler によって作成され実行されるコア プログラムは、pkg/scheduler/scheduler.go にあります。 kube-scheduler のエントリ プログラムを表示する場合、対応するコードは cmd/kube-scheduler/scheduler.go にあります。 スケジュールは主に以下の部分に分かれています。 - 1 つ目は、条件を満たさないノードを除外する事前選択プロセスです。このプロセスは述語(フィルタリング)と呼ばれます
- 次に最適化プロセスが行われます。これは、通過したノードを優先順位(スコアリング)に従って並べ替えるものです。
- 最後に、優先度が最も高いノードが選択されます。途中のステップでエラーが発生した場合は、エラーが直接返されます。
述語ステージでは、まずすべてのノードを走査し、条件を満たさないノードを除外します。これは必須のルールです。このステージで出力された要件を満たすすべてのノードが記録され、第 2 ステージの入力として使用されます。すべてのノードが条件を満たさない場合、いずれかのノードが条件を満たすまでポッドは保留状態のままになります。この期間中、スケジューラは再試行を続けます。 したがって、アプリケーションをデプロイするときに、Pod が常に Pending 状態になっている場合は、スケジュール条件を満たすノードが存在しないことを意味します。この時点で、ノード リソースが使用可能かどうかを確認できます。 優先順位の段階では、ノードを再度スクリーニングします。複数のノードが条件を満たす場合、システムはノードを優先順位(優先度)に従って並べ替え、最終的に最も優先度の高いノードを選択して Pod アプリケーションをデプロイします。 以下はスケジュール プロセスの簡単な図です。 kube-scheduler フィルター より詳細なプロセスは次のとおりです。 - まず、クライアントはAPIサーバーのREST APIまたはkubectlツールを通じてPodリソースを作成します。
- ユーザリクエストを受信すると、APIサーバは関連データをetcdデータベースに保存します。
- スケジューラは API サーバーを監視して、スケジュール (バインド) されていない Pod のリストを表示し、それらを反復処理して各 Pod にノードを割り当てようとします。この割り当てプロセスは、上で説明した 2 つの段階です。
- 事前選択段階 (述語)、ノードのフィルタリング、スケジューラは一連のルールを使用して、要件を満たさないノードをフィルタリングします。たとえば、ポッドがリソース要求を設定すると、ポッドに必要なリソースよりも利用可能なリソースが少ないホストは明らかに除外されます。
- 優先度: ノードの優先度をスコア付けします。前のステージで除外されたノード リストのスコアリング。スケジューラは、デプロイメントによって制御される複数の Pod レプリカを可能な限り異なるホストに分散したり、負荷が最も低いホストを使用したりといった、いくつかの全体的な最適化戦略を考慮します。
- 上記の段階でフィルタリングした後、スコアが最も高いノードが Pod にバインドするために選択され、その結果が etcd に保存されます。最後に選択されたノードに対応する kubelet は、Pod を作成するための関連操作を実行します (もちろん、監視 APIServer によっても検出されます)。
現在、スケジューラはプラグインの形式でスケジューリング フレームワークを実装しています。デフォルトの組み込みスケジュール プラグインは次のコードに示されています。 // pkg/scheduler/framework/plugins/registry.go
// NewInTreeRegistry はすべての内部プラグインを含むレジストリを構築します。 // 外部プラグインは、WithFrameworkOutOfTreeRegistry オプションを介して追加のプラグインを登録できます。 func NewInTreeRegistry ()ランタイム。レジストリ{ fts : = plfeature です。特徴{ EnableDynamicResourceAllocation :機能。デフォルトのフィーチャゲート。有効(機能. DynamicResourceAllocation )、 EnableReadWriteOncePod :機能。デフォルトのフィーチャゲート。有効(機能.ReadWriteOncePod) 、 EnableVolumeCapacityPriority :機能。デフォルトのフィーチャゲート。有効(機能. VolumeCapacityPriority )、 EnableMinDomainsInPodTopologySpread :機能。デフォルトのフィーチャゲート。有効( features.MinDomainsInPodTopologySpread ) 、 EnableNodeInclusionPolicyInPodTopologySpread :機能。デフォルトフィーチャゲート。有効( features.NodeInclusionPolicyInPodTopologySpread )、 EnableMatchLabelKeysInPodTopologySpread :機能。デフォルトのフィーチャゲート。有効(機能. MatchLabelKeysInPodTopologySpread )、 EnablePodSchedulingReadiness :機能。デフォルトのフィーチャゲート。有効( features.PodSchedulingReadiness )、 }
レジストリ: =ランタイム。レジストリ{ 動的リソース。名前:ランタイム。 FactoryAdapter ( fts 、 dynamicresources 。新規)、 セレクタスプレッド。名前: selectorspread 。新しい、 イメージローカリティ。名前: imagelocality 。新しい、 汚染の許容。名前: tainttoleration 。新しい、 ノード名。名前:ノード名。新しい、 ノードポート。名前: nodeports 。新しい、 ノードアフィニティ。名前: nodeaffinity 。新しい、 ポッドトポロジスプレッド。名前:ランタイム。 FactoryAdapter ( fts 、 podtopologyspread 。新規)、 ノードはスケジュールできません。名前: nodeunschedulable 。新しい、 ノードリソース。名前:ランタイム。 FactoryAdapter ( fts 、 noderesources . NewFit )、 ノードリソース。 BalancedAllocationName :ランタイム。 FactoryAdapter ( fts 、 noderesources . NewBalancedAllocation )、 ボリュームバインディング。名前:ランタイム。 FactoryAdapter ( fts 、 volumebinding 。新規)、 音量制限。名前:ランタイム。 FactoryAdapter ( fts 、 volumerestrictions 。新規)、 ボリュームゾーン。名前: volumezone 。新しい、 ノードボリューム制限。 CSIName :ランタイム。 FactoryAdapter ( fts 、 nodevolumelimits 、 NewCSI )、 ノードボリューム制限。 EBSName :ランタイム。 FactoryAdapter ( fts 、 nodevolumelimits 、 NewEBS )、 ノードボリューム制限。 GCEPDName :ランタイム。 FactoryAdapter ( fts 、 nodevolumelimits 、 NewGCEPD )、 ノードボリューム制限。 AzureDiskName :ランタイム。 FactoryAdapter ( fts 、 nodevolumelimits 、 NewAzureDisk )、 ノードボリューム制限。 CinderName :ランタイム。 FactoryAdapter ( fts 、 nodevolumelimits 、 NewCinder )、 インターポダフィニティ。名前: interpodaffinity 。新しい、 キューソート。名前: queuesort 。新しい、 デフォルトバインダー。名前: defaultbinder 。新しい、 デフォルトのプリエンプション。名前:ランタイム。 FactoryAdapter ( fts 、 defaultpreemption 。新規)、 スケジューリングゲート。名前:ランタイム。 FactoryAdapter ( fts 、 schedulinggates 。新規)、 }
返品登録 } 上記から、スケジューラの一連のアルゴリズムは、スケジューリングのさまざまな段階でさまざまなプラグインによって完了することがわかります。まず、スケジュールのフレームワークを理解しましょう。 スケジューリングフレームワークスケジューリング フレームワークは、一連の拡張ポイントを定義します。ユーザーは、拡張ポイントによって定義されたインターフェースを実装して、独自のスケジューリング ロジック (拡張機能と呼びます) を定義し、その拡張機能を拡張ポイントに登録できます。スケジューリング フレームワークは、スケジューリング ワークフローの実行時に対応する拡張ポイントに遭遇すると、ユーザーが登録した拡張機能を呼び出します。スケジューリング フレームワークは、特定の目的のために拡張ポイントを予約します。拡張ポイントの一部の拡張機能はスケジューラの意思決定方法を変更できる一方、拡張ポイントの一部の拡張機能は通知を送信するだけです。 Pod がスケジュールされるたびに、スケジュール プロセスとバインディング プロセスという 2 つのプロセスに従って実行されることがわかります。 スケジューリング プロセスは Pod に適したノードを選択し、バインディング プロセスはスケジューリング プロセスの決定をクラスターに適用します (つまり、選択されたノードで Pod を実行します)。スケジューリング プロセスとバインディング プロセスの組み合わせは、スケジューリング コンテキストと呼ばれます。スケジューリング プロセスは同步 実行され (ある時点でスケジュールされる Pod は 1 つだけ)、バインディング プロセスは非同期的に実行できる (同じ時点で複数の Pod を同時にバインドできる) ことに注意してください。 次のような状況が発生すると、スケジュールおよびバインディング プロセスは途中で終了します。 - スケジューラは、現在このポッドに代替ノードがないと考えています
- 内部エラー
この時点で、Pod はスケジュール キューに戻され、次の再試行を待機します。 拡張ポイント次の図は、スケジューリング フレームワーク内のスケジューリング コンテキストと拡張ポイントを示しています。拡張機能では複数の拡張ポイントを登録できるため、より複雑なステートフル タスクを実行できます。 スケジュールフレームワークの拡張 PreEnqueue この拡張機能は、Pod が内部アクティブ キューに追加される前に呼び出され、Pod がスケジュールの準備完了としてマークされます。すべての PreEnqueue プラグインが Success を返す場合にのみ、Pod はアクティブ キューに追加されます。それ以外の場合は、内部のスケジュール不可能な Pod リストに追加され、スケジュール不可能にはなりません。 (Pod の API に .spec.schedulingGates フィールドを追加して、Pod がスケジュールの準備ができているかどうかをマークできます。Pod がスケジュールの準備ができたら、サービス プロバイダーはこのフィールドを変更してスケジューラに通知できます。) - QueueSort 拡張機能は、スケジュールする Pod のキューをソートして、最初にスケジュールする Pod を決定するために使用されます。 QueueSort 拡張機能では、基本的に、2 つの Pod のうちどちらがスケジュールの優先度が高いかを比較するメソッド Less(*QueuedPodInfo, *QueuedPodInfo) を実装するだけで済みます。同時に有効にできる QueueSort プラグインは 1 つだけです。
- プレフィルター拡張機能は、ポッド情報を前処理したり、クラスターまたはポッドが満たす必要のある前提条件を確認したりするために使用されます。プレフィルターがエラーを返す場合、スケジュール プロセスは終了します。
- フィルター拡張機能は、Pod を実行できないノードを除外するために使用されます。スケジューラは各ノードに対してフィルター拡張を順番に実行します。いずれかのフィルターがノードを選択不可としてマークした場合、残りのフィルター拡張は実行されません。スケジューラは複数のノードで同時にフィルター拡張を実行できます。
- ポストフィルターは通知タイプの拡張ポイントです。拡張機能を呼び出すためのパラメーターは、フィルター フェーズ後にオプション ノードとしてフィルターされるノードのリストです。この情報は拡張機能で使用され、内部状態を更新したり、ログやメトリック情報を生成したりできます。
- スコアリング拡張機能は、すべてのオプション ノードにスコアを付けるために使用されます。スケジューラは各ノードに対してスコアリング拡張機能を呼び出し、スコアリング結果は範囲内の整数になります。正規化スコアリング フェーズでは、スケジューラは特定のノードに対する各スコアリング拡張機能のスコアリング結果と拡張機能の重みを結合して、最終的なスコアリング結果として算出します。
- Normalize スコアリング拡張機能は、スケジューラがノードの最終的なソートを実行する前に、各ノードのスコアリング結果を変更します。この拡張ポイントに登録された拡張機能が呼び出されると、同じプラグイン内のスコアリング拡張機能のスコアリング結果がパラメータとして取得されます。スケジューリング フレームワークがスケジュールを実行するたびに、すべてのプラグインで正規化スコアリング拡張機能が呼び出されます。
- Reserve は、ステートフル プラグインがノード上の Pod 用に予約されたリソースを取得するために使用できる通知拡張ポイントです。このイベントは、スケジューラがポッドをノードにバインドする前に発生します。目的は、Pod がノードにバインドされるのを待機している間にスケジューラが新しい Pod をノードにスケジュールするときに、実際に使用されるリソースが使用可能なリソースを超える状況を回避することです (Pod のノードへのバインドは非同期で行われるため)。これはスケジュール設定プロセスの最後のステップです。 Pod が予約状態になった後、バインディングが失敗した場合は Unreserve 拡張機能がトリガーされ、バインディングが成功した場合は Post-bind 拡張機能によってバインディング プロセスが終了します。
- Permit 拡張機能は、Pod とノードのバインドを防止または遅延するために使用されます。許可の延長では、次の 3 つのいずれかを実行できます。
- 承認: すべての許可拡張機能がポッドとノードのバインドを承認すると、スケジューラはバインド プロセスを続行します。
- 拒否: 許可拡張機能によってポッドのノードへのバインドが拒否された場合、ポッドはスケジュールのためにキューに戻され、予約解除拡張機能がトリガーされます。
- wait: 許可拡張機能が wait を返す場合、Pod は別の拡張機能によって承認されるまで許可フェーズのままになります。タイムアウト イベントが発生すると、待機状態が拒否に変わり、Pod はスケジュールされるキューに戻され、Unreserve 拡張機能がトリガーされます。
- 事前バインド拡張機能は、Pod バインディングの前に何らかのロジックを実行するために使用されます。たとえば、事前バインド拡張機能を使用すると、ネットワークベースのデータ ボリュームをノードにマウントして、ポッドで使用できるようになります。いずれかの事前バインド拡張機能がエラーを返す場合、Pod はスケジュールされるキューに戻され、Unreserve 拡張機能がトリガーされます。
- Bind 拡張機能は、Pod をノードにバインドするために使用されます。
- バインド拡張機能は、すべての事前バインド拡張機能が正常に実行された場合にのみ実行されます。
- ディスパッチ フレームワークは、登録されている順序でバインド拡張機能を 1 つずつ呼び出します。
- 特定のバインド拡張機能では、Pod を処理するか処理しないかを選択できます。
- バインド拡張機能がポッドとノードのバインドを処理する場合、残りのバインド拡張機能は無視されます。
- Post-bind は通知拡張機能です。
- ポッドがノードに正常にバインドされた後、バインド後の拡張機能が受動的に呼び出されます。
- バインド後拡張機能は、バインド プロセスの最後のステップであり、リソースのクリーンアップ アクションを実行するために使用できます。
- Unreserve は通知拡張機能です。リソースが Pod 用に予約されていて、バインディング プロセス中に Pod が拒否された場合、予約解除拡張機能が呼び出されます。予約解除スケーリングにより、ポッド用に予約されていたノード上のコンピューティング リソースが解放されます。プラグインでは、予約拡張子と予約解除拡張子がペアで表示される必要があります。
独自のプラグインを実装する場合は、プラグインをスケジューリング フレームワークに登録し、構成を完了する必要があります。さらに、拡張ポイント インターフェイスを実装する必要があります。対応する拡張ポイント インターフェースは、以下に示すように、ソース コード pkg/scheduler/framework/interface.go ファイルにあります。 // プラグインは、すべてのスケジューリング フレームワーク プラグインの親タイプです。 プラグインインターフェース型{ 名前()文字列 }
// PreEnqueuePlugin は、「PreEnqueue」プラグインによって実装される必要があるインターフェースです。 // これらのプラグインは、Pod を activeQ に追加する前に呼び出されます。 // 注意: preEnqueueプラグインは軽量かつ効率的であることが期待されるため、 // 外部エンドポイントへのアクセスなどのコストのかかる呼び出しが含まれます。そうでなければ他の // イベント ハンドラーでの Pod のエンキュー。 PreEnqueuePluginインターフェース型{ プラグイン // PreEnqueue は、activeQ に Pod を追加する前に呼び出されます。 PreEnqueue ( ctx context . Context , p * v1 . Pod ) *ステータス }
// LessFuncはポッド情報をソートする関数です タイプLessFunc func ( podInfo1 , podInfo2 * QueuedPodInfo ) bool
// QueueSortPlugin は、「QueueSort」プラグインによって実装される必要があるインターフェースです。 // これらのプラグインは、スケジュール キュー内のポッドをソートするために使用されます。キューソートは1回のみ // プラグインは一度に 1 つだけ有効にできます。 QueueSortPluginインターフェース型{ プラグイン // Less は、スケジュール キュー内のポッドをソートするために使用されます。 Less ( * QueuedPodInfo 、 * QueuedPodInfo )ブール値 }
// EnqueueExtensionsは、プラグインが効率的に実装できるオプションのインターフェースです。 // スケジュール不可能なポッドを内部スケジュールキューに移動します。プラグイン // ポッドのスケジューリングに失敗するプラグイン (例: フィルター プラグイン) は、このインターフェイスを実装することが期待されます。 EnqueueExtensionsインターフェース型{ // EventsToRegisterはPodを引き起こす可能性のある一連のイベントを返します // このプラグインのスケジュールによって失敗しました。 // イベントは内部スケジューリングキューをインスタンス化するときに登録されます。 // イベント ハンドラーを動的に構築するために活用されます。 // 注意: 返されるリストは静的である必要があります (構成パラメータに依存しない)。 // そうしないと、未定義の動作が発生します。 イベント登録() []クラスターイベント }
// PreFilterExtensionsは、プラグインに含まれるインターフェースで、 // 事前に計算された値に増分更新を行うためのコールバック // 州。 PreFilterExtensionsインターフェース型{ // AddPodは、影響を評価しようとしているときにフレームワークによって呼び出されます // podToSchedule をスケジュールするときにノードに podToAdd を追加します。 AddPod ( ctx context . Context 、 state * CycleState 、 podToSchedule * v1 . Pod 、 podInfoToAdd * PodInfo 、 nodeInfo * NodeInfo ) *ステータス // RemovePodは、影響を評価しようとしているときにフレームワークによって呼び出されます // podToSchedule をスケジュールするときにノードから podToRemove を削除します。 RemovePod ( ctx context . Context 、 state * CycleState 、 podToSchedule * v1 . Pod 、 podInfoToRemove * PodInfo 、 nodeInfo * NodeInfo ) *ステータス }
// PreFilterPlugin は、「PreFilter」プラグインによって実装される必要があるインターフェースです。 // これらのプラグインは、スケジュール サイクルの開始時に呼び出されます。 タイプPreFilterPluginインターフェース{ プラグイン // PreFilter はスケジュール サイクルの開始時に呼び出されます。すべてのプレフィルター // プラグインは成功を返す必要があります。そうでない場合、ポッドは拒否されます。プレフィルターはオプションで // 下流でどのノードを評価するかを制御する PreFilterResult を返します。これは便利です // O(1) 時間で処理するノードのサブセットを決定できる場合。 PreFilter ( ctx context . Context 、 state * CycleState 、 p * v1 . Pod ) ( * PreFilterResult 、 * Status ) // PreFilterExtensions は、プラグインが実装している場合は PreFilterExtensions インターフェースを返します。 // そうでない場合は nil になります。プレフィルタープラグインは段階的に拡張機能を提供することができます // 前処理された情報を変更します。フレームワークは拡張機能が // AddPod/RemovePodはPreFilterの後にのみ呼び出され、クローンされた場合でも呼び出されます。 // CycleStateであり、これらの関数を複数回呼び出す場合があります。 // 特定のノードで再度フィルタリングします。 プレフィルター拡張機能()プレフィルター拡張機能 }
// FilterPlugin はフィルター プラグインのインターフェースです。これらのプラグインは、 // ポッドを実行できないホストを除外するためのフィルター拡張ポイント。 // この概念は、元のスケジューラでは「述語」と呼ばれていました。 // これらのプラグインは、Status.code で「Success」、「Unschedulable」、または「Error」を返す必要があります。 // ただし、スケジューラは他の有効なコードも受け入れます。 // 「成功」以外の場合は、指定されたホストが除外されます。 // ポッドを実行しています。 タイプFilterPluginインターフェース{ プラグイン // フィルターはスケジューリング フレームワークによって呼び出されます。 // すべてのFilterPluginsは「Success」を返す必要があります。 // 指定されたノードはポッドに適合します。フィルターが「成功」を返さない場合、 // 「Unschedulable」、「UnschedulableAndUnresolvable」、または「Error」を返します。 // 評価対象のノードについては、フィルタプラグインは渡された // この特定のノードの情報のnodeInfo参照(例:ポッド) // ノード上で実行されていると見なされる)を検索するのではなく、 // NodeInfoSnapshot は同じになることが保証されないためです。 // たとえば、プリエンプション中に、元のコピーを渡すことがあります // 評価するためにいくつかのポッドが削除されたnodeInfoオブジェクト // ターゲット ポッドをスケジュールするためにそれらをプリエンプトする可能性があります。 フィルター( ctx context . Context 、 state * CycleState 、 pod * v1 . Pod 、 nodeInfo * NodeInfo ) *ステータス }
// PostFilterPlugin は「PostFilter」プラグインのインターフェースです。これらのプラグインは // ポッド後はスケジュールできません。 PostFilterPluginインターフェース型{ プラグイン // PostFilter はスケジューリング フレームワークによって呼び出されます。 // PostFilter プラグインは、次のいずれかのステータスを返す必要があります。 // - スケジュール不可: プラグインは正常に実行されますが、ポッドをスケジュール可能にすることはできません。 // - 成功: プラグインは正常に実行され、ポッドをスケジュール可能にすることができます。 // - エラー: 内部エラーのためプラグインが中止されました。 // // 情報プラグインは他のプラグインより先に構成する必要があり、常に Unschedulable ステータスを返します。 // オプションで、成功ステータスとともに nil 以外の PostFilterResult が返される場合があります。例えば、 // プリエンプションプラグインはnominatedNodeNameを返すことを選択する可能性があり、フレームワークはそれを再利用して更新することができます。 // プリエンプター ポッドの .spec.status.nominatedNodeName フィールド。 PostFilter ( ctx context.Context 、 state * CycleState 、 pod * v1.Pod 、 filteredNodeStatusMap NodeToStatusMap ) ( * PostFilterResult 、 * Status ) }
// PreScorePlugin は「PreScore」プラグインのインターフェースです。 PreScoreは // 情報拡張ポイント。プラグインはノードのリストとともに呼び出されます // フィルタリングフェーズを通過しました。プラグインはこのデータを使用して内部を更新する場合があります // 状態を取得したり、ログ/メトリックを生成したりします。 PreScorePluginインターフェース型{ プラグイン // PreScoreはノードのリストの後にスケジューリングフレームワークによって呼び出されます // フィルタリングフェーズを通過しました。すべてのプレスコアプラグインは成功を返すか、 // ポッドは拒否されます PreScore ( ctx context . Context 、 state * CycleState 、 pod * v1 . Pod 、 nodes [] * v1 . Node ) *ステータス }
// ScoreExtensions はスコア拡張機能のインターフェースです。 型ScoreExtensionsインターフェース{ // NormalizeScore は、同じプラグインの「Score」によって生成されたすべてのノード スコアに対して呼び出されます。 // 方法。 NormalizeScoreの実行が成功すると、スコアリストが更新され、 // 成功ステータス。 NormalizeScore ( ctx context . Context 、 state * CycleState 、 p * v1 . Pod 、 scores NodeScoreList ) *ステータス }
// ScorePluginは、スコアプラグインがランキング付けするために実装する必要があるインターフェースです。 // フィルタリングフェーズを通過したノード。 タイプScorePluginインターフェース{ プラグイン // スコアはフィルタリングされた各ノードで呼び出されます。成功と整数を返す必要があります // ノードのランクを示します。すべてのスコアリングプラグインは成功を返すか、 // ポッドは拒否されます。 スコア( ctxコンテキスト.コンテキスト、状態* CycleState 、 p * v1 . Pod 、 nodeName文字列) ( int64 、 *ステータス)
// ScoreExtensions は、ScoreExtensions インターフェースを実装している場合はそのインターフェースを返し、実装していない場合は nil を返します。 スコア拡張()スコア拡張 }
// ReservePlugin は、Reserve と Unreserve を備えたプラグインのインターフェースです // メソッド。これらはプラグインの状態を更新するためのものです。このコンセプト // 元のスケジューラでは「assume」と呼ばれていました。これらのプラグインは // Status.code で Success または Error のみを返します。しかし、スケジューラは受け入れる // 他の有効なコードも同様です。成功以外のものは // ポッドの拒否。 タイプReservePluginインターフェース{ プラグイン // スケジューラキャッシュが空になったときに、スケジューリングフレームワークによってReserveが呼び出されます。 // 更新されました。このメソッドが失敗したステータスを返す場合、スケジューラは // 有効なすべての ReservePlugins の Unreserve メソッド。 予約( ctxコンテキスト.コンテキスト、状態* CycleState 、 p * v1 . Pod 、 nodeName文字列) *ステータス // 予約済みのポッドが削除されたときに、Unreserve がスケジューリングフレームワークによって呼び出されます。 // 拒否されました、後続のプラグインの予約中にエラーが発生しました、または // 後のフェーズで。 Unreserveメソッドの実装はべき等である必要があります // 対応する予約が存在しない場合でも、スケジューラによって呼び出される場合があります。 // 同じプラグインのメソッドが呼び出されませんでした。 予約解除( ctx context . Context 、 state * CycleState 、 p * v1 . Pod 、 nodeName文字列) }
// PreBindPlugin は、「PreBind」プラグインによって実装される必要があるインターフェースです。 // これらのプラグインは、ポッドがスケジュールされる前に呼び出されます。 PreBindPluginインターフェース型{ プラグイン // PreBind はポッドをバインドする前に呼び出されます。すべてのprebindプラグインは // 成功しない場合はポッドは拒否され、バインディングのために送信されません。 PreBind ( ctx context . Context 、 state * CycleState 、 p * v1 . Pod 、 nodeName文字列) *ステータス }
// PostBindPlugin は、「PostBind」プラグインによって実装される必要があるインターフェースです。 // これらのプラグインは、ポッドがノードに正常にバインドされた後に呼び出されます。 PostBindPluginインターフェース型{ プラグイン // ポッドが正常にバインドされた後に PostBind が呼び出されます。これらのプラグインは // 情報提供。この拡張ポイントの一般的な用途は、清掃です。 // 上。ポッドがスケジュールされた後にプラグインの状態をクリーンアップする必要がある場合、 // バインドされている場合、PostBind は登録する必要がある拡張ポイントです。 PostBind ( ctxコンテキスト.コンテキスト、状態* CycleState 、 p * v1 . Pod 、ノード名文字列) }
// PermitPlugin は、「Permit」プラグインによって実装される必要があるインターフェースです。 // これらのプラグインは、ポッドがノードにバインドされる前に呼び出されます。 PermitPluginインターフェース型{ プラグイン // Permit はポッドをバインドする前 (およびプラグインを事前バインドする前) に呼び出されます。許可する // プラグインは、Pod のバインドを防止または遅延するために使用されます。許可プラグイン // 成功を返すか、タイムアウト期間を待ってください。そうしないと、ポッドは拒否されます。 // 待機タイムアウトまたはポッドが拒否された場合にもポッドは拒否されます。 // 待っている。プラグインが「wait」を返す場合、フレームワークは待機するだけであることに注意してください。 // 他のプラグインがポッドを拒否しない限り、残りのプラグインを実行した後。 許可( ctx context . Context 、 state * CycleState 、 p * v1 . Pod 、 nodeName string ) ( * Status 、 time . Duration ) }
// BindPlugin は、「Bind」プラグインによって実装される必要があるインターフェースです。バインド // プラグインはポッドをノードにバインドするために使用されます。 タイプBindPluginインターフェース{ プラグイン // すべての事前バインド プラグインが完了するまで、バインド プラグインは呼び出されません。それぞれ // バインド プラグインは設定された順序で呼び出されます。バインドプラグインは、 // 指定された Pod を処理するかどうかを指定します。バインドプラグインがPodを処理することを選択した場合、 // 残りのバインド プラグインはスキップされます。バインドプラグインがポッドを処理しない場合、 // ステータス コードで Skip を返す必要があります。バインドプラグインがエラーを返した場合、 // ポッドは拒否され、バインドされません。 Bind ( ctx context . Context 、 state * CycleState 、 p * v1 . Pod 、 nodeName string ) *ステータス } スケジューリング フレームワーク プラグインを有効または無効にするには、クラスターをインストールして構成するときに KubeSchedulerConfiguration リソース オブジェクトを使用します。次の構成例では、reserve および preBind 拡張ポイントを実装するプラグインを有効にし、別のプラグインを無効にし、プラグイン foo の構成情報を提供します。 APIバージョン: kubescheduler.config.k8s.io/v1 種類: KubeSchedulerConfiguration
--- プラグイン: 予約する: 有効: - 名前: foo - 名前: バー 無効: - 名前: バズ 事前バインド: 有効: - 名前: foo 無効: - 名前: バズ
プラグイン設定: - 名前: foo 引数: > fooプラグインで解析できるコンテンツ 拡張機能が呼び出される順序は次のとおりです。 - 特定の拡張ポイントに対応する拡張機能が設定されていない場合、スケジューリングフレームワークはデフォルトのプラグインの拡張機能を使用します。
- 拡張機能が拡張ポイントに対して構成されアクティブ化されている場合、ディスパッチ フレームワークは最初に既定のプラグインの拡張機能を呼び出し、次に構成内の拡張機能を呼び出します。
- デフォルトのプラグイン拡張機能は常に最初に呼び出され、その後、KubeSchedulerConfigurationで拡張機能が有効になっている順序で、拡張ポイントの拡張機能が1つずつ呼び出されます。
- まずデフォルトのプラグイン拡張機能を無効にし、次に有効リストの特定の位置でデフォルトのプラグイン拡張機能を有効にすることができます。これにより、デフォルトのプラグイン拡張機能が呼び出される順序を変更できます。
デフォルトのプラグイン foo が予約拡張ポイントを実装していると仮定します。プラグイン bar を追加し、それを foo の前に呼び出す場合は、まず foo を無効にし、次に bar と foo の順序で有効にする必要があります。構成例は次のようになります。 APIバージョン: kubescheduler.config.k8s.io/v1 種類: KubeSchedulerConfiguration
--- プロフィール: - プラグイン: 予約する: 有効: - 名前: バー - 名前: foo 無効: - 名前: foo ソースコードディレクトリ pkg/scheduler/framework/plugins/examples にはいくつかのサンプルプラグインがあり、それらの実装を参照することができます。 例実際、スケジューリング フレームワークのプラグインを実装するのは難しくありません。対応する拡張ポイントを実装し、プラグインをスケジューラに登録するだけです。以下は、初期化中にデフォルトのスケジューラによって登録されるプラグインです。 // pkg/scheduler/algorithmprovider/registry.go func NewRegistry ()レジストリ{ レジストリを返す{ // ファクトリーマップ: // 新しいプラグインはここに登録されます。 // 例: // { // stateful_plugin.Name: stateful.NewStatefulMultipointExample、 // fooplugin.Name: fooplugin.New, // } } } ただし、一部のプラグインはデフォルトで登録されていないため、スケジューラにプラグイン コードを認識させたい場合は、自分でスケジューラを実装する必要があります。もちろん、このスケジューラを自分たちだけで完全に実装する必要はありません。デフォルトのスケジューラを呼び出して、上記の NewRegistry() 関数でプラグインを登録するだけです。 kube-scheduler ソース コード ファイル kubernetes/cmd/kube-scheduler/app/server.go には、NewSchedulerCommand エントリ関数があります。この関数のパラメーターは Option 型のリストであり、このオプションはプラグイン構成の定義になります。 // オプションはframework.Registryを設定します。 タイプオプションfunc (フレームワーク.レジストリ)エラー
// NewSchedulerCommand は、デフォルトのパラメータと registryOptions を持つ *cobra.Command オブジェクトを作成します。 func NewSchedulerCommand ( registryOptions ... Option ) * cobra 。指示{ ...... } したがって、この関数を関数エントリとして直接呼び出し、独自のプラグインをパラメーターとして渡すことができます。このファイルの下には、Option インスタンスを作成するための WithPlugin という関数もあります。 func WithPlugin ( name 文字列、ファクトリーランタイム。PluginFactory )オプション{ return func (レジストリランタイム.レジストリ) error { レジストリを返します。レジスタ(名前、工場) } } 最終的に、エントリ関数は次のようになります。 パッケージメイン
輸入( 「k8s.io/コンポーネントベース/cli」 「k8s.io/kubernetes/cmd/kube-scheduler/app」 「数学/ランド」 「オス」 // スキーム パッケージが初期化されていることを確認します。 _ "シンプルスケジューラ/pkg/スケジューラ/apis/config/スキーマ" 「シンプルスケジューラ/pkg/スケジューラ/フレームワーク/プラグイン」 "時間" )
関数main (){ ランド。シード(時間. Now () . UTC () . UnixNano ()) コマンド: = app 。新しいスケジューラコマンド( アプリ。 WithPlugin (プラグイン. Name ,プラグイン. New )) コード: = cli 。実行(コマンド) OS 。終了(コード) } その中で、app.withplugin(sample.name、sample.new)は、次に実装するプラグインです。 withplugin関数のパラメーターから、ここのサンプルがフレームワークの値である必要があることもわかります。PluginFactoryタイプ、およびPluginFactoryの定義は関数です。 タイプPluginFactory = func(configuration runtime.object、f framework.handle)(framework.plugin、error) したがって、sample.newは実際には上記の関数です。この関数では、プラグインからいくつかのデータを取得し、論理処理を実行できます。プラグインの実装は次のとおりです。ここでは、データを取得してログを印刷するだけです。実際のニーズがある場合は、取得したデータに従って処理できます。ここでは、プレフィルター、フィルター、およびプレバインドの3つの拡張ポイントのみを実装します。他の人も同じように拡張できます。 パッケージプラグイン
輸入( "コンテクスト" 「fmt」 v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "K8s.io/klog/v2" 「K8s.io/kubernetes/pkg/scheduler/framework」 「simple-scheduler/pkg/scheduler/apis/config」 「simple-scheduler/pkg/scheduler/apis/config/validation」 )
const name = "sample-plugin"
タイプサンプルstruct { args * config 。 sampleargs フレームワークを処理します。ハンドル }
func ( s * sample ) name () string { 返品名 }
func ( s * sample ) prefilter ( ctxcontext。context。cyclestate * framework。cyclestate 、 pod * v1。pod ) ( * framework。prefilterresult 、 * framework。Status ) { クログ。 V ( 3 )。 infof ( "prefilter pod:%v" 、 pod。Name ) nil 、 nilを返します }
func ( s * sample )フィルター( ctxコンテキスト。コンテキスト、サイクレステート*フレームワーク。サイクレステート、ポッド* v1。pod 、 nodeinfo *フレームワーク。NodeInfo) *フレームワーク。状態{ クログ。 V ( 3 )。 infof ( "フィルターポッド:%v、ノード:%v" 、 pod。NAME 、 nodeInfo。Node ( )。名前) フレームワークを返します。 NewStatus (フレームワーク。成功、 "" ) }
func ( s * sample ) pre -bind ( ctxコンテキスト。コンテキスト、状態*フレームワーク、サイクレステート、 pod * v1。pod 、 nodename string ) *フレームワーク。状態{ nodeinfoの場合、 err : = s 。ハンドル。 snapshotsharedLister ()。 nodeinfos ()。 get ( nodename );エラー!=ゼロ{ フレームワークを返します。 newStatus ( Framework。Error 、 fmt。Sprintf ( "pre -bind get node:%s info serper:%s" 、 nodename 、 err。errer ())) }それ以外{ クログ。 V ( 3 )。 infof ( "プリバインドノード情報:%+v" 、 nodeinfo。node ( ) ) フレームワークを返します。 NewStatus (フレームワーク。成功、 "" ) } }
func new ( FPARGSランタイム。オブジェクト、 FHフレームワーク。ハンドル)(フレームワーク、プラグイン、エラー){ args 、 ok : = fpargs 。 ( * config。sampleargs ) もし!わかりました{ nil 、 fmtを返します。 errorf ( "type%tのargsを取得し、 *sampleargsが欲しい" 、 fpargs ) } err : =検証の場合。 validatesmeplatepluginargs ( * args );エラー!=ゼロ{ nil 、 errを返します } return & sample { args : args 、 ハンドル: FH 、 }、 nil } 完全なコードは、リポジトリhttps://github.com/cnych/sample-scheduler-frameworkで取得できます。
プラグインを発送するためのパラメーターも次のとおりです。 // +k8s:deepcopy-gen:interfaces = k8s.io/apimachinery/pkg/runtime.object
sampleargs structをタイプ{ metav1 。タイプメタ FavoriteColor String `json : " favory_color、omitempty " ` FavoriteNumber int `json : " favorial_number、omitempty " ` 感謝の文字列`json : " antys_to、omitempty " ` } framework.decodeinto関数は古いバージョンで提供され、渡したパラメーターを直接変換しますが、新しいバージョンはruntime.Objectオブジェクトである必要があるため、対応するディープコピーメソッドを実装する必要があります。したがって、アノテーション +K8S:deepcopy-gen:interfaces = k8s.io/apimachinery/pkg/runtime.objectを構造に追加し、Kubernetesソースコードで提供されるハック/update-gen.shスクリプトを介して対応するディープコピー方法を自動的に生成できます。 file register.goで同意するsampleargsをaddwondtypes関数に呼び出して追加する必要があります。また、Main.goファイルでは、ここに定義されているスキーマをインポートしました。これは、PKG/APIで導入されたすべての構成初期化スキーム/構成を使用することに注意してください。 実装が完了した後、コンパイルしてミラーにパッケージ化できます。次に、展開コントローラーを使用して、通常のアプリケーションとして展開できます。クラスター内のいくつかのリソースオブジェクトを取得する必要があるため、もちろんRBAC許可を適用してから、-configパラメーターを介してスケジューラを構成する必要があります。また、Kubescheduleconfigurationリソースオブジェクト構成も使用します。プラグインを介して実装したプラグインを有効または無効にするか、PluginconFigを介していくつかのパラメーター値をプラグインに渡すことができます。 #サンプル-スケジューラ。ヤム 種類:クラスターロール Apiversion : RBAC 。許可。 K8S 。 IO / V1 メタデータ: 名前:サンプル-スケジューラ-クラスターロール ルール: - apigroups : - 「」 リソース: -エンドポイント -イベント 動詞: -作成する -得る -アップデート - apigroups : - 「」 リソース: -ノード 動詞: -得る -リスト -時計 - apigroups : - 「」 リソース: -ポッド 動詞: -消去 -得る -リスト -時計 -アップデート - apigroups : - 「」 リソース: -バインディング -ポッド/バインディング 動詞: -作成する - apigroups : - 「」 リソース: -ポッド/ステータス 動詞: - パッチ -アップデート - apigroups : - 「」 リソース: -ReplicationControllers -サービス 動詞: -得る -リスト -時計 - apigroups : -アプリ -拡張機能 リソース: -レプリカセット 動詞: -得る -リスト -時計 - apigroups : -アプリ リソース: -ステートフルセット 動詞: -得る -リスト -時計 - apigroups : -ポリシー リソース: -PoddisurptionBudgets 動詞: -得る -リスト -時計 - apigroups : - 「」 リソース: -PersistentVolumeclaims -PersistentVolumes 動詞: -得る -リスト -時計 - apigroups : - 「」 リソース: -構成マップ 動詞: -得る -リスト -時計 - apigroups : - 「Storage.k8s.io」 リソース: -StorageClasses -CSINODES 動詞: -得る -リスト -時計 - apigroups : - 「coordination.k8s.io」 リソース: -リース 動詞: -作成する -得る -リスト -アップデート - apigroups : - 「events.k8s.io」 リソース: -イベント 動詞: -作成する - パッチ -アップデート --- APIバージョン: v1 種類: ServiceAcCount メタデータ: 名前:サンプル-スケジューラ-SA 名前空間: Kube-システム --- 種類:クラスターロールバインディング Apiversion : RBAC 。許可。 K8S 。 IO / V1 メタデータ: 名前:サンプル-スケジューラ-クラスターロールバインディング 名前空間: Kube-システム Roleref : Apigroup : RBAC 。許可。 k8s .io 種類:クラスターロール 名前:サンプル-スケジューラ-クラスターロール 科目: -種類: ServiceAcCount 名前:サンプル-スケジューラ-SA 名前空間: Kube-システム --- APIバージョン: v1 種類: ConfigMap メタデータ: 名前:スケジューラ- config 名前空間: Kube-システム データ: スケジューラ- config 。 yaml : | Apiversion : Kubescheduler 。 config 。 K8S 。 IO / V1 種類: Kubescheduleconfiguration LeaderElection : leaderelect : true リースされた復元: 15秒 RenewDeadline : 10 s ResourceLock : EndPointSleases ResourceName :サンプル-スケジューラ ResourceNamesPace : Kube-システム retryperiod : 2秒 プロフィール: -スケジュール名:サンプル-スケジューラ プラグイン: Prefilter : 有効: -名前: 「Sample-Plugin」 フィルター: 有効: -名前: 「Sample-Plugin」 プラグイン設定: -名前:サンプル-プラグイン args : #runtime 。物体 FavoriteColor : "#326ce5" FavoriteNumber : 7 ありがとう: 「Kubernetes」 --- Apiversion : Apps / V1 種類:展開 メタデータ: 名前:サンプル-スケジューラ 名前空間: Kube-システム ラベル: コンポーネント:サンプル-スケジューラ 仕様: セレクター: マッチラベル: コンポーネント:サンプル-スケジューラ テンプレート: メタデータ: ラベル: コンポーネント:サンプル-スケジューラ 仕様: serviceaccountname : sample -scheduler -sa PriorityClassName :システム-クラスター-クリティカル ボリューム: -名前:スケジューラ- config configMap : 名前:スケジューラ- config コンテナ: -名前:スケジューラ 画像: CNYCH /サンプル-スケジューラ: V0 .26 .4 ImagePullpolicy : ifnotpresent 指示: -サンプル-スケジューラ --cnotallow = / etc / kubernetes / scheduler -config.yaml --v = 3 ボリュームマウント: -名前:スケジューラ- config MountPath : / etc / Kubernetes 上記のリソースオブジェクトを直接展開するだけで、Sample-Schedulerというスケジューラを展開します。次に、このスケジューラを使用するためにアプリケーションを展開できます。 #テスト-スケジューラ。ヤム Apiversion : Apps / V1 種類:展開 メタデータ: 名前:テスト-スケジューラ 仕様: セレクター: マッチラベル: アプリ:テスト-スケジューラ テンプレート: メタデータ: ラベル: アプリ:テスト-スケジューラ 仕様: スケジュール名:サンプル-スケジューラ#デフォルトのデフォルトを指定しないで、使用するスケジューラを指定する-スケジューラ コンテナ: -画像: nginx : 1.7 .9 ImagePullpolicy : ifnotpresent 名前: nginx ポート: -containerport : 80 ここで、スケジュール名フィールドを手動で指定し、上記のスケジューラー名サンプルスケジューラーに設定したことに注意してください。 このリソースオブジェクトを直接作成し、作成が完了したら、カスタムスケジューラのログ情報を表示します。 pods -n kube -system -l compnotallow =サンプル-スケジューラを取得するkubectl 名前準備完了ステータス再起動年齢 サンプル-スケジューラ-896658CD7 -K7VCL 1/1ランニング0 57秒 ➜kubectl logs -fサンプル-スケジューラ-896658CD7 -K7VCL -N Kube -System I0114 09 : 14 : 18.878613 1 EventHandlers 。 GO : 173 ]予定外のポッドデフォルト/テストのためのイベントを追加-6486F D49FC -ZJHCX I0114 09 : 14 : 18.878670 1スケジューラ。 GO : 464 ] PODのスケジュールを試してみる:デフォルト/テスト-スケジューラ-6486F D49FC -ZJHCX I0114 09 : 14 : 18.878706 1サンプル。 Go : 77 ] 「Prefilter Podを開始」 pod = "test-scheduler-6486fd49fc-zjhcx" I0114 09 : 14 : 18.878802 1サンプル。 Go : 93 ] "Start Filter pod" pod = "test-scheduler-6486fd49fc-zjhcx" node = "node2" prefilterstate =& { resource :{ millicpu : 0メモリ: 0 ephemeralStorage : 0 aoverpodnumber:0 scalarresours : 0 scalResours : 0 []}}} I0114 09 : 14 : 18.878835 1サンプル。 Go : 93 ] "Start Filter pod" pod = "test-scheduler-6486fd49fc-zjhcx" node = "node1" prefilterstate =& { resource :{ millicpu : 0 ephemeralStorage : 0 approadPodNumber : 0 scalRRESOURS : 0 scalResours : 0 []}}} I0114 09 : 14 : 18.879043 1 default_binder 。 GO : 51 ]デフォルト/テストをバインドしようとする-SCHEDULER -6486F D49FC -ZJHCXからNode1へ I0114 09 : 14 : 18.886360 1スケジューラ。 GO : 609 ] "POD" pod = "default/test-scheduler-6486fd49fc-zjhcx" node = "node1" evaluatedNodes = 3 feablenodes = 2 I0114 09 : 14 : 18.887426 1 EventHandlers 。 GO : 205 ]予定外のポッドデフォルト/テストのためのイベントを削除-スケジューラ-6486F D49FC -ZJHCX I0114 09 : 14 : 18.887475 1 EventHandlers 。 GO : 225 ]スケジュールされたポッドデフォルト/テストのイベントを追加-スケジューラ-6486F D49FC -ZJHCX ポッドを作成した後、対応するログがカスタマイズされたスケジューラに表示され、対応するログが定義した拡張ポイントに表示されることがわかります。私たちの例が成功していることを証明してください。 PODのスケジュール名を確認して確認することもできます。 ➜kubectlはポッドを取得します 名前準備完了ステータス再起動年齢 テスト-スケジューラ-6486F D49FC -ZJHCX 1/1ランニング0 35秒 ➜Kubectlポッドテストを取得-Scheduler -6486F D49FC -ZJHCX -O Yaml ...... RestArtPolicy :常に スケジュール名:サンプル-スケジューラ SecurityContext :{} ServiceAcCount :デフォルト ...... Kubernetes v1.17から始めて、スケジューラフレームワークに組み込まれた事前に選択された優先関数がプラグインであるため、スケジューラを拡張するために、このスケジューリングのフレームワークを習得して理解する必要があります。 |