Skip to content

Commit e2db3c3

Browse files
committed
support binpack policy
1 parent f9f1724 commit e2db3c3

7 files changed

Lines changed: 614 additions & 0 deletions

File tree

installer/helm/chart/volcano/config/volcano-scheduler-ci.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ tiers:
99
- name: predicates
1010
- name: proportion
1111
- name: nodeorder
12+
- name: binpack

installer/helm/chart/volcano/config/volcano-scheduler.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ tiers:
99
- name: predicates
1010
- name: proportion
1111
- name: nodeorder
12+
- name: binpack

installer/volcano-development.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ data:
2222
- name: predicates
2323
- name: proportion
2424
- name: nodeorder
25+
- name: binpack
2526
volcano-scheduler.conf: |
2627
actions: "enqueue, allocate, backfill"
2728
tiers:
@@ -34,6 +35,7 @@ data:
3435
- name: predicates
3536
- name: proportion
3637
- name: nodeorder
38+
- name: binpack
3739
3840
---
3941
apiVersion: v1
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/*
2+
Copyright 2019 The Volcano Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package binpack
18+
19+
import (
20+
"fmt"
21+
"strings"
22+
23+
"github.com/golang/glog"
24+
25+
"k8s.io/api/core/v1"
26+
schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
27+
28+
"volcano.sh/volcano/pkg/scheduler/api"
29+
"volcano.sh/volcano/pkg/scheduler/framework"
30+
)
31+
32+
const (
33+
// PluginName indicates name of volcano scheduler plugin.
34+
PluginName = "binpack"
35+
)
36+
37+
const (
38+
// BinpackWeight is the key for providing Binpack Priority Weight in YAML
39+
BinpackWeight = "binpack.weight"
40+
// BinpackCPU is the key for weight of cpu
41+
BinpackCPU = "binpack.cpu"
42+
// BinpackMemory is the key for memory of cpu
43+
BinpackMemory = "binpack.memory"
44+
45+
// BinpackResources is the key for additional resource key name
46+
BinpackResources = "binpack.resources"
47+
// BinpackResourcesPrefix is the key prefix for additional resource key name
48+
BinpackResourcesPrefix = BinpackResources + "."
49+
50+
resourceFmt = "%s[%d]"
51+
)
52+
53+
type priorityWeight struct {
54+
BinPackingWeight int
55+
BinPackingCPU int
56+
BinPackingMemory int
57+
BinPackingResources map[v1.ResourceName]int
58+
}
59+
60+
func (w *priorityWeight) String() string {
61+
length := 3
62+
if extendLength := len(w.BinPackingResources); extendLength == 0 {
63+
length++
64+
} else {
65+
length += extendLength
66+
}
67+
msg := make([]string, 0, length)
68+
msg = append(msg,
69+
fmt.Sprintf(resourceFmt, BinpackWeight, w.BinPackingWeight),
70+
fmt.Sprintf(resourceFmt, BinpackCPU, w.BinPackingCPU),
71+
fmt.Sprintf(resourceFmt, BinpackMemory, w.BinPackingMemory),
72+
)
73+
74+
if len(w.BinPackingResources) == 0 {
75+
msg = append(msg, "no extend resources.")
76+
} else {
77+
for name, weight := range w.BinPackingResources {
78+
msg = append(msg, fmt.Sprintf(resourceFmt, name, weight))
79+
}
80+
}
81+
return strings.Join(msg, ", ")
82+
}
83+
84+
type binpackPlugin struct {
85+
// Arguments given for the plugin
86+
weight priorityWeight
87+
}
88+
89+
//New function returns prioritizePlugin object
90+
func New(aruguments framework.Arguments) framework.Plugin {
91+
weight := calculateWeight(aruguments)
92+
return &binpackPlugin{weight: weight}
93+
}
94+
95+
func calculateWeight(args framework.Arguments) priorityWeight {
96+
/*
97+
User Should give priorityWeight in this format(binpack.weight, binpack.cpu, binpack.memory).
98+
Support change the weight about cpu, memory and additional resource by arguments.
99+
100+
actions: "enqueue, reclaim, allocate, backfill, preempt"
101+
tiers:
102+
- plugins:
103+
- name: binpack
104+
arguments:
105+
binpack.weight: 10
106+
binpack.cpu: 5
107+
binpack.memory: 1
108+
binpack.resources: nvidia.com/gpu, example.com/foo
109+
binpack.resources.nvidia.com/gpu: 2
110+
binpack.resources.example.com/foo: 3
111+
*/
112+
// Values are initialized to 1.
113+
weight := priorityWeight{
114+
BinPackingWeight: 1,
115+
BinPackingCPU: 1,
116+
BinPackingMemory: 1,
117+
BinPackingResources: make(map[v1.ResourceName]int),
118+
}
119+
120+
// Checks whether binpack.weight is provided or not, if given, modifies the value in weight struct.
121+
args.GetInt(&weight.BinPackingWeight, BinpackWeight)
122+
// Checks whether binpack.cpu is provided or not, if given, modifies the value in weight struct.
123+
args.GetInt(&weight.BinPackingCPU, BinpackCPU)
124+
if weight.BinPackingCPU < 0 {
125+
weight.BinPackingCPU = 1
126+
}
127+
// Checks whether binpack.memory is provided or not, if given, modifies the value in weight struct.
128+
args.GetInt(&weight.BinPackingMemory, BinpackMemory)
129+
if weight.BinPackingMemory < 0 {
130+
weight.BinPackingMemory = 1
131+
}
132+
133+
resourcesStr := args[BinpackResources]
134+
resources := strings.Split(resourcesStr, ",")
135+
for _, resource := range resources {
136+
resource = strings.TrimSpace(resource)
137+
if resource == "" {
138+
continue
139+
}
140+
141+
// binpack.resources.[ResourceName]
142+
resourceKey := BinpackResourcesPrefix + resource
143+
resourceWeight := 1
144+
args.GetInt(&resourceWeight, resourceKey)
145+
if resourceWeight < 0 {
146+
resourceWeight = 1
147+
}
148+
weight.BinPackingResources[v1.ResourceName(resource)] = resourceWeight
149+
}
150+
151+
return weight
152+
}
153+
154+
func (bp *binpackPlugin) Name() string {
155+
return PluginName
156+
}
157+
158+
func (bp *binpackPlugin) OnSessionOpen(ssn *framework.Session) {
159+
glog.V(3).Infof("Enter binpack plugin ...")
160+
if glog.V(3) {
161+
defer func() {
162+
glog.V(3).Infof("Leaving binpack plugin. %s ...", bp.weight.String())
163+
}()
164+
}
165+
166+
nodeOrderFn := func(task *api.TaskInfo, node *api.NodeInfo) (float64, error) {
167+
binPackingScore := BinPackingScore(task, node, bp.weight)
168+
169+
glog.V(4).Infof("Binpack score for Task %s/%s on node %s is: %v", task.Namespace, task.Name, node.Name, binPackingScore)
170+
return binPackingScore, nil
171+
}
172+
if bp.weight.BinPackingWeight != 0 {
173+
ssn.AddNodeOrderFn(bp.Name(), nodeOrderFn)
174+
} else {
175+
glog.Infof("binpack weight is zero, skip node order function")
176+
}
177+
}
178+
179+
func (bp *binpackPlugin) OnSessionClose(ssn *framework.Session) {
180+
}
181+
182+
// BinPackingScore use the best fit polices during scheduling.
183+
// Goals:
184+
// - Schedule Jobs using BestFit Policy using Resource Bin Packing Priority Function
185+
// - Reduce Fragmentation of scarce resources on the Cluster
186+
func BinPackingScore(task *api.TaskInfo, node *api.NodeInfo, weight priorityWeight) float64 {
187+
score := 0.0
188+
weightSum := 0
189+
requested := task.Resreq
190+
allocatable := node.Allocatable
191+
used := node.Used
192+
193+
score += ResourceBinPackingScore(requested.MilliCPU, allocatable.MilliCPU, used.MilliCPU, weight.BinPackingCPU)
194+
weightSum += weight.BinPackingCPU
195+
score += ResourceBinPackingScore(requested.Memory, allocatable.Memory, used.Memory, weight.BinPackingMemory)
196+
weightSum += weight.BinPackingMemory
197+
198+
// All resource with weight should be calculated, because the weightSum need it,
199+
// even the node have no this resource.
200+
for name, weight := range weight.BinPackingResources {
201+
weightSum += weight
202+
score += ResourceBinPackingScore(
203+
requested.ScalarResources[name], allocatable.ScalarResources[name],
204+
used.ScalarResources[name], weight,
205+
)
206+
}
207+
208+
// mapping the result from [0, weightSum] to [0, 10(MaxPriority)]
209+
if weightSum > 0 {
210+
score = score / float64(weightSum)
211+
}
212+
score *= schedulerapi.MaxPriority * float64(weight.BinPackingWeight)
213+
214+
return score
215+
}
216+
217+
// ResourceBinPackingScore calculate the binpack score for resource with provided info
218+
func ResourceBinPackingScore(requested, capacity, used float64, weight int) float64 {
219+
if capacity == 0 || weight == 0 {
220+
return 0
221+
}
222+
223+
usedFinally := requested + used
224+
if usedFinally > capacity {
225+
return 0
226+
}
227+
228+
score := usedFinally * float64(weight) / capacity
229+
return score
230+
}

0 commit comments

Comments
 (0)