Skip to content

Commit 2280569

Browse files
committed
Add NodeMetadata score plugin
Signed-off-by: Alik Khilazhev <7482065+alikhil@users.noreply.github.com>
1 parent 31b11f1 commit 2280569

File tree

9 files changed

+1370
-0
lines changed

9 files changed

+1370
-0
lines changed

apis/config/register.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ func addKnownTypes(scheme *runtime.Scheme) error {
4545
&NetworkOverheadArgs{},
4646
&SySchedArgs{},
4747
&PeaksArgs{},
48+
&NodeMetadataArgs{},
4849
)
4950
return nil
5051
}

apis/config/types.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,3 +298,69 @@ type PowerModel struct {
298298
// Power = K0 + K1 * e ^(K2 * x) : where x is utilisation
299299
// Idle power of node will be K0 + K1
300300
}
301+
302+
// MetadataSourceType defines where to look for metadata
303+
type MetadataSourceType string
304+
305+
const (
306+
// MetadataSourceLabel indicates metadata should be read from node labels
307+
MetadataSourceLabel MetadataSourceType = "Label"
308+
// MetadataSourceAnnotation indicates metadata should be read from node annotations
309+
MetadataSourceAnnotation MetadataSourceType = "Annotation"
310+
)
311+
312+
// MetadataValueType defines the type of metadata value
313+
type MetadataValueType string
314+
315+
const (
316+
// MetadataTypeNumber indicates the metadata value is a numeric value
317+
MetadataTypeNumber MetadataValueType = "Number"
318+
// MetadataTypeTimestamp indicates the metadata value is a timestamp
319+
MetadataTypeTimestamp MetadataValueType = "Timestamp"
320+
)
321+
322+
// MetadataScoringStrategy defines how to score nodes based on metadata values
323+
type MetadataScoringStrategy string
324+
325+
const (
326+
// ScoringStrategyHighest favors nodes with highest numeric values
327+
ScoringStrategyHighest MetadataScoringStrategy = "Highest"
328+
// ScoringStrategyLowest favors nodes with lowest numeric values
329+
ScoringStrategyLowest MetadataScoringStrategy = "Lowest"
330+
// ScoringStrategyNewest favors nodes with newest (most recent) timestamps
331+
ScoringStrategyNewest MetadataScoringStrategy = "Newest"
332+
// ScoringStrategyOldest favors nodes with oldest timestamps
333+
ScoringStrategyOldest MetadataScoringStrategy = "Oldest"
334+
)
335+
336+
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
337+
338+
// NodeMetadataArgs holds arguments used to configure the NodeMetadata plugin.
339+
type NodeMetadataArgs struct {
340+
metav1.TypeMeta
341+
342+
// MetadataKey is the name of the label or annotation to use for scoring
343+
MetadataKey string `json:"metadataKey"`
344+
345+
// MetadataSource indicates whether to read from labels or annotations
346+
// Valid values: "Label", "Annotation"
347+
MetadataSource MetadataSourceType `json:"metadataSource"`
348+
349+
// MetadataType indicates the type of value in the metadata
350+
// Valid values: "Number", "Timestamp"
351+
MetadataType MetadataValueType `json:"metadataType"`
352+
353+
// ScoringStrategy defines how nodes should be scored
354+
// For Number type: "Highest" or "Lowest"
355+
// For Timestamp type: "Newest" or "Oldest"
356+
ScoringStrategy MetadataScoringStrategy `json:"scoringStrategy"`
357+
358+
// TimestampFormat is the Go time format string for parsing timestamps
359+
// Only used when MetadataType is "Timestamp"
360+
// Default: time.RFC3339 ("2006-01-02T15:04:05Z07:00")
361+
// Examples:
362+
// - RFC3339: "2006-01-02T15:04:05Z07:00"
363+
// - Unix timestamp: Use MetadataType "Number" instead
364+
// - Custom: "2006-01-02 15:04:05"
365+
TimestampFormat string `json:"timestampFormat,omitempty"`
366+
}

