Kubernetes: ワークフロー、スケジューラーの機能、アルゴリズム

技術者であるWang Menhai氏が、**Kubernetes**の**ワークフロー、スケジューラーの機能、アルゴリズム**を4つのパートに分けて解説します。

アリババのテクニカルエキスパート、Wang Menghai（Musu）氏によるものです。

[Kubernetes](https://www.alibabacloud.com/product/kubernetes?spm=a2c65.11461447.0.0.61b6ee8bi4qUB2)は最も人気のある自動コンテナO&Mプラットフォームであり、柔軟な宣言型コンテナオーケストレーションを特徴としています。この記事では、Kubernetes 1.16における基本的なスケジューリングフレームワーク、スケジューリングプロセス、主要なフィルタ、Scoreアルゴリズムの実装、およびカスタムスケジューリングを実装するための2つの方法について説明します。

#スケジューリングプロセス
###スケジューリングの概要
Kubernetesは、最も人気のある自動コンテナO&Mプラットフォームです。Kubernetesでのコンテナオーケストレーションには、Kubernetesのスケジューラが欠かせません。本稿では、Kubernetes v1.16のKubernetesスケジューラに焦点を当てます。

![スクリーンショット 2021-05-06 10.15.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/bf5a1842-e197-f13f-4ec5-70271f999be1.png)



###ポリシー
現在、Kubernetesのスケジューラが使用するスケジューラポリシーの起動設定は、設定ファイル、コマンドラインパラメータ、ConfigMapの3つの形式をサポートしています。スケジューラポリシーは、最新のScheduler Frameworkのどの述語、優先順位、エクステンダー、プラグインがメインのスケジューリングプロセスで使用されるかを指定するために設定することができます。

###インフォーマー
Kubernetesスケジューラは起動時に、スケジューリングに必要なデータを、List APIとWatch APIを使って、インフォーマーを介してKubernetes APIサーバーから取得します。このデータには、Pod、ノード、PV（Persistent Volumes）、PVC（Persistent Volume Claim）などが含まれます。このようなデータは、Kubernetesスケジューラのキャッシュデータとして前処理されます。

###スケジュールパイプライン
Kubernetesのスケジューラは、スケジューリング対象のPodをインフォーマーを介してキューに挿入します。これらのPodは、キューから周期的にポップアウトされ、スケジュールパイプラインにプッシュされます。

スケジュールパイプラインは、スケジュールスレッド、待機スレッド、バインドスレッドに分かれています。

- スケジュールスレッドは、pre-filter、filter、post-filter、score、reserveの各フェーズに分かれています。
フィルターフェーズでは，Podスペックに合致するノードを選択します。scoreフェーズでは，選択されたノードをスコアリングし，ソートし、reserveフェーズでは，最適にソートされたノードのノードキャッ シュに，そのノードにPodが割り当てられていることを示すPodを入れます。このようにして、次にスケジューリングを待つPodは、ノードがフィルタリングされ，スコアリングされたときに、以前に割り当てられたPodを知ることができます。

- waitスレッドは、PVの作成が成功するのを待ったり、ギャングスケジューリングで関連するPodのスケジューリングが成功するのを待ったりして、Podに関連するリソースの準備が整うのを待ちます。
- bindスレッドは、Podとノードの関連付けをKubernetes APIサーバーに持続的に保存します。

Podはscheduleスレッドでのみ1つずつスケジューリングされ、waitスレッドとbindスレッドでは非同期かつ並列にスケジューリングされます。

#スケジューリング処理
ここでは、Kubernetesのスケジューラーの仕組みについて説明します。次の図は、Kubernetesのスケジューラーのワークフローを示しています。

![スクリーンショット 2021-05-06 10.15.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/c64e5f50-74d6-4741-faad-dc5c2f46cd15.png)

スケジューリングキューには、activeQ、backoffQ、unscchedulableQの3つのサブキューがあります。

Kubernetesのスケジューラが起動すると、スケジューリングされるべきすべてのPodがactiveQに入り、優先順位に基づいてPodがソートされます。スケジュールパイプラインは、スケジューリングのためにactiveQからPodをフェッチします。スケジューリングに失敗した場合、そのPodは実際の状況に応じてunschedulableQまたはbackoffQに入れられます。Podのスケジューリング中にノードキャッシュやPodキャッシュが変更された場合など、スケジューラーがキャッシュした場合には、PodはbackoffQに入れられます。それ以外の場合、PodはunscchedulableQに入れられます。

UnschedulableQは、60秒などの比較的長い間隔で、定期的にPodをactiveQまたはbackoffQにフラッシュしたり、スケジューラキャッシュが変更されたときに、関連するPodをactiveQまたはbackoffQにフラッシュしたりします。backoffQは、unschedulableQよりも速く、再スケジューリングのためにバックオフモードでスケジューリングされるPodをactiveQに転送します。

スケジュール・スレッド・フェーズでは、スケジュールされるPodがスケジュール・パイプラインからフェッチされ、関連するノードがノード・キャッシュからフェッチされ、フィルタ・ロジックにマッチします。ノードキャッシュ内のノードをトラバースするための空間アルゴリズムは、すべてのノードをフィルタリングしないように最適化されており、災害復旧目的でサンプルスケジューリングが有効になっています。

最適化されたアルゴリズムロジックでは、ノードキャッシュ内のノードはゾーンに応じて異なるヒープに分配されます。詳細はnode_tree.goのNextメソッドを参照してください。フィルタフェーズでは、ノードキャッシュに対してゾーンインデックスを保持します。ノードがフィルタリングのためにポップアウトされるたびに、ゾーンインデックスは自動的に1つずつ増加する。次に、対象ゾーンのノードリストからノードがフェッチされます。

前述の図に示すように、縦軸のノード・インデックスも、ノードがポップアウトされるたびに自動的にインクリメントされ、現在のゾーンにノードがない場合は、次のゾーンからノードがフェッチされます。左右方向に変化するゾーンインデックスと、トップダウン方向に変化するノードインデックスにより、フェッチされたノードが異なるゾーンに分散されるようになります。これにより、すべてのノードをフィルタリングすることがなくなり、ノードがアベイラビリティゾーンに均等に分散されるようになります。この空間アルゴリズムは、Kubernetes 1.17で削除されました。これは、pod preferやnode preferへの配慮が欠けていることや、pod specに準拠していないことが原因と考えられます。

サンプルスケジューリングにおけるサンプリングスコープは以下の通りです。デフォルトのサンプリングレート＝Max(5, 50 - クラスタ内のノード数/125)。サンプリングスコープ = Max(100, クラスタ内のノード数 x サンプリングレート)。

ノード数3000のクラスタの場合、サンプリングレート=Max(5, 50 - 3000/125) = 26%、サンプリングスコープ=Max(100, 3000 x 0.26) = 780となります。スケジュールパイプラインでは，780個の候補ノードがマッチした時点でフィルタフェーズが終了し，その後スコアフェーズが開始されます。

スコアフェーズでは、スケジューラポリシーで指定された優先度プラグインによってノードがソートされます。スコアが最も高いノードがSelectHostになります。リザーブフェーズでは、SelectHostノードにPodが割り当てられます。このフェーズはアカウント先取りとも呼ばれます。先取りプロセスでは、Podキャッシュ内のPodの状態が Assumed（メモリ状態）に変更されます。

スケジューリングプロセスには、Podのステートマシンのライフサイクルが含まれます。Podは以下のいずれかの状態にあります。Initial（仮想状態）， Assumed（予約状態），Added（追加状態），Delete（仮想状態）。情報提供者がWatch APIを使用して、Podデータがノードに割り当てられていると判断すると、PodはAdded状態に遷移します。選択したノードのバインディングに失敗した場合、ノードはロールバックされます。スケジュールされたPodは Assumed 状態から Initial 状態に戻り、ノードからスケジュール解除されます。

その後、PodはunschedulableQに戻されます。Podは、場合によってはスケジューリングキューからbackoffQに戻されます。backoffQはunschedulableQよりも待ち時間が短く、2の指数乗に上げた劣化値を指定するポリシーを設定します。1回目の再試行に1sかかるとすると、2回目の再試行に2s、3回目の再試行に4s、4回目の再試行に8sかかります。最大リトライ時間は10sです。

#スケジューリングアルゴリズムの実装
###述語(フィルター)
フィルターは用途に応じて4種類に分けられます。

- ストレージに関するフィルタ
- Podとノードのマッチングに関するフィルタ
- Podマッチング用のフィルタ
- Pod配信に関するフィルタ

###ストレージに関連するフィルター
ストレージに関連するフィルタを以下に示します。

- NoVolumeZoneConflictは、PVCに関連するPVのラベルにゾーンを設定して、照合するPodと関連するPVを制限します。
- MaxCSIVolumeCountPredは、PVCで指定されたプロビジョンの単一CSIプラグイン上のPVの最大数をチェックします。
- CheckVolumeBindingPred は、PVC-PV 結合プロセスにおいてロジックチェックを行います。このロジックは複雑で、PV がどのように再利用されるかを決定します。
- NoDiskConfictは、SCSI（Small Computer System Interface）が非繰り返しのボリュームを保存することを保証します。

###Podとノードのマッチング用フィルター
- CheckNodeConditionは、ノードがスケジューリングの準備ができているかどうかをチェックします。具体的には、node.condition.Readyの条件タイプをチェックします。具体的には、node.conditionの条件タイプをチェックします。Readyがtrue、NetworkUnavailableがfalse、Node.Spec.Unschedulableがfalseに設定されていることを確認します。
- CheckNodeUnschedulableは、Kubernetesコントローラによってスケジューリング不可能とマークされたノードに、NodeUnschedulableラベルを割り当てます。このラベルが貼られたノードはスケジュールされません。CheckNodeUnschedulableは、Kubernetes 1.16でtaintに変更されました。Podが汚染されたノードを許容するかどうかをチェックする必要があります。

- PodToleratesNodeTaintsは、Podが汚染されたノードを許容するかどうかをチェックします。
- PodFitsHostPortsは、Podのコンテナで宣言されたポートが、ノードに割り当てられたPodで使用されているかどうかをチェックします。
- MatchNodeSelectorは、Pod.Spec.Affinity.NodeAffinityとPod.Spec.NodeSelectorがノードラベルと一致するかどうかをチェックします。

###Podマッチング用フィルター
MatchinterPodAffinityは、PodのアフィニティとPodのアンチアフィニティをチェックするためのロジックを実装しています。 AffinityのpodAffinityTermは、ノードやゾーンなどのトポロジーで表現できる、サポートされているトポロジーキーを記述しています。このトポロジーキーは、パフォーマンスに大きな影響を与えます。

###Pod分散用フィルター
- EvenPodsSpread
- CheckServiceAffinity

###EvenPodsSpread
EvenPodsSpreadは新機能で、そのspecフィールドには
指定されたトポロジーキーに基づいて、マッチしたPodのグループを配布するための要件を記述します。

Podのグループを記述するには、次のコードを使用します。

```
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    whenUnsatisfiable: DoNotSchedule
    topologyKey: k8s.io/hostname
    selector:
      matchLabels:
        app: foo
      matchExpressions:
      - key: app
        operator: In
        values: ['foo', 'foo2']
```

- topologySpreadConstraintsは、Podが均等に配置されるトポロジーを示します。複数のtopologySpreadConstraintsの間にはAND関係があります。
- selector は、トポロジーに分散させるPodのリストを示します。
- topologyKey は、Podを配布するトポロジーを示します。
- maxSkew は、不均等に分配されるPodの最大数を示します。
- whenUnsatisfiable は、topologySpreadConstraint が満たされない場合に適用されるポリシーを示します。有効値：「DoNotSchedule」はフィルター段階で適用され、「ScheduleAnyway」はスコア段階で適用されます。
以下はその例です。



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/ac4445d2-0391-d9c8-f07c-6391a630c7a1.png)

