mirror of
https://github.com/kubernetes-sigs/descheduler.git
synced 2026-01-26 21:31:18 +01:00
feat: enable pod protection based on storage classes
this commit introduces a new customization on the existing PodsWithPVC
protection. this new customization allow users to make pods that refer
to a given storage class unevictable.
for example, to protect pods referring to `storage-class-0` and
`storage-class-1` this configuration can be used:
```yaml
apiVersion: "descheduler/v1alpha2"
kind: "DeschedulerPolicy"
profiles:
- name: ProfileName
pluginConfig:
- name: "DefaultEvictor"
args:
podProtections:
extraEnabled:
- PodsWithPVC
config:
PodsWithPVC:
protectedStorageClasses:
- name: storage-class-0
- name: storage-class-1
```
changes introduced by this pr:
1. the descheduler starts to observe persistent volume claims.
1. a new api field was introduced to allow per pod protection config.
1. rbac had to be adjusted (+persistentvolumeclaims).
This commit is contained in:
@@ -17,11 +17,13 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
@@ -122,13 +124,67 @@ func applyEffectivePodProtections(d *DefaultEvictor, podProtections []PodProtect
|
||||
applyFailedBarePodsProtection(d, protectionMap)
|
||||
applyLocalStoragePodsProtection(d, protectionMap)
|
||||
applyDaemonSetPodsProtection(d, protectionMap)
|
||||
applyPvcPodsProtection(d, protectionMap)
|
||||
applyPVCPodsProtection(d, protectionMap)
|
||||
applyPodsWithoutPDBProtection(d, protectionMap, handle)
|
||||
applyPodsWithResourceClaimsProtection(d, protectionMap)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// protectedPVCStorageClasses returns the list of storage classes that should
|
||||
// be protected from eviction. If the list is empty or nil then all storage
|
||||
// classes are protected (assuming PodsWithPVC protection is enabled).
|
||||
func protectedPVCStorageClasses(d *DefaultEvictor) []ProtectedStorageClass {
|
||||
protcfg := d.args.PodProtections.Config
|
||||
if protcfg == nil {
|
||||
return nil
|
||||
}
|
||||
scconfig := protcfg.PodsWithPVC
|
||||
if scconfig == nil {
|
||||
return nil
|
||||
}
|
||||
return scconfig.ProtectedStorageClasses
|
||||
}
|
||||
|
||||
// podStorageClasses returns a list of storage classes referred by a pod. We
|
||||
// need this when assessing if a pod should be protected because it refers to a
|
||||
// protected storage class.
|
||||
func podStorageClasses(inf informers.SharedInformerFactory, pod *v1.Pod) ([]string, error) {
|
||||
lister := inf.Core().V1().PersistentVolumeClaims().Lister().PersistentVolumeClaims(
|
||||
pod.Namespace,
|
||||
)
|
||||
|
||||
referred := map[string]bool{}
|
||||
for _, vol := range pod.Spec.Volumes {
|
||||
if vol.PersistentVolumeClaim == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
claim, err := lister.Get(vol.PersistentVolumeClaim.ClaimName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"failed to get persistent volume claim %q/%q: %w",
|
||||
pod.Namespace, vol.PersistentVolumeClaim.ClaimName, err,
|
||||
)
|
||||
}
|
||||
|
||||
// this should never happen as once a pvc is created with a nil
|
||||
// storageClass it is automatically picked up by the default
|
||||
// storage class. By returning an error here we make the pod
|
||||
// protected from eviction.
|
||||
if claim.Spec.StorageClassName == nil || *claim.Spec.StorageClassName == "" {
|
||||
return nil, fmt.Errorf(
|
||||
"failed to resolve storage class for pod %q/%q",
|
||||
pod.Namespace, claim.Name,
|
||||
)
|
||||
}
|
||||
|
||||
referred[*claim.Spec.StorageClassName] = true
|
||||
}
|
||||
|
||||
return slices.Collect(maps.Keys(referred)), nil
|
||||
}
|
||||
|
||||
func applyFailedBarePodsProtection(d *DefaultEvictor, protectionMap map[PodProtection]bool) {
|
||||
isProtectionEnabled := protectionMap[FailedBarePods]
|
||||
if !isProtectionEnabled {
|
||||
@@ -206,16 +262,50 @@ func applyDaemonSetPodsProtection(d *DefaultEvictor, protectionMap map[PodProtec
|
||||
}
|
||||
}
|
||||
|
||||
func applyPvcPodsProtection(d *DefaultEvictor, protectionMap map[PodProtection]bool) {
|
||||
isProtectionEnabled := protectionMap[PodsWithPVC]
|
||||
if isProtectionEnabled {
|
||||
d.constraints = append(d.constraints, func(pod *v1.Pod) error {
|
||||
if utils.IsPodWithPVC(pod) {
|
||||
return fmt.Errorf("pod with PVC is protected against eviction")
|
||||
// applyPVCPodsProtection protects pods that refer to a PVC from eviction. If
|
||||
// the user has specified a list of storage classes to protect then only pods
|
||||
// referring to PVCs of those storage classes are protected.
|
||||
func applyPVCPodsProtection(d *DefaultEvictor, enabledProtections map[PodProtection]bool) {
|
||||
if !enabledProtections[PodsWithPVC] {
|
||||
return
|
||||
}
|
||||
|
||||
// if the user isn't filtering by storage classes we protect all pods
|
||||
// referring to a PVC.
|
||||
protected := protectedPVCStorageClasses(d)
|
||||
if len(protected) == 0 {
|
||||
d.constraints = append(
|
||||
d.constraints,
|
||||
func(pod *v1.Pod) error {
|
||||
if utils.IsPodWithPVC(pod) {
|
||||
return fmt.Errorf("pod with PVC is protected against eviction")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
protectedsc := map[string]bool{}
|
||||
for _, class := range protected {
|
||||
protectedsc[class.Name] = true
|
||||
}
|
||||
|
||||
d.constraints = append(
|
||||
d.constraints, func(pod *v1.Pod) error {
|
||||
classes, err := podStorageClasses(d.handle.SharedInformerFactory(), pod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, class := range classes {
|
||||
if !protectedsc[class] {
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("pod using protected storage class %q", class)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func applyPodsWithoutPDBProtection(d *DefaultEvictor, protectionMap map[PodProtection]bool, handle frameworktypes.Handle) {
|
||||
|
||||
Reference in New Issue
Block a user