mirror of
https://github.com/kubernetes-sigs/descheduler.git
synced 2026-01-26 05:14:13 +01:00
This commit adds a sample plugin implementation as follow:
This directory provides an example plugin for the Kubernetes Descheduler,
demonstrating how to evict pods based on custom criteria. The plugin targets
pods based on:
* **Name Regex:** Pods matching a specified regular expression.
* **Age:** Pods older than a defined duration.
* **Namespace:** Pods within or outside a given list of namespaces (inclusion
or exclusion).
To incorporate this plugin into your Descheduler build, you must register it
within the Descheduler's plugin registry. Follow these steps:
1. **Register the Plugin:**
* Modify the `pkg/descheduler/setupplugins.go` file.
* Add the following registration line to the end of the
`RegisterDefaultPlugins()` function:
```go
pluginregistry.Register(
example.PluginName,
example.New,
&example.Example{},
&example.ExampleArgs{},
example.ValidateExampleArgs,
example.SetDefaults_Example,
registry,
)
```
2. **Generate Code:**
* If you modify the plugin's code, execute `make gen` before rebuilding the
Descheduler. This ensures generated code is up-to-date.
3. **Rebuild the Descheduler:**
* Build the descheduler with your changes.
Configure the plugin's behavior using the Descheduler's policy configuration.
Here's an example:
```yaml
apiVersion: descheduler/v1alpha2
kind: DeschedulerPolicy
profiles:
- name: LifecycleAndUtilization
plugins:
deschedule:
enabled:
- Example
pluginConfig:
- name: Example
args:
regex: ^descheduler-test.*$
maxAge: 3m
namespaces:
include:
- default
```
- `regex: ^descheduler-test.*$`: Evicts pods whose names match the regular
expression `^descheduler-test.*$`.
- `maxAge: 3m`: Evicts pods older than 3 minutes.
- `namespaces.include: - default`: Evicts pods within the default namespace.
This configuration will cause the plugin to evict pods that meet all three
criteria: matching the `regex`, exceeding the `maxAge`, and residing in the
specified namespace.
171 lines
5.6 KiB
Go
171 lines
5.6 KiB
Go
/*
|
|
Copyright 2025 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package example
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"regexp"
|
|
"time"
|
|
|
|
v1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
|
"k8s.io/klog/v2"
|
|
|
|
fwtypes "sigs.k8s.io/descheduler/pkg/framework/types"
|
|
|
|
"sigs.k8s.io/descheduler/pkg/descheduler/evictions"
|
|
podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod"
|
|
)
|
|
|
|
// PluginName is used when registering the plugin. You need to choose a unique
|
|
// name across all plugins. This name is used to identify the plugin config in
|
|
// the descheduler policy.
|
|
const PluginName = "Example"
|
|
|
|
// We need to ensure that the plugin struct complies with the DeschedulePlugin
|
|
// interface. This prevent unexpected changes that may render this type
|
|
// incompatible.
|
|
var _ fwtypes.DeschedulePlugin = &Example{}
|
|
|
|
// Example is our plugin (implementing the DeschedulePlugin interface). This
|
|
// plugin will evict pods that match a regex and are older than a certain age.
|
|
type Example struct {
|
|
handle fwtypes.Handle
|
|
args *ExampleArgs
|
|
podFilter podutil.FilterFunc
|
|
}
|
|
|
|
// New builds a plugin instance from its arguments. Arguments are passed in as
|
|
// a runtime.Object. Handle is used by plugins to retrieve a kubernetes client
|
|
// set, evictor interface, shared informer factory and other instruments shared
|
|
// across different plugins.
|
|
func New(args runtime.Object, handle fwtypes.Handle) (fwtypes.Plugin, error) {
|
|
// make sure we are receiving the right argument type.
|
|
exampleArgs, ok := args.(*ExampleArgs)
|
|
if !ok {
|
|
return nil, fmt.Errorf("args must be of type ExampleArgs, got %T", args)
|
|
}
|
|
|
|
// we can use the included and excluded namespaces to filter the pods we want
|
|
// to evict.
|
|
var includedNamespaces, excludedNamespaces sets.Set[string]
|
|
if exampleArgs.Namespaces != nil {
|
|
includedNamespaces = sets.New(exampleArgs.Namespaces.Include...)
|
|
excludedNamespaces = sets.New(exampleArgs.Namespaces.Exclude...)
|
|
}
|
|
|
|
// here we create a pod filter that will return only pods that can be
|
|
// evicted (according to the evictor and inside the namespaces we want).
|
|
// NOTE: here we could also add a function to filter out by the regex and
|
|
// age but for sake of the example we are keeping it simple and filtering
|
|
// those out in the Deschedule() function.
|
|
podFilter, err := podutil.NewOptions().
|
|
WithNamespaces(includedNamespaces).
|
|
WithoutNamespaces(excludedNamespaces).
|
|
WithFilter(
|
|
podutil.WrapFilterFuncs(
|
|
handle.Evictor().Filter,
|
|
handle.Evictor().PreEvictionFilter,
|
|
),
|
|
).
|
|
BuildFilterFunc()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error initializing pod filter function: %v", err)
|
|
}
|
|
|
|
return &Example{
|
|
handle: handle,
|
|
podFilter: podFilter,
|
|
args: exampleArgs,
|
|
}, nil
|
|
}
|
|
|
|
// Name returns the plugin name.
|
|
func (d *Example) Name() string {
|
|
return PluginName
|
|
}
|
|
|
|
// Deschedule is the function where most of the logic around eviction is laid
|
|
// down. Here we go through all pods in all nodes and evict the ones that match
|
|
// the regex and are older than the maximum age. This function receives a list
|
|
// of nodes we need to process.
|
|
func (d *Example) Deschedule(ctx context.Context, nodes []*v1.Node) *fwtypes.Status {
|
|
var podsToEvict []*v1.Pod
|
|
logger := klog.FromContext(ctx)
|
|
logger.Info("Example plugin starting descheduling")
|
|
|
|
re, err := regexp.Compile(d.args.Regex)
|
|
if err != nil {
|
|
err = fmt.Errorf("fail to compile regex: %w", err)
|
|
return &fwtypes.Status{Err: err}
|
|
}
|
|
|
|
duration, err := time.ParseDuration(d.args.MaxAge)
|
|
if err != nil {
|
|
err = fmt.Errorf("fail to parse max age: %w", err)
|
|
return &fwtypes.Status{Err: err}
|
|
}
|
|
|
|
// here we create an auxiliar filter to remove all pods that don't
|
|
// match the provided regex or are still too young to be evicted.
|
|
// This filter will be used when we list all pods on a node. This
|
|
// filter here could have been part of the podFilter but we are
|
|
// keeping it separate for the sake of the example.
|
|
filter := func(pod *v1.Pod) bool {
|
|
if !re.MatchString(pod.Name) {
|
|
return false
|
|
}
|
|
deadline := pod.CreationTimestamp.Add(duration)
|
|
return time.Now().After(deadline)
|
|
}
|
|
|
|
// go node by node getting all pods that we can evict.
|
|
for _, node := range nodes {
|
|
// ListAllPodsOnANode is a helper function that retrieves all
|
|
// pods filtering out the ones we can't evict. We merge the
|
|
// default filters with the one we created above.
|
|
pods, err := podutil.ListAllPodsOnANode(
|
|
node.Name,
|
|
d.handle.GetPodsAssignedToNodeFunc(),
|
|
podutil.WrapFilterFuncs(d.podFilter, filter),
|
|
)
|
|
if err != nil {
|
|
err = fmt.Errorf("fail to list pods: %w", err)
|
|
return &fwtypes.Status{Err: err}
|
|
}
|
|
|
|
// as we have already filtered out pods that don't match the
|
|
// regex or are too young we can simply add them all to the
|
|
// eviction list.
|
|
podsToEvict = append(podsToEvict, pods...)
|
|
}
|
|
|
|
// evict all the pods.
|
|
for _, pod := range podsToEvict {
|
|
logger.Info("Example plugin evicting pod", "pod", klog.KObj(pod))
|
|
opts := evictions.EvictOptions{StrategyName: PluginName}
|
|
if err := d.handle.Evictor().Evict(ctx, pod, opts); err != nil {
|
|
logger.Error(err, "unable to evict pod", "pod", klog.KObj(pod))
|
|
}
|
|
}
|
|
|
|
logger.Info("Example plugin finished descheduling")
|
|
return nil
|
|
}
|