selectorフィールドは、app=fooラベルを持つすべてのPodを選択します。選択されたPodはゾーンに分散している必要があり、maxSkewは1に設定されています。

クラスタには3つのゾーンがあります。ゾーン1にはapp=fooラベルを持つPodが1つあり、ゾーン2も同様です。

不均等に配置されたPodの数を計算する式は次のとおりです。ActualSkew = count[tempo] - min(count[tempo]) です。

一致したPodのリストが，セレクタフィールドに基づいてフェッチされます。

一致したPodは，topologyKeyフィールドに基づいてグループ化され，count[tempo]が得られます。

前述の図を参照してください。

maxSkewが1に設定されているとします。 Podがゾーン1またはゾーン2に分配される場合、スキュー値は2となり、maxSkew値よりも大きくなります。したがって、PodはZone 3にしか配布できない。Podがゾーン3に分配される場合、min(count[tempo])とcount[tempo]は1に等しく、スキュー値は0であるため、Podはゾーン2にしか分配されません。

maxSkewが2に設定されているとすると、PodをZone 1またはZone 2に分配する場合、スキュー値は2/1/0または1/2/0となり、最大値は2で、maxSkewの値を超えません。したがって、Podをゾーン1、ゾーン2、ゾーン3に分配することができます。

EvenPodsSpreadは、トポロジーキーに基づいてPodを均等に分配するようにします。Podを各トポロジーに均等に配布したい場合は、maxSkewを1に設定することができます。 ただし、この記述は厳密には管理されていません。たとえば、topologyValueでは、Podを配布するトポロジーの数を指定していません。

