Merge pull request #318 from bskiba/fix-empty

Always add empty nodes to unneeded nodes
This commit is contained in:
Beata Skiba 2017-09-08 16:31:19 +02:00 committed by GitHub
commit 1d10a14aa0
2 changed files with 76 additions and 3 deletions

View File

@ -209,8 +209,22 @@ func (sd *ScaleDown) UpdateUnneededNodes(
currentlyUnneededNodes = append(currentlyUnneededNodes, node) currentlyUnneededNodes = append(currentlyUnneededNodes, node)
} }
emptyNodes := make(map[string]bool)
emptyNodesList := getEmptyNodes(currentlyUnneededNodes, pods, len(currentlyUnneededNodes), sd.context.CloudProvider)
for _, node := range emptyNodesList {
emptyNodes[node.Name] = true
}
currentlyUnneededNonEmptyNodes := make([]*apiv1.Node, 0, len(currentlyUnneededNodes))
for _, node := range currentlyUnneededNodes {
if !emptyNodes[node.Name] {
currentlyUnneededNonEmptyNodes = append(currentlyUnneededNonEmptyNodes, node)
}
}
// Phase2 - check which nodes can be probably removed using fast drain. // Phase2 - check which nodes can be probably removed using fast drain.
currentCandidates, currentNonCandidates := sd.chooseCandidates(currentlyUnneededNodes) currentCandidates, currentNonCandidates := sd.chooseCandidates(currentlyUnneededNonEmptyNodes)
// Look for nodes to remove in the current candidates // Look for nodes to remove in the current candidates
nodesToRemove, unremovable, newHints, simulatorErr := simulator.FindNodesToRemove( nodesToRemove, unremovable, newHints, simulatorErr := simulator.FindNodesToRemove(
@ -249,6 +263,9 @@ func (sd *ScaleDown) UpdateUnneededNodes(
} }
} }
for _, node := range emptyNodesList {
nodesToRemove = append(nodesToRemove, simulator.NodeToBeRemoved{Node: node, PodsToReschedule: []*apiv1.Pod{}})
}
// Update the timestamp map. // Update the timestamp map.
result := make(map[string]time.Time) result := make(map[string]time.Time)
unneededNodesList := make([]*apiv1.Node, 0, len(nodesToRemove)) unneededNodesList := make([]*apiv1.Node, 0, len(nodesToRemove))

View File

