1. Kubernetes スケジューリング構成
① スケジューラの設定
kube-scheduler は kube-scheduler の設定ファイルとして設定ファイル用のリソースを提供しており、起動時に --config= でファイルを指定します。 各 Kubernetes バージョンで現在使用されている KubeSchedulerConfiguration は次のとおりです。
1.21 より前のバージョンは v1beta1 を使用します。
バージョン 1.22 は v1beta2 を使用しますが、v1beta1 はそのままです。
バージョン 1.23、1.24、および 1.25 は v1beta3 を使用しますが、v1beta2 は保持し、v1beta1 を削除します。
以下に示すように、これは kubeSchedulerConfiguration の簡単な例であり、kubeconfig には起動パラメーター --kubeconfig と同じ機能があり、kubeSchedulerConfiguration は他のコンポーネントの構成ファイルと似ています (kubeletConfiguration はサービスとして開始される構成ファイルです)。
apiVersion: kubescheduler. config. k8s. io/ v1beta1
kind: KubeSchedulerConfiguration
clientConnection:
kubeconfig: / etc/ srv/ kubernetes/ kube- scheduler/ kubeconfig
–kubeconfig と --config を同時に指定することはできません。 --config を指定すると、他のパラメータは当然失敗します。
②kubeSchedulerConfigurationの使用
構成ファイルを通じて、ユーザーは複数のスケジューラをカスタマイズし、各ステージの拡張ポイントを構成できます。また、プラグインは、これらの拡張ポイントを通じて、スケジューリング コンテキスト全体でのスケジューリング動作を提供します。
以下に示す構成は、拡張ポイントの構成例です (name="*" の場合、拡張ポイントに対応するすべてのプラグインが無効/有効になります)。
apiVersion: kubescheduler. config. k8s. io/ v1beta1
kind: KubeSchedulerConfiguration
profiles:
- plugins:
score:
disabled:
- name: PodTopologySpread
enabled:
- name: MyCustomPluginA
weight: 2
- name: MyCustomPluginB
weight: 1
kubernetes は複数のスケジューラを提供しているため、設定ファイルも当然複数の設定ファイルをサポートしています。プロファイルもリスト形式です。複数の設定リストを指定するだけで済みます。以下は複数の設定ファイルの例です。拡張子が複数ある場合は、ポイント 、およびスケジューラごとに複数の拡張ポイントを構成することもできます。
apiVersion: kubescheduler. config. k8s. io/ v1beta2
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: default- scheduler
plugins:
preScore:
disabled:
- name: '*'
score:
disabled:
- name: '*'
- schedulerName: no- scoring- scheduler
plugins:
preScore:
disabled:
- name: '*'
score:
disabled:
- name: '*'
③スケジューラースケジュールプラグイン
kube-scheduler は、デフォルトでスケジューリング方法として多くのプラグインを提供します。これらのプラグインは、デフォルトで構成されていない場合に有効になります。たとえば、次のとおりです。
ImageLocality: スケジューリングは、コンテナー イメージを持つノードにさらに偏ります。拡張ポイント: スコア。
TaintToleration: 汚染と耐性の機能を実現します。拡張ポイント: フィルター、preScore、スコア。
NodeName: スケジューリング戦略の最も単純なスケジューリング メソッド NodeName を実装します。拡張ポイント: フィルター。
NodePorts: スケジューリングにより、ノード ポートが占有されているかどうかがチェックされます。拡張ポイント: preFilter、filter。
NodeAffinity: ノード アフィニティ関連の機能、拡張ポイント: フィルター、スコアを提供します。
PodTopologySpread: Pod トポロジ ドメインの機能を実現します。拡張ポイント: preFilter、filter、preScore、score。
NodeResourcesFit: このプラグインは、次の 3 つの戦略のいずれかを使用して、Pod によって要求されたすべてのリソースがノードにあるかどうかを確認します。 LeastAllocated (デフォルト) MostAllocated および RequestedToCapacityRatio、拡張ポイント: preFilter、フィルター、スコア。
VolumeBinding: ノードが要求されたボリュームを持っているか、またはバインドできるかどうかを確認します。拡張ポイント: preFilter、filter、reserve、preBind、score。
VolumeRestrictions: ノードにインストールされているボリュームがボリューム プロバイダー固有の制限、拡張ポイント: フィルターを満たしているかどうかを確認します。
VolumeZone: 要求されたボリュームがゾーン要件を満たしているかどうかを確認します。拡張ポイント: フィルター。
InterPodAffinity: Pod 間のアフィニティとアンチアフィニティの機能を実現します。拡張ポイント: preFilter、filter、preScore、score。
PrioritySort: デフォルトの優先順位に基づいて並べ替えを提供します。拡張ポイント: queueSort。
2. kube-schedulerを拡張するにはどうすればよいですか?
初めてスケジューラを書こうと思うと、kube-scheduler を拡張するのは非常に難しいことだと考えるのが一般的ですが、実は Kubernetes の公式ではこれらのことをすでに考えていました。このため、kubernetes ではフレームワークの概念が導入されました。バージョン 1.15. このフレームワークは、スケジューラをより拡張可能にすることを目的としています。
フレームワークは、各拡張ポイントを再定義することでそれをプラグインとして使用し、ユーザーがツリー拡張機能を kube-scheduler に登録できるように登録することをサポートします。
① エントリの定義
スケジューラではカスタマイズが可能ですが、対応する NewSchedulerCommand を参照してプラグインのロジックを実装するだけで済みます。
import (
scheduler "k8s.io/kubernetes/cmd/kube-scheduler/app"
)
func main ( ) {
command : = scheduler. NewSchedulerCommand (
scheduler. WithPlugin ( "example-plugin1" , ExamplePlugin1 ) ,
scheduler. WithPlugin ( "example-plugin2" , ExamplePlugin2 ) )
if err : = command. Execute ( ) ; err != nil {
fmt. Fprintf ( os. Stderr , "%v\n" , err)
os. Exit ( 1 )
}
}
NewSchedulerCommand を使用すると、ツリー外のプラグインの挿入、つまり外部カスタム プラグインの挿入が可能になります。この場合、ソース コードを変更してスケジューラを定義する必要はなく、独自に実装するだけでカスタム スケジューラを完成させることができます。
func WithPlugin ( name string, factory runtime. PluginFactory ) Option {
return func ( registry runtime. Registry ) error {
return registry. Register ( name, factory)
}
}
②プラグインの実装
プラグインの実装では、対応する拡張ポイント インターフェイスを実装するだけで済みます。組み込みプラグイン NodeAffinity は、その構造を観察することで見つけることができます。プラグインの実装では、対応する拡張ポイント抽象インターフェイスを実装します。
プラグイン構造を定義します: Framework.FrameworkHandle は、Kubernetes API とスケジューラーの間の呼び出しに使用されます。構造から、リスター、インフォーマーなどが含まれていることがわかります。このパラメーターも実装する必要があります。
type NodeAffinity struct {
handle framework. FrameworkHandle
}
func ( pl * NodeAffinity ) Score ( ctx context. Context , state * framework. CycleState , pod * v1. Pod , nodeName string) ( int64, * framework. Status ) {
nodeInfo, err : = pl. handle. SnapshotSharedLister ( ) . NodeInfos ( ) . Get ( nodeName)
if err != nil {
return 0 , framework. NewStatus ( framework. Error , fmt. Sprintf ( "getting node %q from Snapshot: %v" , nodeName, err) )
}
node : = nodeInfo. Node ( )
if node == nil {
return 0 , framework. NewStatus ( framework. Error , fmt. Sprintf ( "getting node %q from Snapshot: %v" , nodeName, err) )
}
affinity : = pod. Spec . Affinity
var count int64
if affinity != nil && affinity. NodeAffinity != nil && affinity. NodeAffinity . PreferredDuringSchedulingIgnoredDuringExecution != nil {
for i : = range affinity. NodeAffinity . PreferredDuringSchedulingIgnoredDuringExecution {
preferredSchedulingTerm : = & affinity. NodeAffinity . PreferredDuringSchedulingIgnoredDuringExecution [ i]
if preferredSchedulingTerm. Weight == 0 {
continue
}
nodeSelector, err : = v1helper. NodeSelectorRequirementsAsSelector ( preferredSchedulingTerm. Preference . MatchExpressions )
if err != nil {
return 0 , framework. NewStatus ( framework. Error , err. Error ( ) )
}
if nodeSelector. Matches ( labels. Set ( node. Labels ) ) {
count += int64 ( preferredSchedulingTerm. Weight )
}
}
}
return count, nil
}
最後に、New 関数を実装することで、この拡張機能を登録するメソッドを提供します。この関数は、main.go のツリー プラグインの外としてスケジューラーに挿入できます。
func New ( _ runtime. Object , h framework. FrameworkHandle ) ( framework. Plugin , error) {
return & NodeAffinity {
handle: h} , nil
}
3. ネットワークトラフィックに基づいたスケジューリング
スケジューラー プラグインを拡張する方法についての上記の理解を通じて、トラフィック ベースのスケジューリングの例が以下で完成します。通常、ネットワーク内のノードによって一定期間使用されるネットワーク トラフィックも、非常に一般的な状況です。本番環境。
たとえば、バランスの取れた構成の複数のホストのうち、ホスト A はサービス オーダー スクリプトとして実行され、ホスト B は通常のサービスとして実行されます。オーダーには大量のデータのダウンロードが必要ですが、現時点ではハードウェア リソースが占有されることはほとんどないためです。ポッドがこのノードにスケジュールされている場合、双方のビジネスが影響を受ける可能性があります (フロントエンド エージェントは、ノードの接続数が少なく、多数の接続がスケジュールされると考え、注文スクリプトはネットワーク帯域幅の占有により効率が低下します)。
①環境構成
Kubernetes クラスターには少なくとも 2 つのノードが必要です。
提供されている kubernetes クラスターはすべて、prometheus node_exporter をインストールする必要があります。prometheus node_exporter はクラスターの内部または外部に配置できますが、ここではクラスター外部のものが使用されます。
promQL と client_golang について理解していること 。
この例は大きく次の手順に分かれています。
プラグイン API を定義します。プラグインの名前は NetworkTraffic です。
拡張ポイントを定義します。ここではスコア拡張ポイントが使用され、スコアリング アルゴリズムが定義されます。
スコアを取得する方法を定義します (プロメテウス インジケーターから対応するデータを取得します)。
カスタム スケジューラへのパラメータ入力を定義します。
プロジェクトをクラスターにデプロイします (クラスター内デプロイメントおよびクラスター外デプロイメント)。
この例では、組み込みプラグイン nodeaffinity に従ってコードの記述を完了します。このプラグインを選択した理由は、このプラグインが比較的シンプルであり、必要な目的と基本的に同じであるためです。実際には、他のプラグインも-ins も同じ効果があります。
②エラー処理
プロジェクトの初期化、go mod tinyなどの操作を行うと、次のようなエラーが多数発生します。
go: github. com/ GoogleCloudPlatform / spark- on- k8s- operator@ v0.0 . 0 - 20210307184338 - 1947244ce5f4 requires
k8s. io/ apiextensions- apiserver@ v0.0 . 0 : reading k8s. io/ apiextensions- apiserver/ go. mod at revision v0.0 . 0 : unknown revision v0.0 . 0
この問題は、kubernetes issue #79384 で言及されています。ざっと見ただけでは、この問題が発生した理由は説明されていませんでした。最後に、上司がスクリプトを提供しました。上記の問題が解決できない場合は、スクリプトを直接実行すると、問題が解決されます。普通:
#! / bin/ sh
set - euo pipefail
VERSION = ${
1 #"v" }
if [ - z "$VERSION" ] ; then
echo "Must specify version!"
exit 1
fi
MODS = ( $(
curl - sS https: / / raw. githubusercontent. com/ kubernetes/ kubernetes/ v${
VERSION } / go. mod |
sed - n 's | . * k8s. io/ \( . * \) => . / staging/ src/ k8s. io/ . * | k8s. io/ \1 | p'
) )
for MOD in "${MODS[@]}" ; do
V = $(
go mod download - json "${MOD}@kubernetes-${VERSION}" |
sed - n 's | . * "Version" : "\(.*\)" . * | \1 | p'
)
go mod edit "-replace=${MOD}=${MOD}@${V}"
done
go get "k8s.io/kubernetes@v${VERSION}"
③ プラグインAPIの定義
上記の内容の説明から、プラグインを定義するには、対応する拡張ポイント抽象インターフェイスを実装するだけで済み、プロジェクト ディレクトリ pkg/networtraffic/networktraffice.go を初期化できることがわかります。
プラグイン名と変数を定義します。
const Name = "NetworkTraffic"
var _ = framework. ScorePlugin ( & NetworkTraffic {
} )
type NetworkTraffic struct {
prometheus * PrometheusHandle
handle framework. FrameworkHandle
}
④ 拡張ポイントを定義する
Score 拡張ポイントが選択されているため、対応する抽象化を実現するには、対応するメソッドを定義する必要があります。
func ( n * NetworkTraffic ) Score ( ctx context. Context , state * framework. CycleState , p * corev1. Pod , nodeName string) ( int64, * framework. Status ) {
nodeBandwidth, err : = n. prometheus. GetGauge ( nodeName)
if err != nil {
return 0 , framework. NewStatus ( framework. Error , fmt. Sprintf ( "error getting node bandwidth measure: %s" , err) )
}
bandWidth : = int64 ( nodeBandwidth. Value )
klog. Infof ( "[NetworkTraffic] node '%s' bandwidth: %s" , nodeName, bandWidth)
return bandWidth, nil
}
次に、結果を正規化する必要があります。ソース コードから、Score 拡張ポイントにはこの 1 つのメソッド以上の実装が必要であることがわかります。
parallelize. Until ( ctx, len ( f. scorePlugins) , func ( index int) {
pl : = f. scorePlugins[ index]
nodeScoreList : = pluginToNodeScores[ pl. Name ( ) ]
if pl. ScoreExtensions ( ) == nil {
return
}
status : = f. runScoreExtension ( ctx, pl, state, pod, nodeScoreList)
if ! status. IsSuccess ( ) {
err : = fmt. Errorf ( "normalize score plugin %q failed with error %v" , pl. Name ( ) , status. Message ( ) )
errCh. SendErrorWithCancel ( err, cancel)
return
}
} )
上記のコードから、Score を実装するには ScoreExtensions を実装し、実装されていない場合は直接返す必要があることがわかります。 nodeaffinity の例によれば、このメソッドは拡張ポイント オブジェクト自体を返すだけであり、特定の正規化は NormalizeScore での実際のスコアリング操作であることがわかります。
func ( pl * NodeAffinity ) NormalizeScore ( ctx context. Context , state * framework. CycleState , pod * v1. Pod , scores framework. NodeScoreList ) * framework. Status {
return pluginhelper. DefaultNormalizeScore ( framework. MaxNodeScore , false , scores)
}
func ( pl * NodeAffinity ) ScoreExtensions ( ) framework. ScoreExtensions {
return pl
}
スケジューリング フレームワークでは、実際に操作を実行するメソッドも NormalizeScore() です。
func ( f * frameworkImpl) runScoreExtension ( ctx context. Context , pl framework. ScorePlugin , state * framework. CycleState , pod * v1. Pod , nodeScoreList framework. NodeScoreList ) * framework. Status {
if ! state. ShouldRecordPluginMetrics ( ) {
return pl. ScoreExtensions ( ) . NormalizeScore ( ctx, state, pod, nodeScoreList)
}
startTime : = time. Now ( )
status : = pl. ScoreExtensions ( ) . NormalizeScore ( ctx, state, pod, nodeScoreList)
f. metricsRecorder. observePluginDurationAsync ( scoreExtensionNormalize, pl. Name ( ) , status, metrics. SinceInSeconds ( startTime) )
return status
}
NormalizeScore では、ノードを選択するための特定のアルゴリズムを実装する必要があります。実装されるアルゴリズムの式は、最高のスコア、最高の現在の帯域幅、および最高の帯域幅になります。これにより、より大きな帯域幅を占有するマシンのスコアが低くなります。 たとえば、最高の帯域幅が 200,000 で、現在のノードの帯域幅が 140,000 の場合、ノード スコアは次のようになります。
func ( n * NetworkTraffic ) ScoreExtensions ( ) framework. ScoreExtensions {
return n
}
func ( n * NetworkTraffic ) NormalizeScore ( ctx context. Context , state * framework. CycleState , pod * corev1. Pod , scores framework. NodeScoreList ) * framework. Status {
var higherScore int64
for _, node : = range scores {
if higherScore < node. Score {
higherScore = node. Score
}
}
for i, node : = range scores {
scores[ i] . Score = framework. MaxNodeScore - ( node. Score * 100 / higherScore)
klog. Infof ( "[NetworkTraffic] Nodes final score: %v" , scores)
}
klog. Infof ( "[NetworkTraffic] Nodes final score: %v" , scores)
return nil
}
kubernetes では、最大ノード数が 5000 までサポートされていますが、最大スコアを取得する場合、ループによって多くのパフォーマンスが消費されることになりませんか? 実際、心配する必要はありません。 スケジューラーは、デプロイメント・サイクルの数を決定するパラメータpercentageOfNodesToScoreを提供します。
⑤ プラグイン名の設定
登録時にプラグインを使用するには、名前を設定する必要もあります。
func ( n * NetworkTraffic ) Name ( ) string {
return Name
}
⑥ 渡すパラメータを定義する
ネットワーク プラグインの拡張機能には prometheusHandle もあります。これは、prometheus サーバーを操作してインジケーターを取得するアクションです。 まず、PrometheusHandle 構造を定義する必要があります。
type PrometheusHandle struct {
deviceName string
timeRange time. Duration
ip string
client v1. API
}
この構造では、アクションとインジケーターをクエリする必要がありますが、インジケーターについては、Node のネットワーク トラフィックを取得するための計算方法として、node_network_receive_bytes_total が使用されます。 環境はクラスターの外部にデプロイされるため、ノードのホスト名はなく、promQL を通じて取得されます。ステートメント全体は次のとおりです。
sum_over_time ( node_network_receive_bytes_total{
device= "eth0" } [ 1s] ) * on ( instance) group_left ( nodename) ( node_uname_info{
nodename= "node01" } )
整个 Prometheus 部分如下:
type PrometheusHandle struct {
deviceName string
timeRange time. Duration
ip string
client v1. API
}
func NewProme ( ip, deviceName string, timeRace time. Duration ) * PrometheusHandle {
client, err : = api. NewClient ( api. Config {
Address : ip} )
if err != nil {
klog. Fatalf ( "[NetworkTraffic] FatalError creating prometheus client: %s" , err. Error ( ) )
}
return & PrometheusHandle {
deviceName: deviceName,
ip: ip,
timeRange: timeRace,
client: v1. NewAPI ( client) ,
}
}
func ( p * PrometheusHandle ) GetGauge ( node string) ( * model. Sample , error) {
value, err : = p. query ( fmt. Sprintf ( nodeMeasureQueryTemplate, node, p. deviceName, p. timeRange) )
fmt. Println ( fmt. Sprintf ( nodeMeasureQueryTemplate, p. deviceName, p. timeRange, node) )
if err != nil {
return nil, fmt. Errorf ( "[NetworkTraffic] Error querying prometheus: %w" , err)
}
nodeMeasure : = value. ( model. Vector )
if len ( nodeMeasure) != 1 {
return nil, fmt. Errorf ( "[NetworkTraffic] Invalid response, expected 1 value, got %d" , len ( nodeMeasure) )
}
return nodeMeasure[ 0 ] , nil
}
func ( p * PrometheusHandle ) query ( promQL string) ( model. Value , error) {
results, warnings, err : = p. client. Query ( context. Background ( ) , promQL, time. Now ( ) )
if len ( warnings) > 0 {
klog. Warningf ( "[NetworkTraffic Plugin] Warnings: %v\n" , warnings)
}
return results, err
}
⑦ スケジューラのパラメータを設定する
プロメテウスのアドレス、ネットワークカード名、取得するデータのサイズを指定する必要があるため、全体の構造は以下のようになり、さらにパラメータの構造もArgs形式で名前に従う必要があります。
type NetworkTrafficArgs struct {
IP string `json: "ip" `
DeviceName string `json: "deviceName" `
TimeRange int `json: "timeRange" `
}
このタイプのデータを KubeSchedulerConfiguration で解析できる構造にするためには、APIServer を拡張するときに対応するリソース タイプを拡張するというもう 1 つの手順が必要です。ここで、kubernetes は、KubeSchedulerConfiguration のリソース タイプを拡張するための 2 つのメソッドを提供します。
1 つは、古いバージョンで提供されていた Framework.DecodeInto 関数がこの操作を実行できることです。
func New ( plArgs * runtime. Unknown , handle framework. FrameworkHandle ) ( framework. Plugin , error) {
args : = Args {
}
if err : = framework. DecodeInto ( plArgs, & args) ; err != nil {
return nil, err
}
...
}
もう 1 つの方法は、NodeLabel などで、対応するディープ コピー メソッドを実装することです。
type NodeLabelArgs struct {
metav1. TypeMeta
PresentLabels [ ] string
AbsentLabels [ ] string
PresentLabelsPreference [ ] string
AbsentLabelsPreference [ ] string
}
最後に、それを register に登録します。全体の動作は APIServer を拡張するのと似ています。
func addKnownTypes ( scheme * runtime. Scheme ) error {
scheme. AddKnownTypes ( SchemeGroupVersion ,
& KubeSchedulerConfiguration {
} ,
& Policy {
} ,
& InterPodAffinityArgs {
} ,
& NodeLabelArgs {
} ,
& NodeResourcesFitArgs {
} ,
& PodTopologySpreadArgs {
} ,
& RequestedToCapacityRatioArgs {
} ,
& ServiceAffinityArgs {
} ,
& VolumeBindingArgs {
} ,
& NodeResourcesLeastAllocatedArgs {
} ,
& NodeResourcesMostAllocatedArgs {
} ,
)
scheme. AddKnownTypes ( schema. GroupVersion {
Group : "" , Version : runtime. APIVersionInternal } , & Policy {
} )
return nil
}
ディープ コピー関数やその他のファイルを生成するには、kubernetes コード ベースのスクリプト kubernetes/hack/update-codegen.sh を使用できます。便宜上、ここでは Framework.DecodeInto メソッドを使用します。
⑧ プロジェクト展開
スケジューラーのプロファイルを準備すると、カスタム パラメーターが KubeSchedulerConfiguration のリソース タイプとして認識されることがわかります。
apiVersion: kubescheduler. config. k8s. io/ v1beta1
kind: KubeSchedulerConfiguration
clientConnection:
kubeconfig: / mnt/ d/ src/ go_work/ customScheduler/ scheduler. conf
profiles:
- schedulerName: custom- scheduler
plugins:
score:
enabled:
- name: "NetworkTraffic"
disabled:
- name: "*"
pluginConfig:
- name: "NetworkTraffic"
args:
ip: "http://10.0.0.4:9090"
deviceName: "eth0"
timeRange: 60
クラスター内にデプロイする必要がある場合は、ミラー イメージにパッケージ化できます。
FROM golang: alpine AS builder
MAINTAINER cylon
WORKDIR / scheduler
COPY . / / scheduler
ENV GOPROXY https: / / goproxy. cn, direct
RUN \
sed - i 's / dl- cdn. alpinelinux. org/ mirrors. ustc. edu. cn/ g' / etc/ apk/ repositories && \
apk add upx && \
GOOS = linux GOARCH = amd64 CGO_ENABLED = 0 go build - ldflags "-s -w" - o scheduler main. go && \
upx - 1 scheduler && \
chmod + x scheduler
FROM alpine AS runner
WORKDIR / go/ scheduler
COPY - - from= builder / scheduler/ scheduler .
COPY - - from= builder / scheduler/ scheduler. yaml / etc/
VOLUME [ "./scheduler" ]
クラスター内のデプロイメントに必要なリソースのリスト:
apiVersion: v1
kind: ServiceAccount
metadata:
name: scheduler- sa
namespace: kube- system
- - -
apiVersion: rbac. authorization. k8s. io/ v1
kind: ClusterRoleBinding
metadata:
name: scheduler
subjects:
- kind: ServiceAccount
name: scheduler- sa
namespace: kube- system
roleRef:
kind: ClusterRole
name: system: kube- scheduler
apiGroup: rbac. authorization. k8s. io
- - -
apiVersion: apps/ v1
kind: Deployment
metadata:
name: custom- scheduler
namespace: kube- system
labels:
component: custom- scheduler
spec:
selector:
matchLabels:
component: custom- scheduler
template:
metadata:
labels:
component: custom- scheduler
spec:
serviceAccountName: scheduler- sa
priorityClassName: system- cluster- critical
containers:
- name: scheduler
image: cylonchau/ custom- scheduler: v0.0 . 1
imagePullPolicy: IfNotPresent
command:
- . / scheduler
- - - config= / etc/ scheduler. yaml
- - - v= 3
livenessProbe:
httpGet:
path: / healthz
port: 10251
initialDelaySeconds: 15
readinessProbe:
httpGet:
path: / healthz
port: 10251
カスタム スケジューラを起動します。これはシンプル バイナリ モードで起動されるため、認証ファイルとして kubeconfig が必要です。
$ . / main - - logtostderr= true \
- - address= 127.0 . 0.1 \
- - v= 3 \
- - config= `pwd`/ scheduler. yaml \
- - kubeconfig= `pwd`/ scheduler. conf
起動後、検証の便宜上、元の kube-scheduler サービスは閉じられます。これは、元の kube-scheduler が HA のマスターとして使用されているため、カスタム スケジューラーがポッドの保留を引き起こすために使用されることはありません。
⑨検証結果
使用するスケジューラーの名前を指定して、デプロイする必要があるポッドを準備します。
apiVersion: apps/ v1
kind: Deployment
metadata:
name: nginx- deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx: 1.14 . 2
ports:
- containerPort: 80
schedulerName: custom- scheduler
ここでの実験環境は、マスターとノード 01 の 2 つのノードを持つ kubernetes クラスターです。マスターにはノード 01 より多くのサービスがあるためです。この場合、何があっても、スケジューリング結果は常にノード 01 にスケジュールされます。
$ kubectl get pods - o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx- deployment- 69f76b454c- lpwbl 1 / 1 Running 0 43s 192.168 . 0.17 node01 < none> < none>
nginx- deployment- 69f76b454c- vsb7k 1 / 1 Running 0 43s 192.168 . 0.16 node01 < none> < none>
I0808 01 : 56 : 31.098189 27131 networktraffic. go: 83 ] [ NetworkTraffic ] node 'node01 ' bandwidth: % ! s ( int64= 12541068340 )
I0808 01 : 56 : 31.098461 27131 networktraffic. go: 70 ] [ NetworkTraffic ] Nodes final score: [ {
master- machine 0 } {
node01 12541068340 } ]
I0808 01 : 56 : 31.098651 27131 networktraffic. go: 70 ] [ NetworkTraffic ] Nodes final score: [ {
master- machine 0 } {
node01 71 } ]
I0808 01 : 56 : 31.098911 27131 networktraffic. go: 73 ] [ NetworkTraffic ] Nodes final score: [ {
master- machine 0 } {
node01 71 } ]
I0808 01 : 56 : 31.099275 27131 default_binder. go: 51 ] Attempting to bind default/ nginx- deployment- 69f76b454c- vsb7k to node01
I0808 01 : 56 : 31.101414 27131 eventhandlers. go: 225 ] add event for scheduled pod default/ nginx- deployment- 69f76b454c- lpwbl
I0808 01 : 56 : 31.101414 27131 eventhandlers. go: 205 ] delete event for unscheduled pod default/ nginx- deployment- 69f76b454c- lpwbl
I0808 01 : 56 : 31.103604 27131 scheduler. go: 609 ] "Successfully bound pod to node" pod= "default/nginx-deployment-69f76b454c-lpwbl" node= "no
de01" evaluatedNodes= 2 feasibleNodes= 2
I0808 01 : 56 : 31.104540 27131 scheduler. go: 609 ] "Successfully bound pod to node" pod= "default/nginx-deployment-69f76b454c-vsb7k" node= "no
de01" evaluatedNodes= 2 feasibleNodes= 2