#優先事項について
次に、クラスタの断片化、ディザスタリカバリ、リソース使用量、アフィニティ、アンチアフィニティを扱うスコアリングアルゴリズムを見てみましょう。

スコアリングアルゴリズムは4つのタイプに分けられます。

- ノードの使用状況
- Podの分散（トポロジー、サービス、コントローラーに関連するもの
- ノードのアフィニティとアンチアフィニティ
- Podの親和性と反親和性

###リソースの使用
リソースの使用状況に関するスコアリングアルゴリズムを紹介します。



![スクリーンショット 2021-05-06 10.58.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/f17f1592-e41c-819c-f137-e316cbece59f.png)


ノード固有の4つのスコアリングアルゴリズムは、前出の図に示すように、リソースの使用状況に関連しています。

![スクリーンショット 2021-05-06 10.59.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/2f8e6367-6e0a-5e02-70c6-ed86375b63c4.png)


- リソース使用量計算式
Requestは、ノードに割り当てられているリソースを示します。

Allocatableは、ノードにスケジュール可能なリソースを示します。

- 優先的な分配
Podは、アイドルリソースが最も多いノードではなく、アイドルリソース率が最も高いノードに割り当てられます。アイドルリソース率＝（Allocatable - Request）／Allocatable。アイドルリソース率が高いノードには高いスコアが与えられるため、優先的にPodが割り当てられます。(Allocatable - Request)は、Podが割り当てられた後のノードのアイドルリソースの数を示します。

