@@ -19,13 +19,13 @@ package benchmark
19
19
import (
20
20
"fmt"
21
21
"io/ioutil"
22
- "sync/atomic"
23
22
"testing"
24
23
"time"
25
24
26
25
v1 "k8s.io/api/core/v1"
27
26
utilfeature "k8s.io/apiserver/pkg/util/feature"
28
- "k8s.io/client-go/tools/cache"
27
+ coreinformers "k8s.io/client-go/informers/core/v1"
28
+ clientset "k8s.io/client-go/kubernetes"
29
29
"k8s.io/component-base/featuregate"
30
30
featuregatetesting "k8s.io/component-base/featuregate/testing"
31
31
"k8s.io/klog"
@@ -39,11 +39,13 @@ const (
39
39
)
40
40
41
41
var (
42
- defaultMetrics = []string {
43
- "scheduler_scheduling_algorithm_predicate_evaluation_seconds" ,
44
- "scheduler_scheduling_algorithm_priority_evaluation_seconds" ,
45
- "scheduler_binding_duration_seconds" ,
46
- "scheduler_e2e_scheduling_duration_seconds" ,
42
+ defaultMetricsCollectorConfig = metricsCollectorConfig {
43
+ Metrics : []string {
44
+ "scheduler_scheduling_algorithm_predicate_evaluation_seconds" ,
45
+ "scheduler_scheduling_algorithm_priority_evaluation_seconds" ,
46
+ "scheduler_binding_duration_seconds" ,
47
+ "scheduler_e2e_scheduling_duration_seconds" ,
48
+ },
47
49
}
48
50
)
49
51
52
54
//
53
55
// It specifies nodes and pods in the cluster before running the test. It also specifies the pods to
54
56
// schedule during the test. The config can be as simple as just specify number of nodes/pods, where
55
- // default spec will be applied. It also allows the user to specify a pod spec template for more compicated
56
- // test cases.
57
+ // default spec will be applied. It also allows the user to specify a pod spec template for more
58
+ // complicated test cases.
57
59
//
58
60
// It also specifies the metrics to be collected after the test. If nothing is specified, default metrics
59
61
// such as scheduling throughput and latencies will be collected.
@@ -68,6 +70,8 @@ type testCase struct {
68
70
PodsToSchedule podCase
69
71
// optional, feature gates to set before running the test
70
72
FeatureGates map [featuregate.Feature ]bool
73
+ // optional, replaces default defaultMetricsCollectorConfig if supplied.
74
+ MetricsCollectorConfig * metricsCollectorConfig
71
75
}
72
76
73
77
type nodeCase struct {
@@ -100,6 +104,11 @@ type testParams struct {
100
104
NumPodsToSchedule int
101
105
}
102
106
107
+ type testDataCollector interface {
108
+ run (stopCh chan struct {})
109
+ collect () []DataItem
110
+ }
111
+
103
112
func BenchmarkPerfScheduling (b * testing.B ) {
104
113
dataItems := DataItems {Version : "v1" }
105
114
tests := getSimpleTestCases (configFile )
@@ -119,119 +128,97 @@ func BenchmarkPerfScheduling(b *testing.B) {
119
128
}
120
129
121
130
func perfScheduling (test testCase , b * testing.B ) []DataItem {
122
- var nodeStrategy testutils.PrepareNodeStrategy = & testutils.TrivialNodePrepareStrategy {}
123
- if test .Nodes .NodeAllocatableStrategy != nil {
124
- nodeStrategy = test .Nodes .NodeAllocatableStrategy
125
- } else if test .Nodes .LabelNodePrepareStrategy != nil {
126
- nodeStrategy = test .Nodes .LabelNodePrepareStrategy
127
- } else if test .Nodes .UniqueNodeLabelStrategy != nil {
128
- nodeStrategy = test .Nodes .UniqueNodeLabelStrategy
129
- }
130
-
131
- setupPodStrategy := getPodStrategy (test .InitPods )
132
- testPodStrategy := getPodStrategy (test .PodsToSchedule )
133
-
134
- var nodeSpec * v1.Node
135
- if test .Nodes .NodeTemplatePath != nil {
136
- nodeSpec = getNodeSpecFromFile (test .Nodes .NodeTemplatePath )
137
- }
138
-
139
131
finalFunc , podInformer , clientset := mustSetupScheduler ()
140
132
defer finalFunc ()
141
133
142
- var nodePreparer testutils.TestNodePreparer
143
- if nodeSpec != nil {
144
- nodePreparer = framework .NewIntegrationTestNodePreparerWithNodeSpec (
145
- clientset ,
146
- []testutils.CountToStrategy {{Count : test .Nodes .Num , Strategy : nodeStrategy }},
147
- nodeSpec ,
148
- )
149
- } else {
150
- nodePreparer = framework .NewIntegrationTestNodePreparer (
151
- clientset ,
152
- []testutils.CountToStrategy {{Count : test .Nodes .Num , Strategy : nodeStrategy }},
153
- "scheduler-perf-" ,
154
- )
155
- }
156
-
134
+ nodePreparer := getNodePreparer (test .Nodes , clientset )
157
135
if err := nodePreparer .PrepareNodes (); err != nil {
158
136
klog .Fatalf ("%v" , err )
159
137
}
160
138
defer nodePreparer .CleanupNodes ()
161
139
162
- config := testutils .NewTestPodCreatorConfig ()
163
- config .AddStrategy (setupNamespace , test .InitPods .Num , setupPodStrategy )
164
- podCreator := testutils .NewTestPodCreator (clientset , config )
165
- podCreator .CreatePods ()
166
-
167
- for {
168
- scheduled , err := getScheduledPods (podInformer )
169
- if err != nil {
170
- klog .Fatalf ("%v" , err )
171
- }
172
- if len (scheduled ) >= test .InitPods .Num {
173
- break
174
- }
175
- klog .Infof ("got %d existing pods, required: %d" , len (scheduled ), test .InitPods .Num )
176
- time .Sleep (1 * time .Second )
177
- }
178
-
179
- scheduled := int32 (0 )
180
- completedCh := make (chan struct {})
181
- podInformer .Informer ().AddEventHandler (cache.ResourceEventHandlerFuncs {
182
- UpdateFunc : func (old , cur interface {}) {
183
- curPod := cur .(* v1.Pod )
184
- oldPod := old .(* v1.Pod )
185
-
186
- if len (oldPod .Spec .NodeName ) == 0 && len (curPod .Spec .NodeName ) > 0 {
187
- if atomic .AddInt32 (& scheduled , 1 ) >= int32 (test .PodsToSchedule .Num ) {
188
- completedCh <- struct {}{}
189
- }
190
- }
191
- },
192
- })
140
+ createPods (setupNamespace , test .InitPods , clientset )
141
+ waitNumPodsScheduled (test .InitPods .Num , podInformer )
193
142
194
143
// start benchmark
195
144
b .ResetTimer ()
196
145
197
- // Start measuring throughput
146
+ // Start test data collectors.
198
147
stopCh := make (chan struct {})
199
- throughputCollector := newThroughputCollector (podInformer )
200
- go throughputCollector .run (stopCh )
148
+ collectors := getTestDataCollectors (test , podInformer , b )
149
+ for _ , collector := range collectors {
150
+ go collector .run (stopCh )
151
+ }
201
152
202
- // Scheduling the main workload
203
- config = testutils .NewTestPodCreatorConfig ()
204
- config .AddStrategy (testNamespace , test .PodsToSchedule .Num , testPodStrategy )
205
- podCreator = testutils .NewTestPodCreator (clientset , config )
206
- podCreator .CreatePods ()
153
+ // Schedule the main workload
154
+ createPods (testNamespace , test .PodsToSchedule , clientset )
155
+ waitNumPodsScheduled (test .InitPods .Num + test .PodsToSchedule .Num , podInformer )
207
156
208
- <- completedCh
209
157
close (stopCh )
210
-
211
158
// Note: without this line we're taking the overhead of defer() into account.
212
159
b .StopTimer ()
213
160
214
- setNameLabel := func (dataItem * DataItem ) DataItem {
215
- if dataItem .Labels == nil {
216
- dataItem .Labels = map [string ]string {}
161
+ var dataItems []DataItem
162
+ for _ , collector := range collectors {
163
+ dataItems = append (dataItems , collector .collect ()... )
164
+ }
165
+ return dataItems
166
+ }
167
+
168
+ func waitNumPodsScheduled (num int , podInformer coreinformers.PodInformer ) {
169
+ for {
170
+ scheduled , err := getScheduledPods (podInformer )
171
+ if err != nil {
172
+ klog .Fatalf ("%v" , err )
173
+ }
174
+ if len (scheduled ) >= num {
175
+ break
217
176
}
218
- dataItem . Labels [ "Name" ] = b . Name ( )
219
- return * dataItem
177
+ klog . Infof ( "got %d existing pods, required: %d" , len ( scheduled ), num )
178
+ time . Sleep ( 1 * time . Second )
220
179
}
180
+ }
221
181
222
- dataItems := []DataItem {
223
- setNameLabel (throughputCollector .collect ()),
182
+ func getTestDataCollectors (tc testCase , podInformer coreinformers.PodInformer , b * testing.B ) []testDataCollector {
183
+ collectors := []testDataCollector {newThroughputCollector (podInformer , map [string ]string {"Name" : b .Name ()})}
184
+ metricsCollectorConfig := defaultMetricsCollectorConfig
185
+ if tc .MetricsCollectorConfig != nil {
186
+ metricsCollectorConfig = * tc .MetricsCollectorConfig
224
187
}
188
+ collectors = append (collectors , newMetricsCollector (metricsCollectorConfig , map [string ]string {"Name" : b .Name ()}))
189
+ return collectors
190
+ }
225
191
226
- for _ , metric := range defaultMetrics {
227
- dataItem := newMetricsCollector (metric ).collect ()
228
- if dataItem == nil {
229
- continue
230
- }
231
- dataItems = append (dataItems , setNameLabel (dataItem ))
192
+ func getNodePreparer (nc nodeCase , clientset clientset.Interface ) testutils.TestNodePreparer {
193
+ var nodeStrategy testutils.PrepareNodeStrategy = & testutils.TrivialNodePrepareStrategy {}
194
+ if nc .NodeAllocatableStrategy != nil {
195
+ nodeStrategy = nc .NodeAllocatableStrategy
196
+ } else if nc .LabelNodePrepareStrategy != nil {
197
+ nodeStrategy = nc .LabelNodePrepareStrategy
198
+ } else if nc .UniqueNodeLabelStrategy != nil {
199
+ nodeStrategy = nc .UniqueNodeLabelStrategy
232
200
}
233
201
234
- return dataItems
202
+ if nc .NodeTemplatePath != nil {
203
+ return framework .NewIntegrationTestNodePreparerWithNodeSpec (
204
+ clientset ,
205
+ []testutils.CountToStrategy {{Count : nc .Num , Strategy : nodeStrategy }},
206
+ getNodeSpecFromFile (nc .NodeTemplatePath ),
207
+ )
208
+ }
209
+ return framework .NewIntegrationTestNodePreparer (
210
+ clientset ,
211
+ []testutils.CountToStrategy {{Count : nc .Num , Strategy : nodeStrategy }},
212
+ "scheduler-perf-" ,
213
+ )
214
+ }
215
+
216
+ func createPods (ns string , pc podCase , clientset clientset.Interface ) {
217
+ strategy := getPodStrategy (pc )
218
+ config := testutils .NewTestPodCreatorConfig ()
219
+ config .AddStrategy (ns , pc .Num , strategy )
220
+ podCreator := testutils .NewTestPodCreator (clientset , config )
221
+ podCreator .CreatePods ()
235
222
}
236
223
237
224
func getPodStrategy (pc podCase ) testutils.TestPodCreateStrategy {
0 commit comments