From 8da68695e1d975533af24d4c5ce8ecc3033d71e6 Mon Sep 17 00:00:00 2001 From: Jan Chaloupka Date: Mon, 8 Apr 2024 11:40:15 +0200 Subject: [PATCH] 753 - Descheduling framework --- keps/753-descheduling-framework/README.md | 1211 +++++++++++++++++ .../framework-workflow-diagram.png | Bin 0 -> 64448 bytes keps/753-descheduling-framework/kep.yaml | 29 + 3 files changed, 1240 insertions(+) create mode 100644 keps/753-descheduling-framework/README.md create mode 100644 keps/753-descheduling-framework/framework-workflow-diagram.png create mode 100644 keps/753-descheduling-framework/kep.yaml diff --git a/keps/753-descheduling-framework/README.md b/keps/753-descheduling-framework/README.md new file mode 100644 index 0000000000..ff204dc910 --- /dev/null +++ b/keps/753-descheduling-framework/README.md @@ -0,0 +1,1211 @@ + +# KEP-753: Descheduling framework + + + + + + +- [Release Signoff Checklist](#release-signoff-checklist) +- [Summary](#summary) +- [Motivation](#motivation) + - [Goals](#goals) + - [Non-Goals](#non-goals) +- [Proposal](#proposal) + - [User Stories (Optional)](#user-stories-optional) + - [Story 1](#story-1) + - [Story 2](#story-2) + - [Notes/Constraints/Caveats (Optional)](#notesconstraintscaveats-optional) + - [Risks and Mitigations](#risks-and-mitigations) +- [Design Details](#design-details) + - [Test Plan](#test-plan) + - [Prerequisite testing updates](#prerequisite-testing-updates) + - [Unit tests](#unit-tests) + - [Integration tests](#integration-tests) + - [e2e tests](#e2e-tests) + - [Graduation Criteria](#graduation-criteria) + - [Upgrade / Downgrade Strategy](#upgrade--downgrade-strategy) + - [Version Skew Strategy](#version-skew-strategy) +- [Production Readiness Review Questionnaire](#production-readiness-review-questionnaire) + - [Feature Enablement and Rollback](#feature-enablement-and-rollback) + - [Rollout, Upgrade and Rollback Planning](#rollout-upgrade-and-rollback-planning) + - [Monitoring Requirements](#monitoring-requirements) + - [Dependencies](#dependencies) + - [Scalability](#scalability) + - [Troubleshooting](#troubleshooting) +- [Implementation History](#implementation-history) +- [Drawbacks](#drawbacks) +- [Alternatives](#alternatives) +- [Infrastructure Needed (Optional)](#infrastructure-needed-optional) + + +## Release Signoff Checklist + + + +Items marked with (R) are required *prior to targeting to a milestone / release*. + +- [ ] (R) Enhancement issue in release milestone, which links to KEP dir in [kubernetes/enhancements] (not the initial KEP PR) +- [ ] (R) KEP approvers have approved the KEP status as `implementable` +- [ ] (R) Design details are appropriately documented +- [ ] (R) Test plan is in place, giving consideration to SIG Architecture and SIG Testing input (including test refactors) + - [ ] e2e Tests for all Beta API Operations (endpoints) + - [ ] (R) Ensure GA e2e tests meet requirements for [Conformance Tests](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/conformance-tests.md) + - [ ] (R) Minimum Two Week Window for GA e2e tests to prove flake free +- [ ] (R) Graduation criteria is in place + - [ ] (R) [all GA Endpoints](https://github.com/kubernetes/community/pull/1806) must be hit by [Conformance Tests](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/conformance-tests.md) +- [ ] (R) Production readiness review completed +- [ ] (R) Production readiness review approved +- [ ] "Implementation History" section is up-to-date for milestone +- [ ] User-facing documentation has been created in [kubernetes/website], for publication to [kubernetes.io] +- [ ] Supporting documentation—e.g., additional design documents, links to mailing list discussions/SIG meetings, relevant PRs/issues, release notes + + + +[kubernetes.io]: https://kubernetes.io/ +[kubernetes/enhancements]: https://git.k8s.io/enhancements +[kubernetes/kubernetes]: https://git.k8s.io/kubernetes +[kubernetes/website]: https://git.k8s.io/website + +## Summary + + + +Over the past few years, the Descheduler project has grown considerably both in adoption and contributions. A big part of that growth has been the feature requests of active users. This has helped make it a suitable production option for complex pod eviction logic. This growth has come with regular requests for new features in the Descheduler. The general approach we have taken to addressing new features has usually been to accept and implement any that weren’t strongly opposed. This has led to some excellent improvements in the Descheduler. In fact, this approach has served as the de facto project roadmap as almost every new feature in recent years has been the direct result of a specific user request. + +However, this growth method has started to create some problems that now constrain future growth. Specifically, many new features require additional API updates that expand and contrive the existing API while also adding new maintenance demands. And in some cases, existing features are built with default assumptions that conflict with new features, blocking those new feature requests from progressing (without breaking API or behavior changes). While the Descheduler config API is currently `alpha`, meaning we do technically have the ability to make breaking changes without warning, we have tried not to do this. This is because part of developing the Descheduler’s growth has been to operate within the support constraints of a graduated API (eg, not breaking current defaults) in order to build user trust and establish known use cases for the Descheduler. This feeds toward the end objective of eventually graduating to a stable user-facing API. As such, these two aspects of the Descheduler project (open acceptance of individual features and stable API support) are reaching a point where their coexistence is causing growing pains for the project. + +In addition, the rate at which feature requests (and bug reports) are created outpaces the ability of the active project maintainers to keep up with them. And while many new features are ultimately implemented by the person requesting them (even if they are not actively involved in the Descheduler project), the ownership (and upkeep) of these features eventually fall to the project maintainers. Unfortunately, the ability of the Descheduler maintainers to keep up with new features has not scaled as much as the growth of the project. + +## Motivation + + + +The goal of this proposal is first and foremost to position the Descheduler as a project that is ready to graduate to a stable definition. The current Descheduler project has served well as a proof-of-concept for the adoption of various qualities in production environments. Now, it is time to take what has been learned and apply it toward building a stable eviction logic platform that can be more widely used and trusted. + +We are proposing a collaborative effort to fundamentally redefine the purpose of the Descheduler GitHub repository (and ostensibly, the Descheduler project itself) from that of a centralized “component” to a developer-facing, importable “library”, which we will refer to as the Descheduling Framework. Conceptually, this idea borrows heavily from the Scheduler Framework. But from a technical implementation standpoint, there will likely be many differences between the two frameworks. + +### Goals + + + +* **Focus on the platform**: Consolidate maintainer effort on building a stable platform for writing custom pod eviction logic. +* **API Stability**: Enable third-party developers to implement custom, unique edge-case features without concern for breaking existing behavior that other users may depend on. +* **Enable external development**: Decentralize Descheduler feature development to empower broader diversity of features available to users. +* **Promote ownership of strategies**: Improve feature maintenance by allowing third-party providers to specialize and own their own features, rather than defaulting ownership to the Descheduler maintainers (as is the current case once a new feature merges). The developers of these features know them best, and they deserve full ownership and responsibility for maintaining them. +* **Elevate feature divergence**: Remove the role of “feature gatekeeper” from the Descheduler maintainers to promote a flourishing ecosystem of use cases. + +### Non-Goals + + + +- Break any existing use cases of the Descheduler. We have striven for reliable feature support within the Kubernetes deprecation policies, and it is important not to change that. This refactor should not affect any existing workflows of the Descheduler (including published Descheduler images) + +## Proposal + + + +### User Stories (Optional) + + + +#### Story 1 + +As a descheduler project maintainer I'd like to move maintenance of individual strategies outside the project to strategy owners. So I can focus on the core features of the project by promoting existing functionality to stable and integrating popular enhancements. + +Some of the currently suggested enhancements: + +**Event-triggered Evictions** + +Relevant links: +- https://github.com/kubernetes-sigs/descheduler/pull/488 +- https://github.com/kubernetes-sigs/descheduler/issues/696 + +The Descheduler currently runs on a periodic interval, evicting Pods regularly either as a long-lived Deployment or CronJob. However, the effectiveness of these evictions could be optimized by being more reactive to cluster changes. Triggering evictions based on cluster events is the solution that has been proposed for this. Going hand-in-hand with custom Strategies, this ability will need to be exposed to developers in order to tailor this to their own use cases. + +**Custom Pod Eviction Logic** + +Relevant links: +- https://github.com/kubernetes-sigs/descheduler/issues/690 +- https://github.com/kubernetes-sigs/descheduler/pull/724 + +The Descheduler provides Strategies as the main feature-level logic for eviction algorithms. But it also defines internal types like the [PodEvictor](https://github.com/kubernetes-sigs/descheduler/blob/224e2b0/pkg/descheduler/evictions/evictions.go#L51-L65), [PodLister](https://github.com/kubernetes-sigs/descheduler/blob/224e2b078f62e3537d32caec7733f0918d48c248/pkg/descheduler/pod/pods.go#L165-L175), and [NodeLister](https://github.com/kubernetes-sigs/descheduler/blob/224e2b078f62e3537d32caec7733f0918d48c248/pkg/descheduler/node/node.go#L33) that control additional filtering and sorting of the Kubernetes resources relevant to the Descheduler (namely, Pods and Nodes). These could be extended with custom logic, or even be abstracted to support listing and indexing any generic resource, to provide further fine-grained customization of eviction algorithms. For example, there is a proposal to control Pod eviction [based on availability of PVCs](https://github.com/kubernetes-sigs/descheduler/pull/723) on the node. + +**Extending the Config API** + +Relevant links: +- https://github.com/kubernetes-sigs/descheduler/pull/587 +- https://github.com/kubernetes-sigs/descheduler/issues/486 + +Any custom features that third-party developers implement will also likely require custom configuration options in the [Descheduler API](https://github.com/kubernetes-sigs/descheduler/blob/1529180/pkg/api/types.go). However, the current API is strictly defined and inflexible to changes without growing significantly or breaking current use cases. Therefore, we need to redesign the API in a way that is extensible for generic additions while maintaining stability. + +**Existing Code** + +Some of these have already been designed with future customizability in mind. For example, the PodLister accepts a [Filter function](https://github.com/kubernetes-sigs/descheduler/blob/224e2b078f62e3537d32caec7733f0918d48c248/pkg/descheduler/pod/pods.go#L168) and the generic [Strategy function signature](https://github.com/kubernetes-sigs/descheduler/blob/224e2b078f62e3537d32caec7733f0918d48c248/pkg/descheduler/descheduler.go#L81) used by all Strategies has been standardized to enable a [pseudo-registry](https://github.com/kubernetes-sigs/descheduler/blob/224e2b078f62e3537d32caec7733f0918d48c248/pkg/descheduler/descheduler.go#L178-L189) (similar to how the Scheduler builds a [registry of Plugin functions](https://github.com/kubernetes/kubernetes/blob/1d656d46a2b87898e7c3449be37de1ac925ccb9c/pkg/scheduler/framework/runtime/registry.go#L67-L70)). These patterns can be used as a template to expand this to the rest of the codebase. + +Together, each of these use cases present examples of a customization point for developers. For example, a user wishing to design a custom Strategy may need to add additional config options to the API with eviction priority given to certain pods based on the type of workload that pod is running. + + +#### Story 2 + +As a descheduler user I'd like to extend the descheduler with custom strategies that conforms to my company policies or fits my company security countermeasures. + +Currently suggested enhancement: + +**Custom "Out-of-tree" Strategies** + +Relevant links: +- https://github.com/kubernetes-sigs/descheduler/issues/557 +- https://github.com/kubernetes-sigs/descheduler/issues/679 +- https://github.com/kubernetes-sigs/descheduler/issues/586 +- https://github.com/kubernetes-sigs/descheduler/pull/706 + +Perhaps the most prominent entry point for customizing the Descheduler is the development of custom Strategies. Strategies define individual eviction algorithms at the feature level. The Descheduler currently provides a set of default Strategies that fit many use cases (for example, RemovePodsViolatingNodeTaints). But, with growing unique production use cases, users may need to implement behavior (or modify existing behavior) in a way that is incompatible with current code or too unique to support at a project level. Enabling the development of custom Strategies is the conceptual equivalent of out-of-tree Plugins in the Scheduler Framework. + + +### Notes/Constraints/Caveats (Optional) + + + +- **API boundaries**: It's non-trivial to properly set the API for the new plugins. Each custom plugin may have different expectations about what functionality is provided by the framework. From providing various informers (pods, nodes, etc.) to exposing eviction or sorting logic which can be exposed through a custom plugin. Currently, every strategy is responsible for invoking the eviction explicitly. Some strategies have their own sorting mechanism which could be shared by other strategies. Some strategies are implemented in a way that allows to replace their current sorting mechanism by any reasonable ordering. E.g. time based, annotation based, replicaset size, etc. +- **Framework plugin API**: The first iteration of the plugin API will be reduced to provide minimal interface for the currently existing strategies. Later, it can evolve and be extended with new extension points (to accommodate for new functionalities that can be shared across plugins) or additional "common" routines simplyfing plugin creation. +- **Workload distribution awareness**: Some strategies take into account other pods when selecting victims for eviction. Other strategies aim for balancing pods across nodes. Incorrect order of executing strategies might work towards the expected goal of descheduling. Thus, it's important to allow users to change the ordering. Also, with any ordering it's important to first run all plugins that work against even balancing. +- **Simulation**: The framework needs to allow to simulate its decisions so users can run it in a non-destructive way to either tune the descheduler configuration to improve performance or to detect how much a cluster deviated from expected scheduling constraints. Posing a challenge for custom plugins creators to be aware of this requirement. E.g. to avoid building their own clientsets from scratch that are expected to be injected by the framework. So the framework can inject a fake clientset instead when a simulation is performed. +- **Descheduling policy and plugin configuration**: Each plugin will have its own list of arguments whose data types are unknown in advance. Thus the overall descheduling policy configuration needs to use a "variable" data type for each plugin configuration. Making the whole policy validation and conversion more challenging. +- **Framework internal counters**: The framework needs to keep count of how many pods (in general, per namespace, per node, etc.) were evicted through various plugins. Plugins are expected to evict pods through an abstracted interface injected by the framework. Which (if configured) will limit the number of pods evicted. At the same time setting various metrics. +- **Pods as cattle**: The descheduler treats all the pods the same. If a pod fails to be evicted, another one is retried. There's no guaranteed order of eviction. Some strategies sort pods based on a priority or other ordering that's specific to a decision making logic. +- **Stateless**: The descheduler is currently designed to be stateless. It does not keep track of pods evicted in a previous descheduling cycle. Neither a list of pods that failed to be evicted. +- **Plugins invoking other plugins**: A descheduling plugin logic may invoke routines of other plugins. Each plugin is injected with a shared interface allowing for runtime configuration of sorting or filtering policies customizable through a descheduling policy configuration. + +### Risks and Mitigations + + + +- **Plugins whereabouts**: It's currently unclear where all the new community accepted plugins will live. +- **Limited plugin API**: The existing plugin API might be too limiting for plugin creators. Any such limitation will be discussed through community channels and addressed accordingly. +- **New default plugins acceptance**: The descheduler will strive for reducing acceptance of new default plugins. All new plugins will be external and users will be asked to assembly their own deschedulers whenever the default set is insufficient. +- **Framework in the core**: The descheduler project maintainers will shift their focus from plugins to the framework. Closely collaborating with plugin owners to implement missing functionalities in the framework and adjusting to new ecosystem requirements. +- **Framework stability**: The framework is expected to be a central piece for combining various descheduling policies based on user use cases. Once the framework matures and provides a solid base the amount of new features will be slowly reduced to minimum. Instead, wrapping new functionalities into plugins. +- **Plugins invoking other plugins**: In order to share sorting, filtering and other shared mechanisms across plugins, each plugin needs to be allowed to transparently invoke these mechanisms through a shared interface. When or whether either of the shared mechanisms get invoke is plugin dependent. Posing a risk that some plugins might successfully ignore user configured preferences. + +## Design Details + + + + + +The framework provides basic handles for plugins, their registration, mechanics for running strategies within descheduling cycles and connects all the pieces together. Provides registry with default plugins and ability to extend it with out-of-tree plugins. Plugins are initialized with the provided configuration before the descheduling workflow starts. + +### Plugins + +A plugin is an encapsulation of a descheduling decision making policy. Alongside with one or more extension points. Each extension point responsible for a single action. Some of the actions may invoke other actions (such as sorting or filtering). Currently recognized extension points are: + +- **PreSort**: sort pods before processing +- **Deschedule**: evict pods by processing each pod independently of each other +- **Balance**: balance distribution of pods while taking two or more pods into account +- **Filter**: provides means for filtering pods before they are evicted (i.e. responsible for deciding when a pod can be evicted) +- **Sort**: sort pods before eviction + +The current descheduler strategies can be mapped to the corresponding extension points in the following manner: + +- **Deschedule**: RemoveFailedPods, RemovePodsViolatingNodeAffinity, RemovePodsViolatingNodeTaints, RemovePodsViolatingInterPodAntiAffinity, RemovePodsViolatingInterPodAntiAffinity, RemovePodsHavingTooManyRestarts +- **Balance**: RemoveDuplicatePods, RemovePodsViolatingTopologySpreadConstraint, LowNodeUtilization, HighNodeUtilization + +The Filter extension point corresponds to the current `IsEvictable` of the `PodEvictor` data type. The Sort extension point corresponds to sorting pods by priority from low to high. + +The extension points are divided into two categories: + +- High level: Deschedule and Balance (eviction strategy implementation) +- Low level: PreSort, Filter, Sort + +High level extension points are invoked by the descheduling framework. The low level ones are invoked inside high level extension points. It is solely up to each strategy implementation to decide at which point the low level extension points get invoked. Invocation of all low level extension points is optional. This shifts the responsibility of invoking the shared routines (configured through the descheduling policy) to plugin owners. Increasing emphasis on awareness of this limitation. + +The plugin configuration API is expected to allow plugins to be disabled, resp. enabled in order to change the descheduling behavior. Each plugin is passed its configuration arguments and a handle with a clientset, pod evictor, pod filter, sorter and other utilities. Plugins are expected to be initialized through the following constructor: + +```go +func New(args runtime.Object, handle framework.Handle) (framework.Plugin, error) { + pluginArgs, ok := args.(*framework.PluginArgs) + … +} +``` + +### Extension points + +An extension point is the lowest level action a plugin can perform. Ranging from sorting a list of pods into nominating candidates for eviction followed by their actual eviction through shared interface. Extension points allow for implementation of a custom code logic that can be enabled based on individual use case requirements. + +All plugins are expected to implement the following `Plugin` interface. It serves as a basic data type for storing the plugins into a plugin registry. + +```go +type Plugin interface { + Name() string +} +``` + +Any plugin can implement either of the following interfaces: + +- `PreSortPlugin`: by every sorting mechanism that’s expected to sort pods before processing by individual eviction strategies. E.g. to process the oldest pods first. + ```go + type PreSortPlugin interface { + Plugin + PreLess(*v1.Pod, *v1.Pod) bool + } + ``` + +- `DeschedulePlugin`, `BalancePlugin`: all eviction strategies are expected to implement at least one of these. + ```go + type DeschedulePlugin interface { + Plugin + Deschedule(ctx context.Context, nodes []*v1.Node) *Status + } + + type BalancePlugin interface { + Plugin + Balance(ctx context.Context, nodes []*v1.Node) *Status + } + ``` + +- `EvictPlugin`: any filtering mechanism that wants to filter out pods before eviction. E.g. avoid evicting pods with local storage. + ```go + type EvictPlugin interface { + Plugin + Filter(*v1.Pod) bool + } + ``` + +- `SortPlugin`: any sorting mechanism that wants to sort pods right before the eviction. E.g. any pod eviction can be throttled so it might be more efficient to evict low priority pods first. + ```go + type SortPlugin interface { + Plugin + Less(*v1.Pod, *v1.Pod) bool + } + ``` + +### Descheduling cycle + +Some eviction strategies like PodLifeTime or RemovePodsHavingTooManyRestarts evict pods while ignoring everything else around (nodes, pods). Given the simplicity, filtering and sorting can be done at any point. Other eviction strategies like RemovePodsViolatingInterPodAntiAffinity or RemovePodsViolatingTopologySpreadConstraint need to take other pods or nodes into account when deciding whether a pod gets evicted. In some cases performing pod filtering (e.g. excluding inevictable pods) may be carried in the middle of a strategy implementation. To avoid limiting the strategies while allowing the filtering and sorting implementation to be plugable, the strategies need to be allowed to access certain extension points internally. Thus, the descheduling cycle reduces to invocation of only two extension points (run in the following order): + +- Run all Deschedule extension points +- Run all Balance extension points + +The Balance extension point is run after the Deschedule. The reversed order might work against balancing. The actual pod filtering before eviction and pod sorting before processing or before eviction are expected to be invoked inside the Deschedule and the Balance extension points. A pod can be evicted at any point. While it is optional, all strategies are recommended to use the shared filtering and sorting to allow users to plug a different implementation which is more aligned with their use cases. + +Each descheduling cycle starts by capturing the current state of a cluster. Both Deschedule and Balance extension points are expected to receive the same list of nodes. The order in which individual nodes (and assigned pods) are processed per the mentioned extension points is implementation dependent. Each plugin can traverse the pods in any order, evict any pod at any moment. There's no paralelism of plugins. Only a single plugin is executed at a given time. Each pod nominated for eviction is expected to be evicted as soon as possible. + +**Note**: Later implementations might mark pods for eviction instead so other plugins may take this fact into consideration to avoid unnecessary evictions. This will become more relevant when the eviction is replaced by an evacuation request (TODO(ingvagabund): share the KEP link once available) which introduces a delay before a pod is actually evicted. + + + +### Descheduler + +The descheduler itself is expected to initialize the framework and invoke the high level framework routines responsible for executing individual plugin extension points. E.g. + +```go +func New(...) (*Descheduler, error) { + ... +} + +func (d *Descheduler) descheduleOnce(ctx context.Context) error { + ... + nodes, err := nodeutil.ReadyNodes(ctx, ...) + if status := d.framework.RunDeschedulePlugins(ctx, nodes); status != nil && status.Err != nil { + return status.Err + } + + if status := d.framework.RunBalancePlugins(ctx, nodes); status != nil && status.Err != nil { + return status.Err + } + + return nil +} + +func (d *Descheduler) Run(ctx context.Context) { + wait.NonSlidingUntil(func() { + if err := desch.deschedulerOnce(ctx); err != nil { + klog.Errorf("Error descheduling pods: %v", err) + } + }, rs.DeschedulingInterval, ctx.Done()) +} +``` + +As mentioned earlier, only two extension points are available for executing. The remaining (PreSort, Filter, …) are expected to be invoked from within implementation of Deschedule resp. Balance extension points. + +### Descheduling profiles + +A profile is the smallest configuration unit that allows to run the same strategies with different configuration over different sets of pods (specified by combination of included/excluded namespaces and a label selector). The descheduling profile is not the same as the scheduling profile (a pod is not allowed to specify which descheduler strategy is to be run when the pod is considered for eviction). Thus, each descheduling profile is rather a representation of descheduling strategies sharing the same eviction policies (e.g. identical priority threshold, target node preferences). + +Plugins are expected to be executed in the same order as they are specified across and in each profile. As mentioned previously all `Deschedule` extension points are executed before any `Balance` extension point is (including cross-profile) to aim for even balancing when configured. + + + +### Descheduling workflow + +The following diagram depicts the descheduling cycle workflow. In each descheduling cycle a list of nodes is passed to each Deschedule and Balance plugin. Both extension points are expected to list all pods on each node before further processing. First, all Deschedule extension points from all profiles are executed. Followed by all Balance extension points. The framework can be configured to keep a track of pods evicted per node/namespaces per each descheduling cycle. When a limit on the number of evicted pods is configured and exceeded, the descheduling cycle is prematurely finished. So it’s recommended to put the profiles in a way that the most preferable pods are evicted first. + +![Descheduling framework workflow diagram](framework-workflow-diagram.png) + + +### Plugin registration + +Each plugin is expected to define an initialization function that gets passed plugin arguments (optional) and a handle. All the plugin initialization functions are expected to honor the same signature: + +```go +func(args runtime.Object, handle framework.Handle) (framework.Plugin, error) +``` + +By default, the framework will register all default plugins. The registry API could honor the following structure: + +```go +type PluginBuilder = func(args runtime.Object, handle framework.Handle) (framework.Plugin, error) + +type Registry = map[string]PluginBuilder + +func NewRegistry() Registry { + return Registry { + pluginA.Name: pluginA.New, + pluginB.Name: pluginB.New, + ... + } +} +``` + +Given the same plugin can have different configurations across different profiles, a plugin can get initialized multiple times. + +Registration of out-of-tree plugins is out of scope of this proposal and will be discussed separately. + +### Pod evictor policies as a plugin + +Currently, all the descheduling eviction policies are implemented through the PodEvictor. The implementation tightly couples the code responsible for the eviction itself and the code deciding whether a pod is eligible for descheduling. Given the latter code is application dependent, different workloads may require different policies which do not have to exist in the descheduler code base. Instead of extending the code base with new checks and taking maintenance responsibility, the new code will live externally inside a plugin and be registered in the framework only when it is needed. Allowing users to register otherwise a proprietary solution. + +### Configuration + +The configuration consists of profiles. Each profile is built from a list of plugins and a plugin configuration (optional). Some extension points have a default list of enabled plugins. The same holds for the configuration part. Plugins are executed in the same order as they appear in the configuration. Every plugin configuration is expected to have some default values. Including validation of provided configuration. + +Configuration data types: + +```go +type DeschedulerConfiguration struct { + Profiles []Profiles + ... +} + +type Profile struct { + Name string + PluginConfig []PluginConfig + Plugins Plugins + ... +} + +type PluginConfig struct { + Name string + Args runtime.Object +} + +type Plugins struct { + PreSort []PluginSet + Deschedule []PluginSet + Balance []PluginSet + Filter []PluginSet + Sort []PluginSet +} + +type PluginSet struct { + Enabled []Plugin + Disabled []Plugin +} + +type Plugin string +``` + +**Example**: + +```yaml +apiVersion: descheduler/v1alpha2 +kind: DeschedulerPolicy +profiles: +- name: ProfileName + pluginConfig: + - name: DefaultEvictor + args: + evictSystemCriticalPods: true # default false + evictFailedBarePods: true # default false + evictLocalStoragePods: true # default false + nodeFit: true # default false + - name: PodsHavingTooManyRestarts + args: + podRestartThreshold: 100 + includingInitContainers: true + - name: LowNodeUtilization + args: + lowThreshold: + cpu : 20 + memory: 20 + pods: 20 + highThreshold: + cpu : 50 + memory: 50 + pods: 50 + - name: TopologySpreadConstraint + args: + includeSoftConstraints: true + plugins: + filter: + # default filters: DefaultEvictor + disabled: + enabled: + sort: + # default sorters: Priority + disabled: + Priority + enabled: + Utilization + deschedule: + # plugins descheduling pods without checking other pods + enabled: + - PodsHavingTooManyRestarts + - InterPodAntiAffinity + rebalance: + # plugins descheduling pods while balancing distribution + enabled: + - Duplicates + - LowNodeUtilization + - TopologySpreadConstraint + evict: + # default evict plugins: DefaultEvictor + disabled: + DefaultEvictor + enabled: + CustomEvictor +``` + +### Metrics + +The framework is expected to collect metrics about extension points triggered, about processing times of Deschedule/Balance extension points, pods evicted, etc. + +### Test Plan + + + + +##### Prerequisite testing updates + + + +##### Unit tests + + + + + +- Unit tests for every default descheduling plugin under `pkg/framework/plugins` +- Unit tests around executing profiles (`pkg/framework/profile`) and running a descheduling cycle (`pkg/descheduler`) +- Unit tests around registration and setting up plugins + +##### e2e tests + + + +- e2e tests for evicting pods by default plugins +- e2e tests for exercising various default eviction policies +- e2e tests for exercising filtering and sorting before eviction +- e2e tests for testing eviction limits (per node, per namespace, per cluster) +- e2e tests for metrics + + + +### Graduation Criteria + + + +#### Alpha + +- All default strategies turned into plugins +- The eviction filtering code turned into a plugin +- Plugin registration in place +- Profiles configuration in the descheduling policy +- Conversion from v1alpha1 to v1alpha2 +- Descheduling cycle runs all Deschedule extension points first, then all Balance +- Descheduling framework as a library +- Initial e2e tests completed and enabled + +### Upgrade / Downgrade Strategy + + + +- v1alpha1 descheduling policy is convertable into v1alpha2 (with profiles) +- v1alpha2 to v1alpha1 policy downgrading is not supported +- the new descheduling framework deprecates the previous descheduler implementation, no downgrade to pre-framework implementation is supported + +### Version Skew Strategy + + + +The descheduler/framework relies on watching resources and invocation of the `pods/eviction` subresource. As long as the watching resources and the subresource API do not change, there's no version skew. + +## Production Readiness Review Questionnaire + + + +### Feature Enablement and Rollback + + + +###### How can this feature be enabled / disabled in a live cluster? + + + +- [ ] Other + - Describe the mechanism: + - The descheduling framework is part of an optional descheduler component. The component needs to be deployed as a separate pod. The descheduling policy is passed into the descheduler through the descheduler command arguments. + - Will enabling / disabling the feature require downtime of the control + plane? **No** + - Will enabling / disabling the feature require downtime or reprovisioning + of a node? **No** + +###### Does enabling the feature change any default behavior? + + + +In the pre-framework descheduler implementation the order of configured strategies was fixed. With the framework a user can configure a different order of plugin execution. Also, the new implementation invokes strategies that correspond to the Deschedule extension point first. Thus, changing the way in which pods are evicted. + +###### Can the feature be disabled once it has been enabled (i.e. can we roll back the enablement)? + + + +Does not apply. Disabling the feature corresponds to deploying older descheduler version. + +###### What happens if we reenable the feature if it was previously rolled back? + +Does not apply. Disabling the feature corresponds to deploying older descheduler version. + +###### Are there any tests for feature enablement/disablement? + + + +Does not apply. Disabling the feature corresponds to deploying older descheduler version. + +### Rollout, Upgrade and Rollback Planning + + + +###### How can a rollout or rollback fail? Can it impact already running workloads? + + + +###### What specific metrics should inform a rollback? + + + +###### Were upgrade and rollback tested? Was the upgrade->downgrade->upgrade path tested? + + + +###### Is the rollout accompanied by any deprecations and/or removals of features, APIs, fields of API types, flags, etc.? + + + +### Monitoring Requirements + + + +###### How can an operator determine if the feature is in use by workloads? + + + +Does not apply. The feature is not used by a workload. The feature operates over a workload. + +###### How can someone using this feature know that it is working for their instance? + + + +- [ ] Events + - Event type: `Normal` + - Event action: `Descheduled` + - Event Reason: `pod evicted from NODE node by sigs.k8s.io/descheduler` +- [ ] Other (treat as last resort) + - Details: checking the descheduler logs + +###### What are the reasonable SLOs (Service Level Objectives) for the enhancement? + + + +###### What are the SLIs (Service Level Indicators) an operator can use to determine the health of the service? + + + +- [ ] Metrics + - Metric name: + - [Optional] Aggregation method: + - Components exposing the metric: +- [ ] Other (treat as last resort) + - Details: + +###### Are there any missing metrics that would be useful to have to improve observability of this feature? + + + +### Dependencies + + + +###### Does this feature depend on any specific services running in the cluster? + + + +No + +### Scalability + + + +###### Will enabling / using this feature result in any new API calls? + + + +This will greatly depend on external plugins. The alpha framework implementation migrated all the strategies into plugins. Keeping the original API calls. Some code improvements may result in watching additional resources related to pod owners such as Deployments, ReplicaSets, etc. + +###### Will enabling / using this feature result in introducing new API types? + + + +The v1alpha2 descheduling policy introduces a concept of profiles. Yet, the new data type is not cluster exposed. Only known to the descheduler component through a configuration file. + +###### Will enabling / using this feature result in any new calls to the cloud provider? + + + +No + +###### Will enabling / using this feature result in increasing size or count of the existing API objects? + + + +No + +###### Will enabling / using this feature result in increasing time taken by any operations covered by existing SLIs/SLOs? + + + +No. The descheduler component is a self-standing component. + +###### Will enabling / using this feature result in non-negligible increase of resource usage (CPU, RAM, disk, IO, ...) in any components? + + + +No. The descheduler component is a self-standing component. + +###### Can enabling / using this feature result in resource exhaustion of some node resources (PIDs, sockets, inodes, etc.)? + + + +No. There's no direct interaction with any node resource. + +### Troubleshooting + + + +* **How does this feature react if the API server and/or etcd is unavailable?** The descheduler needs to watch pods, nodes and other resources and perform a pod eviction operation. WIth API server/etcd unavailable the component can not work properly. + +* **What are other known failure modes?** For each of them, fill in the following information by copying the below template: + - [Failure mode brief description] + - Detection: How can it be detected via metrics? Stated another way: + how can an operator troubleshoot without logging into a master or worker node? + - An operator can check the descheduler logs + - Mitigations: What can be done to stop the bleeding, especially for already + running user workloads? + - Diagnostics: What are the useful log messages and their required logging + levels that could help debug the issue? + Not required until feature graduated to beta. + - Testing: Are there any tests for failure mode? If not, describe why. + +* **What steps should be taken if SLOs are not being met to determine the problem?** + +## Implementation History + + + +- 2022-04-11: first draft of the proposal +- 2022-09-24: strategies moved into plugins +- 2023-04-20: descheduling framework types implemented +- 2023-06-21: descheduling cycle and "Descheduler first, Balance after" implemented +- 2024-04-11: updating and turning the proposal into the KEP template + +## Drawbacks + + + +## Alternatives + + + +## Infrastructure Needed (Optional) + + diff --git a/keps/753-descheduling-framework/framework-workflow-diagram.png b/keps/753-descheduling-framework/framework-workflow-diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..555ac24e9090932d452d3a49d3b508ee6e7abeb9 GIT binary patch literal 64448 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ŰeB4bbUX#z|H)qWral_TZIldaEOF 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 literal 0 HcmV?d00001 diff --git a/keps/753-descheduling-framework/kep.yaml b/keps/753-descheduling-framework/kep.yaml new file mode 100644 index 0000000000..f69e1ce6ea --- /dev/null +++ b/keps/753-descheduling-framework/kep.yaml @@ -0,0 +1,29 @@ +title: Descheduling framework +kep-number: 753 +authors: + - "@ingvagabund" + - "@damemi" +owning-sig: sig-scheduling +status: provisional +creation-date: 2024-04-08 +reviewers: + - "@ingvagabund" + - "@damemi" + - "@a7i" + - "@knelasevero" +approvers: + - "@ingvagabund" + - "@damemi" + - "@a7i" + - "@knelasevero" +#replaces: + +# The target maturity stage in the current dev cycle for this KEP. +stage: alpha + +# The most recent milestone for which work toward delivery of this KEP has been +# done. This can be the current (upcoming) milestone, if it is being actively +# worked on. +# latest-milestone: "v1.19" + +# disable-supported: true