Rename "Scheduling: concept as "Scheduling and Eviction"
* Rename `docs/concepts/scheduling` to `docs/concepts/scheduling-eviction` * Retitle concept header to "Scheduling and Eviction" * Update redirects * Update internal links (en only) Part of proposal #19081 Signed-off-by: Adam Kaplan <adam.kaplan@redhat.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: "Scheduling and Eviction"
|
||||
weight: 90
|
||||
---
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
title: Kubernetes Scheduler
|
||||
content_template: templates/concept
|
||||
weight: 50
|
||||
---
|
||||
|
||||
{{% capture overview %}}
|
||||
|
||||
In Kubernetes, _scheduling_ refers to making sure that {{< glossary_tooltip text="Pods" term_id="pod" >}}
|
||||
are matched to {{< glossary_tooltip text="Nodes" term_id="node" >}} so that
|
||||
{{< glossary_tooltip term_id="kubelet" >}} can run them.
|
||||
|
||||
{{% /capture %}}
|
||||
|
||||
{{% capture body %}}
|
||||
|
||||
## Scheduling overview {#scheduling}
|
||||
|
||||
A scheduler watches for newly created Pods that have no Node assigned. For
|
||||
every Pod that the scheduler discovers, the scheduler becomes responsible
|
||||
for finding the best Node for that Pod to run on. The scheduler reaches
|
||||
this placement decision taking into account the scheduling principles
|
||||
described below.
|
||||
|
||||
If you want to understand why Pods are placed onto a particular Node,
|
||||
or if you're planning to implement a custom scheduler yourself, this
|
||||
page will help you learn about scheduling.
|
||||
|
||||
## kube-scheduler
|
||||
|
||||
[kube-scheduler](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-scheduler/)
|
||||
is the default scheduler for Kubernetes and runs as part of the
|
||||
{{< glossary_tooltip text="control plane" term_id="control-plane" >}}.
|
||||
kube-scheduler is designed so that, if you want and need to, you can
|
||||
write your own scheduling component and use that instead.
|
||||
|
||||
For every newly created pod or other unscheduled pods, kube-scheduler
|
||||
selects an optimal node for them to run on. However, every container in
|
||||
pods has different requirements for resources and every pod also has
|
||||
different requirements. Therefore, existing nodes need to be filtered
|
||||
according to the specific scheduling requirements.
|
||||
|
||||
In a cluster, Nodes that meet the scheduling requirements for a Pod
|
||||
are called _feasible_ nodes. If none of the nodes are suitable, the pod
|
||||
remains unscheduled until the scheduler is able to place it.
|
||||
|
||||
The scheduler finds feasible Nodes for a Pod and then runs a set of
|
||||
functions to score the feasible Nodes and picks a Node with the highest
|
||||
score among the feasible ones to run the Pod. The scheduler then notifies
|
||||
the API server about this decision in a process called _binding_.
|
||||
|
||||
Factors that need taken into account for scheduling decisions include
|
||||
individual and collective resource requirements, hardware / software /
|
||||
policy constraints, affinity and anti-affinity specifications, data
|
||||
locality, inter-workload interference, and so on.
|
||||
|
||||
### Node selection in kube-scheduler {#kube-scheduler-implementation}
|
||||
|
||||
kube-scheduler selects a node for the pod in a 2-step operation:
|
||||
|
||||
1. Filtering
|
||||
1. Scoring
|
||||
|
||||
The _filtering_ step finds the set of Nodes where it's feasible to
|
||||
schedule the Pod. For example, the PodFitsResources filter checks whether a
|
||||
candidate Node has enough available resource to meet a Pod's specific
|
||||
resource requests. After this step, the node list contains any suitable
|
||||
Nodes; often, there will be more than one. If the list is empty, that
|
||||
Pod isn't (yet) schedulable.
|
||||
|
||||
In the _scoring_ step, the scheduler ranks the remaining nodes to choose
|
||||
the most suitable Pod placement. The scheduler assigns a score to each Node
|
||||
that survived filtering, basing this score on the active scoring rules.
|
||||
|
||||
Finally, kube-scheduler assigns the Pod to the Node with the highest ranking.
|
||||
If there is more than one node with equal scores, kube-scheduler selects
|
||||
one of these at random.
|
||||
|
||||
There are two supported ways to configure the filtering and scoring behavior
|
||||
of the scheduler:
|
||||
|
||||
1. [Scheduling Policies](/docs/reference/scheduling/policies) allow you to
|
||||
configure _Predicates_ for filtering and _Priorities_ for scoring.
|
||||
1. [Scheduling Profiles](/docs/reference/scheduling/profiles) allow you to
|
||||
configure Plugins that implement different scheduling stages, including:
|
||||
`QueueSort`, `Filter`, `Score`, `Bind`, `Reserve`, `Permit`, and others. You
|
||||
can also configure the kube-scheduler to run different profiles.
|
||||
|
||||
{{% /capture %}}
|
||||
{{% capture whatsnext %}}
|
||||
* Read about [scheduler performance tuning](/docs/concepts/scheduling-eviction/scheduler-perf-tuning/)
|
||||
* Read about [Pod topology spread constraints](/docs/concepts/workloads/pods/pod-topology-spread-constraints/)
|
||||
* Read the [reference documentation](/docs/reference/command-line-tools-reference/kube-scheduler/) for kube-scheduler
|
||||
* Learn about [configuring multiple schedulers](/docs/tasks/administer-cluster/configure-multiple-schedulers/)
|
||||
* Learn about [topology management policies](/docs/tasks/administer-cluster/topology-manager/)
|
||||
* Learn about [Pod Overhead](/docs/concepts/configuration/pod-overhead/)
|
||||
{{% /capture %}}
|
||||
@@ -0,0 +1,167 @@
|
||||
---
|
||||
reviewers:
|
||||
- bsalamat
|
||||
title: Scheduler Performance Tuning
|
||||
content_template: templates/concept
|
||||
weight: 70
|
||||
---
|
||||
|
||||
{{% capture overview %}}
|
||||
|
||||
{{< feature-state for_k8s_version="v1.14" state="beta" >}}
|
||||
|
||||
[kube-scheduler](/docs/concepts/scheduling-eviction/kube-scheduler/#kube-scheduler)
|
||||
is the Kubernetes default scheduler. It is responsible for placement of Pods
|
||||
on Nodes in a cluster.
|
||||
|
||||
Nodes in a cluster that meet the scheduling requirements of a Pod are
|
||||
called _feasible_ Nodes for the Pod. The scheduler finds feasible Nodes
|
||||
for a Pod and then runs a set of functions to score the feasible Nodes,
|
||||
picking a Node with the highest score among the feasible ones to run
|
||||
the Pod. The scheduler then notifies the API server about this decision
|
||||
in a process called _Binding_.
|
||||
|
||||
This page explains performance tuning optimizations that are relevant for
|
||||
large Kubernetes clusters.
|
||||
|
||||
{{% /capture %}}
|
||||
|
||||
{{% capture body %}}
|
||||
|
||||
In large clusters, you can tune the scheduler's behaviour balancing
|
||||
scheduling outcomes between latency (new Pods are placed quickly) and
|
||||
accuracy (the scheduler rarely makes poor placement decisions).
|
||||
|
||||
You configure this tuning setting via kube-scheduler setting
|
||||
`percentageOfNodesToScore`. This KubeSchedulerConfiguration setting determines
|
||||
a threshold for scheduling nodes in your cluster.
|
||||
|
||||
### Setting the threshold
|
||||
|
||||
The `percentageOfNodesToScore` option accepts whole numeric values between 0
|
||||
and 100. The value 0 is a special number which indicates that the kube-scheduler
|
||||
should use its compiled-in default.
|
||||
If you set `percentageOfNodesToScore` above 100, kube-scheduler acts as if you
|
||||
had set a value of 100.
|
||||
|
||||
To change the value, edit the kube-scheduler configuration file (this is likely
|
||||
to be `/etc/kubernetes/config/kube-scheduler.yaml`), then restart the scheduler.
|
||||
|
||||
After you have made this change, you can run
|
||||
```bash
|
||||
kubectl get componentstatuses
|
||||
```
|
||||
to verify that the kube-scheduler component is healthy. The output is similar to:
|
||||
```
|
||||
NAME STATUS MESSAGE ERROR
|
||||
controller-manager Healthy ok
|
||||
scheduler Healthy ok
|
||||
...
|
||||
```
|
||||
|
||||
## Node scoring threshold {#percentage-of-nodes-to-score}
|
||||
|
||||
To improve scheduling performance, the kube-scheduler can stop looking for
|
||||
feasible nodes once it has found enough of them. In large clusters, this saves
|
||||
time compared to a naive approach that would consider every node.
|
||||
|
||||
You specify a threshold for how many nodes are enough, as a whole number percentage
|
||||
of all the nodes in your cluster. The kube-scheduler converts this into an
|
||||
integer number of nodes. During scheduling, if the kube-scheduler has identified
|
||||
enough feasible nodes to exceed the configured percentage, the kube-scheduler
|
||||
stops searching for more feasible nodes and moves on to the
|
||||
[scoring phase](/docs/concepts/scheduling-eviction/kube-scheduler/#kube-scheduler-implementation).
|
||||
|
||||
[How the scheduler iterates over Nodes](#how-the-scheduler-iterates-over-nodes)
|
||||
describes the process in detail.
|
||||
|
||||
### Default threshold
|
||||
|
||||
If you don't specify a threshold, Kubernetes calculates a figure using a
|
||||
linear formula that yields 50% for a 100-node cluster and yields 10%
|
||||
for a 5000-node cluster. The lower bound for the automatic value is 5%.
|
||||
|
||||
This means that, the kube-scheduler always scores at least 5% of your cluster no
|
||||
matter how large the cluster is, unless you have explicitly set
|
||||
`percentageOfNodesToScore` to be smaller than 5.
|
||||
|
||||
If you want the scheduler to score all nodes in your cluster, set
|
||||
`percentageOfNodesToScore` to 100.
|
||||
|
||||
## Example
|
||||
|
||||
Below is an example configuration that sets `percentageOfNodesToScore` to 50%.
|
||||
|
||||
```yaml
|
||||
apiVersion: kubescheduler.config.k8s.io/v1alpha1
|
||||
kind: KubeSchedulerConfiguration
|
||||
algorithmSource:
|
||||
provider: DefaultProvider
|
||||
|
||||
...
|
||||
|
||||
percentageOfNodesToScore: 50
|
||||
```
|
||||
|
||||
|
||||
## Tuning percentageOfNodesToScore
|
||||
|
||||
`percentageOfNodesToScore` must be a value between 1 and 100 with the default
|
||||
value being calculated based on the cluster size. There is also a hardcoded
|
||||
minimum value of 50 nodes.
|
||||
|
||||
{{< note >}}In clusters with less than 50 feasible nodes, the scheduler still
|
||||
checks all the nodes, simply because there are not enough feasible nodes to stop
|
||||
the scheduler's search early.
|
||||
|
||||
In a small cluster, if you set a low value for `percentageOfNodesToScore`, your
|
||||
change will have no or little effect, for a similar reason.
|
||||
|
||||
If your cluster has several hundred Nodes or fewer, leave this configuration option
|
||||
at its default value. Making changes is unlikely to improve the
|
||||
scheduler's performance significantly.
|
||||
{{< /note >}}
|
||||
|
||||
An important detail to consider when setting this value is that when a smaller
|
||||
number of nodes in a cluster are checked for feasibility, some nodes are not
|
||||
sent to be scored for a given Pod. As a result, a Node which could possibly
|
||||
score a higher value for running the given Pod might not even be passed to the
|
||||
scoring phase. This would result in a less than ideal placement of the Pod.
|
||||
|
||||
You should avoid setting `percentageOfNodesToScore` very low so that kube-scheduler
|
||||
does not make frequent, poor Pod placement decisions. Avoid setting the
|
||||
percentage to anything below 10%, unless the scheduler's throughput is critical
|
||||
for your application and the score of nodes is not important. In other words, you
|
||||
prefer to run the Pod on any Node as long as it is feasible.
|
||||
|
||||
## How the scheduler iterates over Nodes
|
||||
|
||||
This section is intended for those who want to understand the internal details
|
||||
of this feature.
|
||||
|
||||
In order to give all the Nodes in a cluster a fair chance of being considered
|
||||
for running Pods, the scheduler iterates over the nodes in a round robin
|
||||
fashion. You can imagine that Nodes are in an array. The scheduler starts from
|
||||
the start of the array and checks feasibility of the nodes until it finds enough
|
||||
Nodes as specified by `percentageOfNodesToScore`. For the next Pod, the
|
||||
scheduler continues from the point in the Node array that it stopped at when
|
||||
checking feasibility of Nodes for the previous Pod.
|
||||
|
||||
If Nodes are in multiple zones, the scheduler iterates over Nodes in various
|
||||
zones to ensure that Nodes from different zones are considered in the
|
||||
feasibility checks. As an example, consider six nodes in two zones:
|
||||
|
||||
```
|
||||
Zone 1: Node 1, Node 2, Node 3, Node 4
|
||||
Zone 2: Node 5, Node 6
|
||||
```
|
||||
|
||||
The Scheduler evaluates feasibility of the nodes in this order:
|
||||
|
||||
```
|
||||
Node 1, Node 5, Node 2, Node 6, Node 3, Node 4
|
||||
```
|
||||
|
||||
After going over all the Nodes, it goes back to Node 1.
|
||||
|
||||
{{% /capture %}}
|
||||
@@ -0,0 +1,242 @@
|
||||
---
|
||||
reviewers:
|
||||
- ahg-g
|
||||
title: Scheduling Framework
|
||||
content_template: templates/concept
|
||||
weight: 60
|
||||
---
|
||||
|
||||
{{% capture overview %}}
|
||||
|
||||
{{< feature-state for_k8s_version="v1.15" state="alpha" >}}
|
||||
|
||||
The scheduling framework is a pluggable architecture for Kubernetes Scheduler
|
||||
that makes scheduler customizations easy. It adds a new set of "plugin" APIs to
|
||||
the existing scheduler. Plugins are compiled into the scheduler. The APIs
|
||||
allow most scheduling features to be implemented as plugins, while keeping the
|
||||
scheduling "core" simple and maintainable. Refer to the [design proposal of the
|
||||
scheduling framework][kep] for more technical information on the design of the
|
||||
framework.
|
||||
|
||||
[kep]: https://github.com/kubernetes/enhancements/blob/master/keps/sig-scheduling/20180409-scheduling-framework.md
|
||||
|
||||
{{% /capture %}}
|
||||
|
||||
{{% capture body %}}
|
||||
|
||||
# Framework workflow
|
||||
|
||||
The Scheduling Framework defines a few extension points. Scheduler plugins
|
||||
register to be invoked at one or more extension points. Some of these plugins
|
||||
can change the scheduling decisions and some are informational only.
|
||||
|
||||
Each attempt to schedule one Pod is split into two phases, the **scheduling
|
||||
cycle** and the **binding cycle**.
|
||||
|
||||
## Scheduling Cycle & Binding Cycle
|
||||
|
||||
The scheduling cycle selects a node for the Pod, and the binding cycle applies
|
||||
that decision to the cluster. Together, a scheduling cycle and binding cycle are
|
||||
referred to as a "scheduling context".
|
||||
|
||||
Scheduling cycles are run serially, while binding cycles may run concurrently.
|
||||
|
||||
A scheduling or binding cycle can be aborted if the Pod is determined to
|
||||
be unschedulable or if there is an internal error. The Pod will be returned to
|
||||
the queue and retried.
|
||||
|
||||
## Extension points
|
||||
|
||||
The following picture shows the scheduling context of a Pod and the extension
|
||||
points that the scheduling framework exposes. In this picture "Filter" is
|
||||
equivalent to "Predicate" and "Scoring" is equivalent to "Priority function".
|
||||
|
||||
One plugin may register at multiple extension points to perform more complex or
|
||||
stateful tasks.
|
||||
|
||||
{{< figure src="/images/docs/scheduling-framework-extensions.png" title="scheduling framework extension points" >}}
|
||||
|
||||
### QueueSort {#queue-sort}
|
||||
|
||||
These plugins are used to sort Pods in the scheduling queue. A queue sort plugin
|
||||
essentially provides a `Less(Pod1, Pod2)` function. Only one queue sort
|
||||
plugin may be enabled at a time.
|
||||
|
||||
### PreFilter {#pre-filter}
|
||||
|
||||
These plugins are used to pre-process info about the Pod, or to check certain
|
||||
conditions that the cluster or the Pod must meet. If a PreFilter plugin returns
|
||||
an error, the scheduling cycle is aborted.
|
||||
|
||||
### Filter
|
||||
|
||||
These plugins are used to filter out nodes that cannot run the Pod. For each
|
||||
node, the scheduler will call filter plugins in their configured order. If any
|
||||
filter plugin marks the node as infeasible, the remaining plugins will not be
|
||||
called for that node. Nodes may be evaluated concurrently.
|
||||
|
||||
### PreScore {#pre-score}
|
||||
|
||||
These plugins are used to perform "pre-scoring" work, which generates a sharable
|
||||
state for Score plugins to use. If a PreScore plugin returns an error, the
|
||||
scheduling cycle is aborted.
|
||||
|
||||
### Score {#scoring}
|
||||
|
||||
These plugins are used to rank nodes that have passed the filtering phase. The
|
||||
scheduler will call each scoring plugin for each node. There will be a well
|
||||
defined range of integers representing the minimum and maximum scores. After the
|
||||
[NormalizeScore](#normalize-scoring) phase, the scheduler will combine node
|
||||
scores from all plugins according to the configured plugin weights.
|
||||
|
||||
### NormalizeScore {#normalize-scoring}
|
||||
|
||||
These plugins are used to modify scores before the scheduler computes a final
|
||||
ranking of Nodes. A plugin that registers for this extension point will be
|
||||
called with the [Score](#scoring) results from the same plugin. This is called
|
||||
once per plugin per scheduling cycle.
|
||||
|
||||
For example, suppose a plugin `BlinkingLightScorer` ranks Nodes based on how
|
||||
many blinking lights they have.
|
||||
|
||||
```go
|
||||
func ScoreNode(_ *v1.pod, n *v1.Node) (int, error) {
|
||||
return getBlinkingLightCount(n)
|
||||
}
|
||||
```
|
||||
|
||||
However, the maximum count of blinking lights may be small compared to
|
||||
`NodeScoreMax`. To fix this, `BlinkingLightScorer` should also register for this
|
||||
extension point.
|
||||
|
||||
```go
|
||||
func NormalizeScores(scores map[string]int) {
|
||||
highest := 0
|
||||
for _, score := range scores {
|
||||
highest = max(highest, score)
|
||||
}
|
||||
for node, score := range scores {
|
||||
scores[node] = score*NodeScoreMax/highest
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If any NormalizeScore plugin returns an error, the scheduling cycle is
|
||||
aborted.
|
||||
|
||||
{{< note >}}
|
||||
Plugins wishing to perform "pre-reserve" work should use the
|
||||
NormalizeScore extension point.
|
||||
{{< /note >}}
|
||||
|
||||
### Reserve
|
||||
|
||||
This is an informational extension point. Plugins which maintain runtime state
|
||||
(aka "stateful plugins") should use this extension point to be notified by the
|
||||
scheduler when resources on a node are being reserved for a given Pod. This
|
||||
happens before the scheduler actually binds the Pod to the Node, and it exists
|
||||
to prevent race conditions while the scheduler waits for the bind to succeed.
|
||||
|
||||
This is the last step in a scheduling cycle. Once a Pod is in the reserved
|
||||
state, it will either trigger [Unreserve](#unreserve) plugins (on failure) or
|
||||
[PostBind](#post-bind) plugins (on success) at the end of the binding cycle.
|
||||
|
||||
### Permit
|
||||
|
||||
_Permit_ plugins are invoked at the end of the scheduling cycle for each Pod, to
|
||||
prevent or delay the binding to the candidate node. A permit plugin can do one of
|
||||
the three things:
|
||||
|
||||
1. **approve** \
|
||||
Once all Permit plugins approve a Pod, it is sent for binding.
|
||||
|
||||
1. **deny** \
|
||||
If any Permit plugin denies a Pod, it is returned to the scheduling queue.
|
||||
This will trigger [Unreserve](#unreserve) plugins.
|
||||
|
||||
1. **wait** (with a timeout) \
|
||||
If a Permit plugin returns "wait", then the Pod is kept in an internal "waiting"
|
||||
Pods list, and the binding cycle of this Pod starts but directly blocks until it
|
||||
gets [approved](#frameworkhandle). If a timeout occurs, **wait** becomes **deny**
|
||||
and the Pod is returned to the scheduling queue, triggering [Unreserve](#unreserve)
|
||||
plugins.
|
||||
|
||||
{{< note >}}
|
||||
While any plugin can access the list of "waiting" Pods and approve them
|
||||
(see [`FrameworkHandle`](#frameworkhandle)), we expect only the permit
|
||||
plugins to approve binding of reserved Pods that are in "waiting" state. Once a Pod
|
||||
is approved, it is sent to the [PreBind](#pre-bind) phase.
|
||||
{{< /note >}}
|
||||
|
||||
### PreBind {#pre-bind}
|
||||
|
||||
These plugins are used to perform any work required before a Pod is bound. For
|
||||
example, a pre-bind plugin may provision a network volume and mount it on the
|
||||
target node before allowing the Pod to run there.
|
||||
|
||||
If any PreBind plugin returns an error, the Pod is [rejected](#unreserve) and
|
||||
returned to the scheduling queue.
|
||||
|
||||
### Bind
|
||||
|
||||
These plugins are used to bind a Pod to a Node. Bind plugins will not be called
|
||||
until all PreBind plugins have completed. Each bind plugin is called in the
|
||||
configured order. A bind plugin may choose whether or not to handle the given
|
||||
Pod. If a bind plugin chooses to handle a Pod, **the remaining bind plugins are
|
||||
skipped**.
|
||||
|
||||
### PostBind {#post-bind}
|
||||
|
||||
This is an informational extension point. Post-bind plugins are called after a
|
||||
Pod is successfully bound. This is the end of a binding cycle, and can be used
|
||||
to clean up associated resources.
|
||||
|
||||
### Unreserve
|
||||
|
||||
This is an informational extension point. If a Pod was reserved and then
|
||||
rejected in a later phase, then unreserve plugins will be notified. Unreserve
|
||||
plugins should clean up state associated with the reserved Pod.
|
||||
|
||||
Plugins that use this extension point usually should also use
|
||||
[Reserve](#reserve).
|
||||
|
||||
## Plugin API
|
||||
|
||||
There are two steps to the plugin API. First, plugins must register and get
|
||||
configured, then they use the extension point interfaces. Extension point
|
||||
interfaces have the following form.
|
||||
|
||||
```go
|
||||
type Plugin interface {
|
||||
Name() string
|
||||
}
|
||||
|
||||
type QueueSortPlugin interface {
|
||||
Plugin
|
||||
Less(*v1.pod, *v1.pod) bool
|
||||
}
|
||||
|
||||
type PreFilterPlugin interface {
|
||||
Plugin
|
||||
PreFilter(context.Context, *framework.CycleState, *v1.pod) error
|
||||
}
|
||||
|
||||
// ...
|
||||
```
|
||||
|
||||
## Plugin configuration
|
||||
|
||||
You can enable or disable plugins in the scheduler configuration. If you are using
|
||||
Kubernetes v1.18 or later, most scheduling
|
||||
[plugins](/docs/reference/scheduling/profiles/#scheduling-plugins) are in use and
|
||||
enabled by default.
|
||||
|
||||
In addition to default plugins, you can also implement your own scheduling
|
||||
plugins and get them configured along with default plugins. You can visit
|
||||
[scheduler-plugins](https://github.com/kubernetes-sigs/scheduler-plugins) for more details.
|
||||
|
||||
If you are using Kubernetes v1.18 or later, you can configure a set of plugins as
|
||||
a scheduler profile and then define multiple profiles to fit various kinds of workload.
|
||||
Learn more at [multiple profiles](/docs/reference/scheduling/profiles/#multiple-profiles).
|
||||
|
||||
{{% /capture %}}
|
||||
Reference in New Issue
Block a user