- 優先的なスタッキング
Podは、リソース使用量が最も多いノードに割り当てられます。リソース使用量＝Request/Allocatable。リソース使用量が多いノードには高いスコアが与えられ、優先的にPodが割り当てられます。

- フラグメンテーション率

フラグメンテーションレートは、ノード上の複数のリソースの使用率の差を表します。現在、この機能はCPU、メモリー、ディスクの各リソースの統計をサポートしています。CPUとメモリーのみを対象とした場合、フラグメンテーションレートは以下のように算出されます。断片化率＝Abs[CPU(Request/Allocatable) - Mem(Request/Allocatable)]. 例えば、CPUの割り当て率が99％、メモリの割り当て率が50％の場合、フラグメンテーション率は99％-50％＝50％となります。利用可能なCPUリソースの割合は1％、利用可能なメモリリソースの割合は50％です。メモリリソースがコンテナで使い切られることはほとんどありません。指定されたスコアは、1 からフラグメンテーション率を引いて計算されます。フラグメンテーション率が高いほど、スコアは低くなります。

- 規定の比率

Kubernetesスケジューラの起動時に、各リソースの使用率にスコアを設定することができます。このようにして、クラスタ内のノードのリソース割り当てカーブを制御することができます。

###Pod配分


![スクリーンショット 2021-05-06 11.00.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/eab992af-d1e8-5690-72f8-77e2c99d7363.png)


Pod配分機能は、マッチしたPodを展開時に異なるトポロジーに分散させることができます。

- SelectorSpreadPriority
SelectorSpreadPriorityは、コントローラ内のPodをノード間で分散させます。SelectorSpreadPriorityは、割り当てられるPodを管理するコントローラ内のPodの総数を計算します。コントローラ内のPodの総数がTであると仮定して、Podが配置されているノードに基づいて、これらのPodに関する統計を収集します。あるノードの統計値をNとすると、このノードに与えられるスコアは次のように計算されます。(T - N)/T．スコアが高いほど、そのノードに配備されているコントローラの数が少ないことを示します。そして、ノードのワークロードに基づいてノードにPodが割り当てられます。

