Don't delete nodes with pods with local storage

This commit is contained in:
Filip Grzadkowski 2016-06-09 17:53:21 +02:00
parent 79687d6b37
commit ca15ccd27a
3 changed files with 81 additions and 8 deletions

View File

@ -17,6 +17,7 @@ limitations under the License.
package simulator package simulator
import ( import (
"flag"
"fmt" "fmt"
"math" "math"
@ -29,6 +30,14 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
) )
var (
skipNodesWithSystemPods = flag.Bool("skip-nodes-with-system-pods", true,
"If true cluster autoscaler will never delete nodes with pods from kube-system (except for DeamonSet "+
"or mirror pods)")
skipNodesWithLocalStorage = flag.Bool("skip-nodes-with-local-storage", true,
"If true cluster autoscaler will never delete nodes with pods with local storage, e.g. EmptyDir or HostPath")
)
// FindNodesToRemove finds nodes that can be removed. // FindNodesToRemove finds nodes that can be removed.
func FindNodesToRemove(candidates []*kube_api.Node, allNodes []*kube_api.Node, pods []*kube_api.Pod, func FindNodesToRemove(candidates []*kube_api.Node, allNodes []*kube_api.Node, pods []*kube_api.Pod,
client *kube_client.Client, predicateChecker *PredicateChecker, maxCount int, client *kube_client.Client, predicateChecker *PredicateChecker, maxCount int,
@ -56,7 +65,7 @@ candidateloop:
if fastCheck { if fastCheck {
if nodeInfo, found := nodeNameToNodeInfo[node.Name]; found { if nodeInfo, found := nodeNameToNodeInfo[node.Name]; found {
podsToRemove, err = FastGetPodsToMove(nodeInfo, false, true, kube_api.Codecs.UniversalDecoder()) podsToRemove, err = FastGetPodsToMove(nodeInfo, false, *skipNodesWithSystemPods, *skipNodesWithLocalStorage, kube_api.Codecs.UniversalDecoder())
if err != nil { if err != nil {
glog.V(2).Infof("%s: node %s cannot be removed: %v", evaluationType, node.Name, err) glog.V(2).Infof("%s: node %s cannot be removed: %v", evaluationType, node.Name, err)
continue candidateloop continue candidateloop

View File

@ -32,7 +32,7 @@ import (
// along with their pods (no abandoned pods with dangling created-by annotation). Usefull for fast // along with their pods (no abandoned pods with dangling created-by annotation). Usefull for fast
// checks. // checks.
func FastGetPodsToMove(nodeInfo *schedulercache.NodeInfo, force bool, func FastGetPodsToMove(nodeInfo *schedulercache.NodeInfo, force bool,
failOnKubeSystemAddons bool, decoder runtime.Decoder) ([]*api.Pod, error) { skipNodesWithSystemPods bool, skipNodesWithLocalStorage bool, decoder runtime.Decoder) ([]*api.Pod, error) {
pods := make([]*api.Pod, 0) pods := make([]*api.Pod, 0)
unreplicatedPodNames := []string{} unreplicatedPodNames := []string{}
for _, pod := range nodeInfo.Pods() { for _, pod := range nodeInfo.Pods() {
@ -61,10 +61,14 @@ func FastGetPodsToMove(nodeInfo *schedulercache.NodeInfo, force bool,
} }
} }
if !daemonsetPod && pod.Namespace == "kube-system" && failOnKubeSystemAddons { if !daemonsetPod && pod.Namespace == "kube-system" && skipNodesWithSystemPods {
return []*api.Pod{}, fmt.Errorf("non-deamons set, non-mirrored, kube-system pod present: %s", pod.Name) return []*api.Pod{}, fmt.Errorf("non-deamons set, non-mirrored, kube-system pod present: %s", pod.Name)
} }
if !daemonsetPod && hasLocalStorage(pod) && skipNodesWithLocalStorage {
return []*api.Pod{}, fmt.Errorf("pod with local storage present: %s", pod.Name)
}
switch { switch {
case daemonsetPod: case daemonsetPod:
break break
@ -82,3 +86,16 @@ func FastGetPodsToMove(nodeInfo *schedulercache.NodeInfo, force bool,
} }
return pods, nil return pods, nil
} }
func hasLocalStorage(pod *api.Pod) bool {
for _, volume := range pod.Spec.Volumes {
if isLocalVolume(&volume) {
return true
}
}
return false
}
func isLocalVolume(volume *api.Volume) bool {
return volume.HostPath != nil || volume.EmptyDir != nil
}

View File

@ -35,7 +35,7 @@ func TestFastGetPodsToMove(t *testing.T) {
Namespace: "ns", Namespace: "ns",
}, },
} }
_, err := FastGetPodsToMove(schedulercache.NewNodeInfo(pod1), false, true, kube_api.Codecs.UniversalDecoder()) _, err := FastGetPodsToMove(schedulercache.NewNodeInfo(pod1), false, true, true, kube_api.Codecs.UniversalDecoder())
assert.Error(t, err) assert.Error(t, err)
// Replicated pod // Replicated pod
@ -48,7 +48,7 @@ func TestFastGetPodsToMove(t *testing.T) {
}, },
}, },
} }
r2, err := FastGetPodsToMove(schedulercache.NewNodeInfo(pod2), false, true, kube_api.Codecs.UniversalDecoder()) r2, err := FastGetPodsToMove(schedulercache.NewNodeInfo(pod2), false, true, true, kube_api.Codecs.UniversalDecoder())
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, 1, len(r2)) assert.Equal(t, 1, len(r2))
assert.Equal(t, pod2, r2[0]) assert.Equal(t, pod2, r2[0])
@ -63,7 +63,7 @@ func TestFastGetPodsToMove(t *testing.T) {
}, },
}, },
} }
r3, err := FastGetPodsToMove(schedulercache.NewNodeInfo(pod3), false, true, kube_api.Codecs.UniversalDecoder()) r3, err := FastGetPodsToMove(schedulercache.NewNodeInfo(pod3), false, true, true, kube_api.Codecs.UniversalDecoder())
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, 0, len(r3)) assert.Equal(t, 0, len(r3))
@ -77,7 +77,7 @@ func TestFastGetPodsToMove(t *testing.T) {
}, },
}, },
} }
r4, err := FastGetPodsToMove(schedulercache.NewNodeInfo(pod2, pod3, pod4), false, true, kube_api.Codecs.UniversalDecoder()) r4, err := FastGetPodsToMove(schedulercache.NewNodeInfo(pod2, pod3, pod4), false, true, true, kube_api.Codecs.UniversalDecoder())
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, 1, len(r4)) assert.Equal(t, 1, len(r4))
assert.Equal(t, pod2, r4[0]) assert.Equal(t, pod2, r4[0])
@ -92,6 +92,53 @@ func TestFastGetPodsToMove(t *testing.T) {
}, },
}, },
} }
_, err = FastGetPodsToMove(schedulercache.NewNodeInfo(pod5), false, true, kube_api.Codecs.UniversalDecoder()) _, err = FastGetPodsToMove(schedulercache.NewNodeInfo(pod5), false, true, true, kube_api.Codecs.UniversalDecoder())
assert.Error(t, err) assert.Error(t, err)
// Local storage
pod6 := &kube_api.Pod{
ObjectMeta: kube_api.ObjectMeta{
Name: "pod6",
Namespace: "ns",
Annotations: map[string]string{
"kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicaSet\"}}",
},
},
Spec: kube_api.PodSpec{
Volumes: []kube_api.Volume{
{
VolumeSource: kube_api.VolumeSource{
EmptyDir: &kube_api.EmptyDirVolumeSource{},
},
},
},
},
}
_, err = FastGetPodsToMove(schedulercache.NewNodeInfo(pod6), false, true, true, kube_api.Codecs.UniversalDecoder())
assert.Error(t, err)
// Non-local storage
pod7 := &kube_api.Pod{
ObjectMeta: kube_api.ObjectMeta{
Name: "pod7",
Namespace: "ns",
Annotations: map[string]string{
"kubernetes.io/created-by": "{\"kind\":\"SerializedReference\",\"apiVersion\":\"v1\",\"reference\":{\"kind\":\"ReplicaSet\"}}",
},
},
Spec: kube_api.PodSpec{
Volumes: []kube_api.Volume{
{
VolumeSource: kube_api.VolumeSource{
GitRepo: &kube_api.GitRepoVolumeSource{
Repository: "my-repo",
},
},
},
},
},
}
r7, err := FastGetPodsToMove(schedulercache.NewNodeInfo(pod7), false, true, true, kube_api.Codecs.UniversalDecoder())
assert.NoError(t, err)
assert.Equal(t, 1, len(r7))
} }