mirror of
https://github.com/kubernetes-sigs/descheduler.git
synced 2026-01-26 21:31:18 +01:00
chore: add descheduler plugin example
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.
This commit is contained in:
170
pkg/framework/plugins/example/example.go
Normal file
170
pkg/framework/plugins/example/example.go
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user