forked from kubernetes/kubernetes
-
Notifications
You must be signed in to change notification settings - Fork 1
/
generic_scheduler.go
204 lines (179 loc) · 6.15 KB
/
generic_scheduler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/*
Copyright 2014 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package scheduler
import (
"fmt"
"math/rand"
"sort"
"strings"
"sync"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
type FailedPredicateMap map[string]util.StringSet
type FitError struct {
Pod *api.Pod
FailedPredicates FailedPredicateMap
}
// implementation of the error interface
func (f *FitError) Error() string {
output := fmt.Sprintf("failed to find fit for pod: %v", f.Pod)
for node, predicateList := range f.FailedPredicates {
output = output + fmt.Sprintf("Node %s: %s", node, strings.Join(predicateList.List(), ","))
}
return output
}
type genericScheduler struct {
predicates map[string]FitPredicate
prioritizers []PriorityConfig
pods PodLister
random *rand.Rand
randomLock sync.Mutex
}
func (g *genericScheduler) Schedule(pod *api.Pod, minionLister MinionLister) (string, error) {
minions, err := minionLister.List()
if err != nil {
return "", err
}
if len(minions.Items) == 0 {
return "", fmt.Errorf("no minions available to schedule pods")
}
filteredNodes, failedPredicateMap, err := findNodesThatFit(pod, g.pods, g.predicates, minions)
if err != nil {
return "", err
}
priorityList, err := prioritizeNodes(pod, g.pods, g.prioritizers, FakeMinionLister(filteredNodes))
if err != nil {
return "", err
}
if len(priorityList) == 0 {
return "", &FitError{
Pod: pod,
FailedPredicates: failedPredicateMap,
}
}
return g.selectHost(priorityList)
}
// This method takes a prioritized list of minions and sorts them in reverse order based on scores
// and then picks one randomly from the minions that had the highest score
func (g *genericScheduler) selectHost(priorityList HostPriorityList) (string, error) {
if len(priorityList) == 0 {
return "", fmt.Errorf("empty priorityList")
}
sort.Sort(sort.Reverse(priorityList))
hosts := getBestHosts(priorityList)
g.randomLock.Lock()
defer g.randomLock.Unlock()
ix := g.random.Int() % len(hosts)
return hosts[ix], nil
}
// Filters the minions to find the ones that fit based on the given predicate functions
// Each minion is passed through the predicate functions to determine if it is a fit
func findNodesThatFit(pod *api.Pod, podLister PodLister, predicates map[string]FitPredicate, nodes api.NodeList) (api.NodeList, FailedPredicateMap, error) {
filtered := []api.Node{}
machineToPods, err := MapPodsToMachines(podLister)
failedPredicateMap := FailedPredicateMap{}
if err != nil {
return api.NodeList{}, FailedPredicateMap{}, err
}
for _, node := range nodes.Items {
fits := true
for name, predicate := range predicates {
fit, err := predicate(pod, machineToPods[node.Name], node.Name)
if err != nil {
return api.NodeList{}, FailedPredicateMap{}, err
}
if !fit {
fits = false
if _, found := failedPredicateMap[node.Name]; !found {
failedPredicateMap[node.Name] = util.StringSet{}
}
failedPredicateMap[node.Name].Insert(name)
break
}
}
if fits {
filtered = append(filtered, node)
}
}
return api.NodeList{Items: filtered}, failedPredicateMap, nil
}
// Prioritizes the minions by running the individual priority functions sequentially.
// Each priority function is expected to set a score of 0-10
// 0 is the lowest priority score (least preferred minion) and 10 is the highest
// Each priority function can also have its own weight
// The minion scores returned by the priority function are multiplied by the weights to get weighted scores
// All scores are finally combined (added) to get the total weighted scores of all minions
func prioritizeNodes(pod *api.Pod, podLister PodLister, priorityConfigs []PriorityConfig, minionLister MinionLister) (HostPriorityList, error) {
result := HostPriorityList{}
// If no priority configs are provided, then the EqualPriority function is applied
// This is required to generate the priority list in the required format
if len(priorityConfigs) == 0 {
return EqualPriority(pod, podLister, minionLister)
}
combinedScores := map[string]int{}
for _, priorityConfig := range priorityConfigs {
weight := priorityConfig.Weight
// skip the priority function if the weight is specified as 0
if weight == 0 {
continue
}
priorityFunc := priorityConfig.Function
prioritizedList, err := priorityFunc(pod, podLister, minionLister)
if err != nil {
return HostPriorityList{}, err
}
for _, hostEntry := range prioritizedList {
combinedScores[hostEntry.host] += hostEntry.score * weight
}
}
for host, score := range combinedScores {
result = append(result, HostPriority{host: host, score: score})
}
return result, nil
}
func getBestHosts(list HostPriorityList) []string {
result := []string{}
for _, hostEntry := range list {
if hostEntry.score == list[0].score {
result = append(result, hostEntry.host)
} else {
break
}
}
return result
}
// EqualPriority is a prioritizer function that gives an equal weight of one to all nodes
func EqualPriority(_ *api.Pod, podLister PodLister, minionLister MinionLister) (HostPriorityList, error) {
nodes, err := minionLister.List()
if err != nil {
fmt.Errorf("failed to list nodes: %v", err)
return []HostPriority{}, err
}
result := []HostPriority{}
for _, minion := range nodes.Items {
result = append(result, HostPriority{
host: minion.Name,
score: 1,
})
}
return result, nil
}
func NewGenericScheduler(predicates map[string]FitPredicate, prioritizers []PriorityConfig, pods PodLister, random *rand.Rand) Scheduler {
return &genericScheduler{
predicates: predicates,
prioritizers: prioritizers,
pods: pods,
random: random,
}
}