- ServiceSpreadingPriority
ある公式ソースによると、ServiceSpreadingPriorityがSelectorSpreadPriorityに取って代わる可能性が非常に高いとのことです。私の意見では、ServiceSpreadingPriorityがサービスの均等な割り当てを保証するからです。

- EvenPodsSpreadPriority
EvenPodsSpreadPriorityでは、トポロジーでマッチしたPodのグループを必要に応じて分配する方法を指定できます。

EvenPodsSpreadPriorityは常に変化しているため複雑です。specに基づいてPodをノードに分散する必要があるトポロジーがあるとします。前述の式を使用して、specが示すlabelSelectorに一致するノード上のPodの数を計算することができます。次に、各ノードの最大リソース使用量の差とPod割り当ての重みを計算します。Podは、重みが大きいノードに優先的に割り当てられます。

###Node AffinityとAnti-affinity


![スクリーンショット 2021-05-06 11.01.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/6425bf5a-afe9-52d9-6c14-3577699b2088.png)


- NodeAffinityPriorityは、Podとノードの間のアフィニティとアンチアフィニティを実装しています。
- ServiceAntiAffinityは、ノードラベルの値に基づいて、サービス内のPodが均等に分散されるようにします。例えば、クラスターにオンプレミスとオフプレミスのノードがあり、この2つの環境にサービスを均等に分散させる必要があるとします。Podは、ノードラベルに基づいてServiceAntiAffinityによってノード間で均等に分散させることができます。
- NodeLabelPrioritizerは、指定されたラベルを持つノードに優先的にPodを割り当てます。アルゴリズムはシンプルです。起動すると、NodeLabelPrioritizerは、スケジューラポリシーで定義されたラベル値に一致するノードがあるかどうかを判断します。Podは、ラベルにマッチしたノードに優先的に割り当てられます。
- ImageLocalityPriorityでは、主に画像のダウンロード速度に基づいてノードのアフィニティを決定します。Podは、画像を持つノードに優先的にスケジューリングされます。画像のサイズが考慮され、Podは複数のイメージを持つことができます。大きなイメージはダウンロードに時間がかかります。このPodは、ノード上のイメージのサイズに基づいてアフィニティを指定します。

###Pod AffinityとAnti-affinity
**InterPodAffinityPriority**

まず、以下のシナリオを見てみましょう。

- シナリオ1：アプリケーションAはデータを提供し、アプリケーションBはサービスを提供します。この2つのアプリケーションは、ローカルネットワークを介した通信のために共同で配置することができます。これにより、ネットワーク伝送が最適化されます。
- シナリオ2：アプリケーションAとアプリケーションBの間にCPU負荷の高いアプリケーションが存在し、お互いに干渉している。InterPodAffinityPriorityを設定して、アプリケーションAとアプリケーションBを異なるノードに配置することができます。

**NodePreferAvoidPodsPriority**

NodePreferAvoidPodsPriorityは、特定のコントローラが管理するPodにアノテーションを追加することで、特定のノードに割り当てられることを防ぎます。アノテーションの条件を満たさないノードには、Podが優先的に割り当てられます。

#Kubernetes スケジューラーの設定

###はじめに

![スクリーンショット 2021-05-06 11.02.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/fac97b5a-4897-6b22-5a84-55715b755ec5.png)


Kubernetes スケジューラは、以下のいずれかの方法で起動できます。

- パラメータを設定せず、デフォルトの設定でKubernetesスケジューラを起動します。
- 設定するスケジューリングファイルを指定してKubernetesスケジューラを起動します。
デフォルトの設定でKubernetesスケジューラを起動した場合、以下のように関連するパラメータを確認することができます。デフォルト設定を指定したファイルに書き込むには、--write-config-to を使用します。

デフォルトの設定ファイルは以下の図のようになります。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/b599ecbf-0db8-938b-9b63-fcbad591c0c1.png)





