balanced_allocation.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. /*
  2. Copyright 2019 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 noderesources
  14. import (
  15. "context"
  16. "fmt"
  17. "math"
  18. v1 "k8s.io/api/core/v1"
  19. "k8s.io/apimachinery/pkg/runtime"
  20. utilfeature "k8s.io/apiserver/pkg/util/feature"
  21. "k8s.io/kubernetes/pkg/features"
  22. framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
  23. )
  24. // BalancedAllocation is a score plugin that calculates the difference between the cpu and memory fraction
  25. // of capacity, and prioritizes the host based on how close the two metrics are to each other.
  26. type BalancedAllocation struct {
  27. handle framework.FrameworkHandle
  28. resourceAllocationScorer
  29. }
  30. var _ = framework.ScorePlugin(&BalancedAllocation{})
  31. // BalancedAllocationName is the name of the plugin used in the plugin registry and configurations.
  32. const BalancedAllocationName = "NodeResourcesBalancedAllocation"
  33. // Name returns name of the plugin. It is used in logs, etc.
  34. func (ba *BalancedAllocation) Name() string {
  35. return BalancedAllocationName
  36. }
  37. // Score invoked at the score extension point.
  38. func (ba *BalancedAllocation) Score(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) {
  39. nodeInfo, err := ba.handle.SnapshotSharedLister().NodeInfos().Get(nodeName)
  40. if err != nil {
  41. return 0, framework.NewStatus(framework.Error, fmt.Sprintf("getting node %q from Snapshot: %v", nodeName, err))
  42. }
  43. // ba.score favors nodes with balanced resource usage rate.
  44. // It should **NOT** be used alone, and **MUST** be used together
  45. // with NodeResourcesLeastAllocated plugin. It calculates the difference between the cpu and memory fraction
  46. // of capacity, and prioritizes the host based on how close the two metrics are to each other.
  47. // Detail: score = 10 - variance(cpuFraction,memoryFraction,volumeFraction)*10. The algorithm is partly inspired by:
  48. // "Wei Huang et al. An Energy Efficient Virtual Machine Placement Algorithm with Balanced
  49. // Resource Utilization"
  50. return ba.score(pod, nodeInfo)
  51. }
  52. // ScoreExtensions of the Score plugin.
  53. func (ba *BalancedAllocation) ScoreExtensions() framework.ScoreExtensions {
  54. return nil
  55. }
  56. // NewBalancedAllocation initializes a new plugin and returns it.
  57. func NewBalancedAllocation(_ *runtime.Unknown, h framework.FrameworkHandle) (framework.Plugin, error) {
  58. return &BalancedAllocation{
  59. handle: h,
  60. resourceAllocationScorer: resourceAllocationScorer{
  61. BalancedAllocationName,
  62. balancedResourceScorer,
  63. defaultRequestedRatioResources,
  64. },
  65. }, nil
  66. }
  67. // todo: use resource weights in the scorer function
  68. func balancedResourceScorer(requested, allocable resourceToValueMap, includeVolumes bool, requestedVolumes int, allocatableVolumes int) int64 {
  69. cpuFraction := fractionOfCapacity(requested[v1.ResourceCPU], allocable[v1.ResourceCPU])
  70. memoryFraction := fractionOfCapacity(requested[v1.ResourceMemory], allocable[v1.ResourceMemory])
  71. // This to find a node which has most balanced CPU, memory and volume usage.
  72. if cpuFraction >= 1 || memoryFraction >= 1 {
  73. // if requested >= capacity, the corresponding host should never be preferred.
  74. return 0
  75. }
  76. if includeVolumes && utilfeature.DefaultFeatureGate.Enabled(features.BalanceAttachedNodeVolumes) && allocatableVolumes > 0 {
  77. volumeFraction := float64(requestedVolumes) / float64(allocatableVolumes)
  78. if volumeFraction >= 1 {
  79. // if requested >= capacity, the corresponding host should never be preferred.
  80. return 0
  81. }
  82. // Compute variance for all the three fractions.
  83. mean := (cpuFraction + memoryFraction + volumeFraction) / float64(3)
  84. variance := float64((((cpuFraction - mean) * (cpuFraction - mean)) + ((memoryFraction - mean) * (memoryFraction - mean)) + ((volumeFraction - mean) * (volumeFraction - mean))) / float64(3))
  85. // Since the variance is between positive fractions, it will be positive fraction. 1-variance lets the
  86. // score to be higher for node which has least variance and multiplying it with 10 provides the scaling
  87. // factor needed.
  88. return int64((1 - variance) * float64(framework.MaxNodeScore))
  89. }
  90. // Upper and lower boundary of difference between cpuFraction and memoryFraction are -1 and 1
  91. // respectively. Multiplying the absolute value of the difference by 10 scales the value to
  92. // 0-10 with 0 representing well balanced allocation and 10 poorly balanced. Subtracting it from
  93. // 10 leads to the score which also scales from 0 to 10 while 10 representing well balanced.
  94. diff := math.Abs(cpuFraction - memoryFraction)
  95. return int64((1 - diff) * float64(framework.MaxNodeScore))
  96. }
  97. func fractionOfCapacity(requested, capacity int64) float64 {
  98. if capacity == 0 {
  99. return 1
  100. }
  101. return float64(requested) / float64(capacity)
  102. }