@ -97,6 +97,7 @@ func TestFindUnneededNodes(t *testing.T) {
ClusterStateRegistry: clusterstate.NewClusterStateRegistry(provider, clusterstate.ClusterStateRegistryConfig{}, fakeLogRecorder), ClusterStateRegistry: clusterstate.NewClusterStateRegistry(provider, clusterstate.ClusterStateRegistryConfig{}, fakeLogRecorder),
PredicateChecker: simulator.NewTestPredicateChecker(), PredicateChecker: simulator.NewTestPredicateChecker(),
LogRecorder: fakeLogRecorder, LogRecorder: fakeLogRecorder,
CloudProvider: provider,
} }
sd := NewScaleDown(&context) sd := NewScaleDown(&context)
sd.UpdateUnneededNodes([]*apiv1.Node{n1, n2, n3, n4, n5}, []*apiv1.Node{n1, n2, n3, n4, n5}, []*apiv1.Pod{p1, p2, p3, p4}, time.Now(), nil) sd.UpdateUnneededNodes([]*apiv1.Node{n1, n2, n3, n4, n5}, []*apiv1.Node{n1, n2, n3, n4, n5}, []*apiv1.Pod{p1, p2, p3, p4}, time.Now(), nil)
@ -171,6 +172,7 @@ func TestFindUnneededMaxCandidates(t *testing.T) {
ClusterStateRegistry: clusterstate.NewClusterStateRegistry(provider, clusterstate.ClusterStateRegistryConfig{}, fakeLogRecorder), ClusterStateRegistry: clusterstate.NewClusterStateRegistry(provider, clusterstate.ClusterStateRegistryConfig{}, fakeLogRecorder),
PredicateChecker: simulator.NewTestPredicateChecker(), PredicateChecker: simulator.NewTestPredicateChecker(),
LogRecorder: fakeLogRecorder, LogRecorder: fakeLogRecorder,
CloudProvider: provider,
} }
sd := NewScaleDown(&context) sd := NewScaleDown(&context)
@ -201,9 +203,62 @@ func TestFindUnneededMaxCandidates(t *testing.T) {
assert.NotContains(t, sd.unneededNodes, deleted) assert.NotContains(t, sd.unneededNodes, deleted)
} }
func TestFindUnneededAdditionalNodePool(t *testing.T) { func TestFindUnneededEmptyNodes(t *testing.T) {
provider := testprovider.NewTestCloudProvider(nil, nil) provider := testprovider.NewTestCloudProvider(nil, nil)
provider.AddNodeGroup("ng1", 1, 100, 2) provider.AddNodeGroup("ng1", 1, 100, 100)
// 30 empty nodes and 70 heavily underutilized.
numNodes := 100
numEmpty := 30
nodes := make([]*apiv1.Node, 0, numNodes)
for i := 0; i < numNodes; i++ {
n := BuildTestNode(fmt.Sprintf("n%v", i), 1000, 10)
SetNodeReadyState(n, true, time.Time{})
provider.AddNode("ng1", n)
nodes = append(nodes, n)
}
// shared owner reference
ownerRef := GenerateOwnerReferences("rs", "ReplicaSet", "extensions/v1beta1", "")
pods := make([]*apiv1.Pod, 0, numNodes)
for i := 0; i < numNodes-numEmpty; i++ {
p := BuildTestPod(fmt.Sprintf("p%v", i), 100, 0)
p.Spec.NodeName = fmt.Sprintf("n%v", i)
p.OwnerReferences = ownerRef
pods = append(pods, p)
}
fakeClient := &fake.Clientset{}
fakeRecorder := kube_util.CreateEventRecorder(fakeClient)
fakeLogRecorder, _ := utils.NewStatusMapRecorder(fakeClient, "kube-system", fakeRecorder, false)
numCandidates := 30
context := AutoscalingContext{
AutoscalingOptions: AutoscalingOptions{
ScaleDownUtilizationThreshold: 0.35,
ScaleDownNonEmptyCandidatesCount: numCandidates,
ScaleDownCandidatesPoolRatio: 1.0,
ScaleDownCandidatesPoolMinCount: 1000,
},
ClusterStateRegistry: clusterstate.NewClusterStateRegistry(provider, clusterstate.ClusterStateRegistryConfig{}, fakeLogRecorder),
PredicateChecker: simulator.NewTestPredicateChecker(),
LogRecorder: fakeLogRecorder,
CloudProvider: provider,
}
sd := NewScaleDown(&context)
sd.UpdateUnneededNodes(nodes, nodes, pods, time.Now(), nil)
for _, node := range sd.unneededNodesList {
t.Log(node.Name)
}
assert.Equal(t, numEmpty+numCandidates, len(sd.unneededNodes))
}
func TestFindUnneededNodePool(t *testing.T) {
provider := testprovider.NewTestCloudProvider(nil, nil)
provider.AddNodeGroup("ng1", 1, 100, 100)
numNodes := 100 numNodes := 100
nodes := make([]*apiv1.Node, 0, numNodes) nodes := make([]*apiv1.Node, 0, numNodes)
@ -241,6 +296,7 @@ func TestFindUnneededAdditionalNodePool(t *testing.T) {
ClusterStateRegistry: clusterstate.NewClusterStateRegistry(provider, clusterstate.ClusterStateRegistryConfig{}, fakeLogRecorder), ClusterStateRegistry: clusterstate.NewClusterStateRegistry(provider, clusterstate.ClusterStateRegistryConfig{}, fakeLogRecorder),
PredicateChecker: simulator.NewTestPredicateChecker(), PredicateChecker: simulator.NewTestPredicateChecker(),
LogRecorder: fakeLogRecorder, LogRecorder: fakeLogRecorder,
CloudProvider: provider,
} }
sd := NewScaleDown(&context) sd := NewScaleDown(&context)