- algorithmSourceはアルゴリズムを3つの形式で提供します。Provider, file, configMapの3つの形式で提供します。
- percentageOfNodesToscoreは、Kubernetesのスケジューラが提供する拡張機能で、ノードで採取するサンプル数を減らすためのものです。
- schedulerNameは、Kubernetesのスケジューラーが起動したときにスケジュールされるPodを示します。指定されていない場合は、デフォルト名のdefault-schedulerが使用されます。
- bindTimeoutSecondsは、バインドフェーズの継続時間（秒）を示します。
- clientConnectionは、Kubernetes APIサーバとの対話のためのパラメータを設定します。例えば、contentTypeは、Kubernetes APIサーバとのやりとりに使用されるシリアル化プロトコルを示します。ここでは、protobufに設定されています。
- disablePreemptionは、プリエンプティブなスケジューリングを無効にするために使用します。
- hardPodAffinitySymnetricweightは、Podアフィニティとノードアフィニティの重みを設定します。

#アルゴリズムソース


![スクリーンショット 2021-05-06 11.04.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/c7491db9-5197-9414-edc8-a0e824e0133e.png)


フィルターアルゴリズムとスコアリングアルゴリズムの設定ファイルには、以下の3つの形式を使用できます。

- Provider
- file
- configMap

Provider形式は，2つの実装モードをサポートしています。

- DefaultProvider
- ClusterAutoscalerProvider


ClusterAutoscalerProviderはスタッキングを優先し、DefaultProviderはディストリビューションを優先します。ノードの自動スケーリングを有効にする場合は、ClusterAutoscalerProviderを使用してください。

ポリシーファイルの構成を以下の図に示します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/3969c153-c1ef-f93b-90ce-b9c17cba46f9.png)



フィルタリングのアルゴリズムは述語で、スコアリングのアルゴリズムは優先順位です。"extender "も設定されています。注意していただきたいのは、alwaysCheckAllPredicatesパラメータです。このパラメータは、フィルタリストで "false "が返されたときに、処理を継続するかどうかを示します。初期値はfalseです。trueに設定すると、すべてのプラグインを横断します。

#KubernetesのSchedulerを拡張する
###Scheduler Extender


![スクリーンショット 2021-05-06 11.05.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/449b3da5-f03e-075d-3151-29c2125d3966.png)


まずはじめに、スケジューラエクステンダでできることをご紹介します。スケジューラエクステンダは、Kubernetesのスケジューラを起動した後に起動できます。

エクステンダーの設定は、ポリシーファイルなどの設定ファイルで行います。設定項目には、エクステンダーのURL、HTTPSサービスの有無、ノードキャッシュの有無などがあります。ノードキャッシュが存在する場合、Kubernetesスケジューラはノード情報をnodenamesリストにのみ送信します。ノードキャッシュが無効な場合、Kubernetes スケジューラは nodeinfo の完全な構造を渡します。

ignorableパラメータは、ネットワークに到達できない場合やサービスエラーが返された場合に、Kubernetesスケジューラがスケジューラエクステンダを無視できるかどうかを示します。 managedResourcesは、指定されたリソースタイプを処理する際にKubernetesスケジューラがスケジューラエクステンダを使用することを示します。このパラメータが指定されていない場合、Kubernetes スケジューラはすべてのリソースタイプに対してスケジューラエクステンダを使用します。

GPUシェアを例にしています。スケジューラエクステンダは、各GPUに割り当てられたメモリのサイズを記録します。Kubernetesのスケジューラは、ノード上のGPUの合計メモリが十分かどうかをチェックします。拡張リソースは example/gpu-men: 200g です。Podをスケジューリングする必要があるとします。拡張リソースは、Kubernetesのスケジューラーで確認できます。拡張リソースは、scheduler extenderによって設定されます。スケジュールフェーズでは、構成されたURLを介してスケジューラエクステンダが呼び出され、KubernetesのスケジューラがGPU共有機能を実装できるようになります。

#スケジューラフレームワーク


![スクリーンショット 2021-05-06 11.06.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/7ae30a00-793f-821d-880d-2038875b66cd.png)


ここでは、拡張ポイントの使い方と同時実行モデルについて説明します。

###拡張ポイントの使い方
拡張ポイントは以下のように使用されます。