apis/config/zz_generated.deepcopy.go

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/scheduler/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"sigs.k8s.io/scheduler-plugins/pkg/coscheduling"
2929
"sigs.k8s.io/scheduler-plugins/pkg/networkaware/networkoverhead"
3030
"sigs.k8s.io/scheduler-plugins/pkg/networkaware/topologicalsort"
31+
"sigs.k8s.io/scheduler-plugins/pkg/nodemetadata"
3132
"sigs.k8s.io/scheduler-plugins/pkg/noderesources"
3233
"sigs.k8s.io/scheduler-plugins/pkg/noderesourcetopology"
3334
"sigs.k8s.io/scheduler-plugins/pkg/podstate"
@@ -53,6 +54,7 @@ func main() {
5354
app.WithPlugin(loadvariationriskbalancing.Name, loadvariationriskbalancing.New),
5455
app.WithPlugin(networkoverhead.Name, networkoverhead.New),
5556
app.WithPlugin(topologicalsort.Name, topologicalsort.New),
57+
app.WithPlugin(nodemetadata.Name, nodemetadata.New),
5658
app.WithPlugin(noderesources.AllocatableName, noderesources.NewAllocatable),
5759
app.WithPlugin(noderesourcetopology.Name, noderesourcetopology.New),
5860
app.WithPlugin(preemptiontoleration.Name, preemptiontoleration.New),
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# Example 1: Nodes with priority labels
2+
---
3+
apiVersion: v1
4+
kind: Node
5+
metadata:
6+
name: high-priority-node
7+
labels:
8+
node.example.com/priority: "100"
9+
kubernetes.io/hostname: high-priority-node
10+
spec:
11+
# ... node spec ...
12+
13+
---
14+
apiVersion: v1
15+
kind: Node
16+
metadata:
17+
name: medium-priority-node
18+
labels:
19+
node.example.com/priority: "50"
20+
kubernetes.io/hostname: medium-priority-node
21+
spec:
22+
# ... node spec ...
23+
24+
---
25+
apiVersion: v1
26+
kind: Node
27+
metadata:
28+
name: low-priority-node
29+
labels:
30+
node.example.com/priority: "25"
31+
kubernetes.io/hostname: low-priority-node
32+
spec:
33+
# ... node spec ...
34+
35+
# Example 2: Nodes with cost annotations
36+
---
37+
apiVersion: v1
38+
kind: Node
39+
metadata:
40+
name: cheap-node
41+
annotations:
42+
node.example.com/hourly-cost: "0.5"
43+
labels:
44+
kubernetes.io/hostname: cheap-node
45+
spec:
46+
# ... node spec ...
47+
48+
---
49+
apiVersion: v1
50+
kind: Node
51+
metadata:
52+
name: expensive-node
53+
annotations:
54+
node.example.com/hourly-cost: "2.5"
55+
labels:
56+
kubernetes.io/hostname: expensive-node
57+
spec:
58+
# ... node spec ...
59+
60+
# Example 3: Nodes with maintenance timestamps
61+
---
62+
apiVersion: v1
63+
kind: Node
64+
metadata:
65+
name: recently-maintained-node
66+
annotations:
67+
node.example.com/last-maintenance: "2025-12-03T10:00:00Z"
68+
labels:
69+
kubernetes.io/hostname: recently-maintained-node
70+
spec:
71+
# ... node spec ...
72+
73+
---
74+
apiVersion: v1
75+
kind: Node
76+
metadata:
77+
name: old-maintenance-node
78+
annotations:
79+
node.example.com/last-maintenance: "2025-11-01T10:00:00Z"
80+
labels:
81+
kubernetes.io/hostname: old-maintenance-node
82+
spec:
83+
# ... node spec ...
84+
85+
# Example 4: Nodes with creation date labels
86+
---
87+
apiVersion: v1
88+
kind: Node
89+
metadata:
90+
name: new-node
91+
labels:
92+
node.example.com/created-at: "2025-12-01"
93+
kubernetes.io/hostname: new-node
94+
spec:
95+
# ... node spec ...
96+
97+
---
98+
apiVersion: v1
99+
kind: Node
100+
metadata:
101+
name: old-stable-node
102+
labels:
103+
node.example.com/created-at: "2025-10-01"
104+
kubernetes.io/hostname: old-stable-node
105+
spec:
106+
# ... node spec ...
107+
108+
# Example 5: Labeling an existing node
109+
# Use kubectl to add labels/annotations:
110+
# kubectl label node <node-name> node.example.com/priority=100
111+
# kubectl annotate node <node-name> node.example.com/hourly-cost=1.5
112+
# kubectl annotate node <node-name> node.example.com/last-maintenance=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
apiVersion: kubescheduler.config.k8s.io/v1
2+
kind: KubeSchedulerConfiguration
3+
profiles:
4+
- schedulerName: default-scheduler
5+
plugins:
6+
score:
7+
enabled:
8+
- name: NodeMetadata
9+
weight: 5 # Adjust weight to balance with other scoring plugins
10+
pluginConfig:
11+
- name: NodeMetadata
12+
args:
13+
# Example 1: Score by numeric label (higher priority nodes preferred)
14+
metadataKey: "node.example.com/priority"
15+
metadataSource: "Label"
16+
metadataType: "Number"
17+
scoringStrategy: "Highest"
18+
19+
---
20+
apiVersion: kubescheduler.config.k8s.io/v1
21+
kind: KubeSchedulerConfiguration
22+
profiles:
23+
- schedulerName: cost-aware-scheduler
24+
plugins:
25+
score:
26+
enabled:
27+
- name: NodeMetadata
28+
weight: 10
29+
pluginConfig:
30+
- name: NodeMetadata
31+
args:
32+
# Example 2: Score by cost (lower cost nodes preferred)
33+
metadataKey: "node.example.com/hourly-cost"
34+
metadataSource: "Annotation"
35+
metadataType: "Number"
36+
scoringStrategy: "Lowest"
37+
38+
---
39+
apiVersion: kubescheduler.config.k8s.io/v1
40+
kind: KubeSchedulerConfiguration
41+
profiles:
42+
- schedulerName: freshness-aware-scheduler
43+
plugins:
44+
score:
45+
enabled:
46+
- name: NodeMetadata
47+
weight: 3
48+
pluginConfig:
49+
- name: NodeMetadata
50+
args:
51+
# Example 3: Score by timestamp (newer nodes preferred)
52+
metadataKey: "node.example.com/last-maintenance"
53+
metadataSource: "Annotation"
54+
metadataType: "Timestamp"
55+
scoringStrategy: "Newest"
56+
timestampFormat: "2006-01-02T15:04:05Z07:00" # RFC3339
57+
58+
---
59+
apiVersion: kubescheduler.config.k8s.io/v1
60+
kind: KubeSchedulerConfiguration
61+
profiles:
62+
- schedulerName: stable-nodes-scheduler
63+
plugins:
64+
score:
65+
enabled:
66+
- name: NodeMetadata
67+
weight: 2
68+
pluginConfig:
69+
- name: NodeMetadata
70+
args:
71+
# Example 4: Score by node age (older nodes preferred for stability)
72+
metadataKey: "node.example.com/created-at"
73+
metadataSource: "Label"
74+
metadataType: "Timestamp"
75+
scoringStrategy: "Oldest"
76+
timestampFormat: "2006-01-02"

0 commit comments

Comments
 (0)