scheduler_bench_test.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package benchmark
  14. import (
  15. "fmt"
  16. "testing"
  17. "time"
  18. "k8s.io/api/core/v1"
  19. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  20. "k8s.io/apimachinery/pkg/labels"
  21. "k8s.io/kubernetes/test/integration/framework"
  22. testutils "k8s.io/kubernetes/test/utils"
  23. "k8s.io/klog"
  24. )
  25. var (
  26. defaultNodeStrategy = &testutils.TrivialNodePrepareStrategy{}
  27. )
  28. // BenchmarkScheduling benchmarks the scheduling rate when the cluster has
  29. // various quantities of nodes and scheduled pods.
  30. func BenchmarkScheduling(b *testing.B) {
  31. tests := []struct{ nodes, existingPods, minPods int }{
  32. {nodes: 100, existingPods: 0, minPods: 100},
  33. {nodes: 100, existingPods: 1000, minPods: 100},
  34. {nodes: 1000, existingPods: 0, minPods: 100},
  35. {nodes: 1000, existingPods: 1000, minPods: 100},
  36. {nodes: 5000, existingPods: 1000, minPods: 1000},
  37. }
  38. setupStrategy := testutils.NewSimpleWithControllerCreatePodStrategy("rc1")
  39. testStrategy := testutils.NewSimpleWithControllerCreatePodStrategy("rc2")
  40. for _, test := range tests {
  41. name := fmt.Sprintf("%vNodes/%vPods", test.nodes, test.existingPods)
  42. b.Run(name, func(b *testing.B) {
  43. benchmarkScheduling(test.nodes, test.existingPods, test.minPods, defaultNodeStrategy, setupStrategy, testStrategy, b)
  44. })
  45. }
  46. }
  47. // BenchmarkSchedulingPodAntiAffinity benchmarks the scheduling rate of pods with
  48. // PodAntiAffinity rules when the cluster has various quantities of nodes and
  49. // scheduled pods.
  50. func BenchmarkSchedulingPodAntiAffinity(b *testing.B) {
  51. tests := []struct{ nodes, existingPods, minPods int }{
  52. {nodes: 500, existingPods: 250, minPods: 250},
  53. {nodes: 500, existingPods: 5000, minPods: 250},
  54. {nodes: 1000, existingPods: 1000, minPods: 500},
  55. {nodes: 5000, existingPods: 1000, minPods: 1000},
  56. }
  57. // The setup strategy creates pods with no affinity rules.
  58. setupStrategy := testutils.NewSimpleWithControllerCreatePodStrategy("setup")
  59. testBasePod := makeBasePodWithPodAntiAffinity(
  60. map[string]string{"name": "test", "color": "green"},
  61. map[string]string{"color": "green"})
  62. // The test strategy creates pods with anti-affinity for each other.
  63. testStrategy := testutils.NewCustomCreatePodStrategy(testBasePod)
  64. for _, test := range tests {
  65. name := fmt.Sprintf("%vNodes/%vPods", test.nodes, test.existingPods)
  66. b.Run(name, func(b *testing.B) {
  67. benchmarkScheduling(test.nodes, test.existingPods, test.minPods, defaultNodeStrategy, setupStrategy, testStrategy, b)
  68. })
  69. }
  70. }
  71. // BenchmarkSchedulingPodAffinity benchmarks the scheduling rate of pods with
  72. // PodAffinity rules when the cluster has various quantities of nodes and
  73. // scheduled pods.
  74. func BenchmarkSchedulingPodAffinity(b *testing.B) {
  75. tests := []struct{ nodes, existingPods, minPods int }{
  76. {nodes: 500, existingPods: 250, minPods: 250},
  77. {nodes: 500, existingPods: 5000, minPods: 250},
  78. {nodes: 1000, existingPods: 1000, minPods: 500},
  79. {nodes: 5000, existingPods: 1000, minPods: 1000},
  80. }
  81. // The setup strategy creates pods with no affinity rules.
  82. setupStrategy := testutils.NewSimpleWithControllerCreatePodStrategy("setup")
  83. testBasePod := makeBasePodWithPodAffinity(
  84. map[string]string{"foo": ""},
  85. map[string]string{"foo": ""},
  86. )
  87. // The test strategy creates pods with affinity for each other.
  88. testStrategy := testutils.NewCustomCreatePodStrategy(testBasePod)
  89. nodeStrategy := testutils.NewLabelNodePrepareStrategy(v1.LabelZoneFailureDomain, "zone1")
  90. for _, test := range tests {
  91. name := fmt.Sprintf("%vNodes/%vPods", test.nodes, test.existingPods)
  92. b.Run(name, func(b *testing.B) {
  93. benchmarkScheduling(test.nodes, test.existingPods, test.minPods, nodeStrategy, setupStrategy, testStrategy, b)
  94. })
  95. }
  96. }
  97. // BenchmarkSchedulingNodeAffinity benchmarks the scheduling rate of pods with
  98. // NodeAffinity rules when the cluster has various quantities of nodes and
  99. // scheduled pods.
  100. func BenchmarkSchedulingNodeAffinity(b *testing.B) {
  101. tests := []struct{ nodes, existingPods, minPods int }{
  102. {nodes: 500, existingPods: 250, minPods: 250},
  103. {nodes: 500, existingPods: 5000, minPods: 250},
  104. {nodes: 1000, existingPods: 1000, minPods: 500},
  105. {nodes: 5000, existingPods: 1000, minPods: 1000},
  106. }
  107. // The setup strategy creates pods with no affinity rules.
  108. setupStrategy := testutils.NewSimpleWithControllerCreatePodStrategy("setup")
  109. testBasePod := makeBasePodWithNodeAffinity(v1.LabelZoneFailureDomain, []string{"zone1", "zone2"})
  110. // The test strategy creates pods with node-affinity for each other.
  111. testStrategy := testutils.NewCustomCreatePodStrategy(testBasePod)
  112. nodeStrategy := testutils.NewLabelNodePrepareStrategy(v1.LabelZoneFailureDomain, "zone1")
  113. for _, test := range tests {
  114. name := fmt.Sprintf("%vNodes/%vPods", test.nodes, test.existingPods)
  115. b.Run(name, func(b *testing.B) {
  116. benchmarkScheduling(test.nodes, test.existingPods, test.minPods, nodeStrategy, setupStrategy, testStrategy, b)
  117. })
  118. }
  119. }
  120. // makeBasePodWithPodAntiAffinity creates a Pod object to be used as a template.
  121. // The Pod has a PodAntiAffinity requirement against pods with the given labels.
  122. func makeBasePodWithPodAntiAffinity(podLabels, affinityLabels map[string]string) *v1.Pod {
  123. basePod := &v1.Pod{
  124. ObjectMeta: metav1.ObjectMeta{
  125. GenerateName: "anit-affinity-pod-",
  126. Labels: podLabels,
  127. },
  128. Spec: testutils.MakePodSpec(),
  129. }
  130. basePod.Spec.Affinity = &v1.Affinity{
  131. PodAntiAffinity: &v1.PodAntiAffinity{
  132. RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
  133. {
  134. LabelSelector: &metav1.LabelSelector{
  135. MatchLabels: affinityLabels,
  136. },
  137. TopologyKey: v1.LabelHostname,
  138. },
  139. },
  140. },
  141. }
  142. return basePod
  143. }
  144. // makeBasePodWithPodAffinity creates a Pod object to be used as a template.
  145. // The Pod has a PodAffinity requirement against pods with the given labels.
  146. func makeBasePodWithPodAffinity(podLabels, affinityZoneLabels map[string]string) *v1.Pod {
  147. basePod := &v1.Pod{
  148. ObjectMeta: metav1.ObjectMeta{
  149. GenerateName: "affinity-pod-",
  150. Labels: podLabels,
  151. },
  152. Spec: testutils.MakePodSpec(),
  153. }
  154. basePod.Spec.Affinity = &v1.Affinity{
  155. PodAffinity: &v1.PodAffinity{
  156. RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
  157. {
  158. LabelSelector: &metav1.LabelSelector{
  159. MatchLabels: affinityZoneLabels,
  160. },
  161. TopologyKey: v1.LabelZoneFailureDomain,
  162. },
  163. },
  164. },
  165. }
  166. return basePod
  167. }
  168. // makeBasePodWithNodeAffinity creates a Pod object to be used as a template.
  169. // The Pod has a NodeAffinity requirement against nodes with the given expressions.
  170. func makeBasePodWithNodeAffinity(key string, vals []string) *v1.Pod {
  171. basePod := &v1.Pod{
  172. ObjectMeta: metav1.ObjectMeta{
  173. GenerateName: "node-affinity-",
  174. },
  175. Spec: testutils.MakePodSpec(),
  176. }
  177. basePod.Spec.Affinity = &v1.Affinity{
  178. NodeAffinity: &v1.NodeAffinity{
  179. RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
  180. NodeSelectorTerms: []v1.NodeSelectorTerm{
  181. {
  182. MatchExpressions: []v1.NodeSelectorRequirement{
  183. {
  184. Key: key,
  185. Operator: v1.NodeSelectorOpIn,
  186. Values: vals,
  187. },
  188. },
  189. },
  190. },
  191. },
  192. },
  193. }
  194. return basePod
  195. }
  196. // benchmarkScheduling benchmarks scheduling rate with specific number of nodes
  197. // and specific number of pods already scheduled.
  198. // This will schedule numExistingPods pods before the benchmark starts, and at
  199. // least minPods pods during the benchmark.
  200. func benchmarkScheduling(numNodes, numExistingPods, minPods int,
  201. nodeStrategy testutils.PrepareNodeStrategy,
  202. setupPodStrategy, testPodStrategy testutils.TestPodCreateStrategy,
  203. b *testing.B) {
  204. if b.N < minPods {
  205. b.N = minPods
  206. }
  207. schedulerConfigFactory, finalFunc := mustSetupScheduler()
  208. defer finalFunc()
  209. c := schedulerConfigFactory.GetClient()
  210. nodePreparer := framework.NewIntegrationTestNodePreparer(
  211. c,
  212. []testutils.CountToStrategy{{Count: numNodes, Strategy: nodeStrategy}},
  213. "scheduler-perf-",
  214. )
  215. if err := nodePreparer.PrepareNodes(); err != nil {
  216. klog.Fatalf("%v", err)
  217. }
  218. defer nodePreparer.CleanupNodes()
  219. config := testutils.NewTestPodCreatorConfig()
  220. config.AddStrategy("sched-test", numExistingPods, setupPodStrategy)
  221. podCreator := testutils.NewTestPodCreator(c, config)
  222. podCreator.CreatePods()
  223. for {
  224. scheduled, err := schedulerConfigFactory.GetScheduledPodLister().List(labels.Everything())
  225. if err != nil {
  226. klog.Fatalf("%v", err)
  227. }
  228. if len(scheduled) >= numExistingPods {
  229. break
  230. }
  231. time.Sleep(1 * time.Second)
  232. }
  233. // start benchmark
  234. b.ResetTimer()
  235. config = testutils.NewTestPodCreatorConfig()
  236. config.AddStrategy("sched-test", b.N, testPodStrategy)
  237. podCreator = testutils.NewTestPodCreator(c, config)
  238. podCreator.CreatePods()
  239. for {
  240. // This can potentially affect performance of scheduler, since List() is done under mutex.
  241. // TODO: Setup watch on apiserver and wait until all pods scheduled.
  242. scheduled, err := schedulerConfigFactory.GetScheduledPodLister().List(labels.Everything())
  243. if err != nil {
  244. klog.Fatalf("%v", err)
  245. }
  246. if len(scheduled) >= numExistingPods+b.N {
  247. break
  248. }
  249. // Note: This might introduce slight deviation in accuracy of benchmark results.
  250. // Since the total amount of time is relatively large, it might not be a concern.
  251. time.Sleep(100 * time.Millisecond)
  252. }
  253. }