- QueueSortはカスタムPodのソートに使用されます。QueueSortのソートアルゴリズムが指定されている場合、スケジューリングキュー内のPodは、このアルゴリズムでソートされます。
- PreFilter は、Podキャッシュ要求など、Pod関連の要求を前処理します。
- Filterでは、カスタムフィルタを追加することができます。たとえば，GPUシェアはカスタムフィルタで実装されます。
- PostFilterは、ログやメトリクスの処理、またはスコアフェーズの前にデータを前処理するために使用されます。PostFilterを通じてキャッシュプラグインを設定することができます。
- Score はスコアリングプラグインで、機能拡張を行います。
- Reserve は、ステートフルなプラグインのメモリ使用量を記録します。
- Permitは、Wait、Deny、Approveのアクションをサポートしており、Gangの挿入ポイントとして使用できます。たとえば、すべてのPodがスケジュールされ、利用可能になるまで待つように設定できます。Podがスケジュールできない場合は、拒否されます。

- PreBindは、ノードがバインドされる前に、ノードにディスクを取り付けるなどの操作を行います。
- Bindは1つのBindPluginで1つのPodを処理します。
- PostBindは、バインディングが成功した後のロジックを実装します。ログやメトリクスに適用されます。
- Unreserveは、PermitからBindまでのいずれかのフェーズでエラーが返された場合のロールバックを実装しています。例えば、PermitやPreBindの段階で失敗した場合、リソースはロールバックされます。

###コンカレンシーモデル
コンカレンシーモデルでは，前図の青色の部分に示すように、メインのスケジューリングプロセスは，PreFilterフェーズからReserveフェーズまで実行されます。メインのスケジューリング処理は，スケジューリングのためにPodがキューからフェッチされたときに始まり、Reserveフェーズが完了したときに終了します。その後，Podは，非同期的にwaitスレッドに転送され、waitスレッドの実行が成功すると，Podはbindスレッドに転送されます。これにより、スレッディングモデルが形成されます。

###カスタムプラグイン
カスタムプラグインの書き方と登録方法について説明します。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/dc4c11ed-0765-7860-9ed0-d2a97176c891.png)




ここでは、公式の例を用いて説明します。Bindフェーズでは、Podを特定のノードにバインドしたい場合、Kubernetes APIサーバーでBind操作を行う必要があります。2つのAPIが呼び出されます。Bind APIでは、スケジューラ名とバインドロジックを宣言します。最後に、ロジックを指定するためのコンストラクタメソッドが実装されています。

カスタムプラグインでスケジューラを起動します。

- vendor
- fork

![スクリーンショット 2021-05-06 11.08.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/b4e15a4f-de33-aa71-72b6-530584c9324c.png)


以下のいずれかの方法で、起動時にスケジューラを登録することができます。

- スクリプトを書き、ベンダーを使ってスケジューラのコードを追加する。スケジューラの起動時にdefaultbinderを登録します。NewSchedulerCommandでスケジューラを起動します。
- Kubernetesのスケジューラのソースコードをフォークし、スケジューラのdefaultbinderをregisterプラグインで登録します。このプラグインを登録した後、スクリプトとイメージをビルドします。設定ファイルの plugins.bind.enable でスケジューラを起動します。

#まとめ
この記事で学んだことをまとめてみましょう。

- 第1部では、[Kubernetes](https://www.alibabacloud.com/product/kubernetes?spm=a2c65.11461447.0.0.61b6ee8bi4qUB2)のスケジューラーの全体的なワークフローと、いくつかのアルゴリズムの最適化について説明しました。
- 第2部では、Kubernetesスケジューラのフィルタおよびスコアコンポーネントの実装について説明し、いくつかのスコアアルゴリズムの使用例を示しました。
- 第3部では、Kubernetesスケジューラの設定ファイルの使用方法を説明しています。これらの設定を通じて、希望するスケジューリング動作を実装することができます。
- 第4部では、特殊なビジネスシナリオにおけるスケジューリング要件を満たすために、スケジューラーエクステンダーやスケジューラーフレームワークによってスケジューリング機能を拡張する方法など、高度な操作について説明しています。



*本ブログは英語版からの翻訳です。オリジナルは[こちら](https://www.alibabacloud.com/blog/getting-started-with-kubernetes-%7C-scheduling-process-and-scheduler-algorithms_596299)からご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。*

*アリババクラウドは日本に２つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
[アリババクラウドジャパン公式ページ](https://www.alibabacloud.com/ja)*