1
0
mirror of https://github.com/kubernetes-sigs/descheduler.git synced 2026-01-26 05:14:13 +01:00
Files
descheduler/pkg/framework/plugins/example/example.go
Ricardo Maraschini 57a04aae9f 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.
2025-02-24 18:36:13 +01:00

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
}