Skip to content

Commit

Permalink
add missing docs (external scheduler and plugin extender) (#287)
Browse files Browse the repository at this point in the history
* add missing docs (external scheduler and plugin extender)

* fix minor point

* fix based on the suggestion

* fix broken import

* add the important note on the top
  • Loading branch information
sanposhiho committed Apr 25, 2023
1 parent 9540f5d commit cb2365e
Show file tree
Hide file tree
Showing 16 changed files with 509 additions and 260 deletions.
93 changes: 65 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,81 @@ Nowadays, the scheduler is configurable/extendable in the multiple ways:
- etc...

But, unfortunately, not all configurations/expansions yield good results.
Those who customize the scheduler need to make sure their scheduler is working as expected, and doesn't have an unacceptably negative impact on the scheduling.
Those who customize the scheduler need to make sure their scheduler is working as expected,
and doesn't have an unacceptably negative impact on the scheduling.

In real Kubernetes, we cannot know the results of scheduling in detail without reading the logs, which usually requires privileged access to the control plane.
That's why we are developing a simulator for kube-scheduler -- you can try out the behavior of the scheduler with web UI while checking which plugin made what decision for which Node.
In real Kubernetes, we cannot know the results of scheduling in detail without reading the logs,
which usually requires privileged access to the control plane.

That's why we are developing a simulator for kube-scheduler
-- you can try out the behavior of the scheduler while checking which plugin made what decision for which Node.

## Simulator's architecture

We have several components:
- Simulator (in `/simulator`)
- Web UI (in `/web`)
- Simulator (in `/simulator`): the core implementation of the simulator
- Web UI (in `/web`): the Web client of the simulator
- Coming soon... :) (see [./keps](./keps) to see some nice ideas we're working on)

### Simulator

Simulator internally has kube-apiserver, scheduler, and HTTP server.
Simulator is kube-apiserver + scheduler + the HTTP server which mainly for the web UI.

There are several ways to integrate your scheduler into the simulator.
See [integrate-your-scheduler.md](simulator/docs/integrate-your-scheduler.md).

You can create any resources by communicating with kube-apiserver in any ways (kubectl, k8s client library, or web UI described next)
and see how scheduling is done.

When you create Pods, Pods will get annotations from the simulator which contains the scheduling results per plugins or extenders.

```yaml
kind: Pod
apiVersion: v1
metadata:
name: hoge-pod
annotations:
scheduler-simulator/bind-result: '{"DefaultBinder":"success"}'
scheduler-simulator/filter-result: >-
{"node-282x7":{"AzureDiskLimits":"passed","EBSLimits":"passed","GCEPDLimits":"passed","InterPodAffinity":"passed","NodeAffinity":"passed","NodeName":"passed","NodePorts":"passed","NodeResourcesFit":"passed","NodeUnschedulable":"passed","NodeVolumeLimits":"passed","PodTopologySpread":"passed","TaintToleration":"passed","VolumeBinding":"passed","VolumeRestrictions":"passed","VolumeZone":"passed"},"node-gp9t4":{"AzureDiskLimits":"passed","EBSLimits":"passed","GCEPDLimits":"passed","InterPodAffinity":"passed","NodeAffinity":"passed","NodeName":"passed","NodePorts":"passed","NodeResourcesFit":"passed","NodeUnschedulable":"passed","NodeVolumeLimits":"passed","PodTopologySpread":"passed","TaintToleration":"passed","VolumeBinding":"passed","VolumeRestrictions":"passed","VolumeZone":"passed"}}
scheduler-simulator/finalscore-result: >-
{"node-282x7":{"ImageLocality":"0","InterPodAffinity":"0","NodeAffinity":"0","NodeNumber":"0","NodeResourcesBalancedAllocation":"76","NodeResourcesFit":"73","PodTopologySpread":"200","TaintToleration":"300","VolumeBinding":"0"},"node-gp9t4":{"ImageLocality":"0","InterPodAffinity":"0","NodeAffinity":"0","NodeNumber":"0","NodeResourcesBalancedAllocation":"76","NodeResourcesFit":"73","PodTopologySpread":"200","TaintToleration":"300","VolumeBinding":"0"}}
scheduler-simulator/permit-result: '{}'
scheduler-simulator/permit-result-timeout: '{}'
scheduler-simulator/postfilter-result: '{}'
scheduler-simulator/prebind-result: '{"VolumeBinding":"success"}'
scheduler-simulator/prefilter-result: '{}'
scheduler-simulator/prefilter-result-status: >-
{"InterPodAffinity":"success","NodeAffinity":"success","NodePorts":"success","NodeResourcesFit":"success","PodTopologySpread":"success","VolumeBinding":"success","VolumeRestrictions":"success"}
scheduler-simulator/prescore-result: >-
{"InterPodAffinity":"success","NodeAffinity":"success","NodeNumber":"success","PodTopologySpread":"success","TaintToleration":"success"}
scheduler-simulator/reserve-result: '{"VolumeBinding":"success"}'
scheduler-simulator/result-history: >-
[{"noderesourcefit-prefilter-data":"{\"MilliCPU\":100,\"Memory\":17179869184,\"EphemeralStorage\":0,\"AllowedPodNumber\":0,\"ScalarResources\":null}","scheduler-simulator/bind-result":"{\"DefaultBinder\":\"success\"}","scheduler-simulator/filter-result":"{\"node-282x7\":{\"AzureDiskLimits\":\"passed\",\"EBSLimits\":\"passed\",\"GCEPDLimits\":\"passed\",\"InterPodAffinity\":\"passed\",\"NodeAffinity\":\"passed\",\"NodeName\":\"passed\",\"NodePorts\":\"passed\",\"NodeResourcesFit\":\"passed\",\"NodeUnschedulable\":\"passed\",\"NodeVolumeLimits\":\"passed\",\"PodTopologySpread\":\"passed\",\"TaintToleration\":\"passed\",\"VolumeBinding\":\"passed\",\"VolumeRestrictions\":\"passed\",\"VolumeZone\":\"passed\"},\"node-gp9t4\":{\"AzureDiskLimits\":\"passed\",\"EBSLimits\":\"passed\",\"GCEPDLimits\":\"passed\",\"InterPodAffinity\":\"passed\",\"NodeAffinity\":\"passed\",\"NodeName\":\"passed\",\"NodePorts\":\"passed\",\"NodeResourcesFit\":\"passed\",\"NodeUnschedulable\":\"passed\",\"NodeVolumeLimits\":\"passed\",\"PodTopologySpread\":\"passed\",\"TaintToleration\":\"passed\",\"VolumeBinding\":\"passed\",\"VolumeRestrictions\":\"passed\",\"VolumeZone\":\"passed\"}}","scheduler-simulator/finalscore-result":"{\"node-282x7\":{\"ImageLocality\":\"0\",\"InterPodAffinity\":\"0\",\"NodeAffinity\":\"0\",\"NodeNumber\":\"0\",\"NodeResourcesBalancedAllocation\":\"76\",\"NodeResourcesFit\":\"73\",\"PodTopologySpread\":\"200\",\"TaintToleration\":\"300\",\"VolumeBinding\":\"0\"},\"node-gp9t4\":{\"ImageLocality\":\"0\",\"InterPodAffinity\":\"0\",\"NodeAffinity\":\"0\",\"NodeNumber\":\"0\",\"NodeResourcesBalancedAllocation\":\"76\",\"NodeResourcesFit\":\"73\",\"PodTopologySpread\":\"200\",\"TaintToleration\":\"300\",\"VolumeBinding\":\"0\"}}","scheduler-simulator/permit-result":"{}","scheduler-simulator/permit-result-timeout":"{}","scheduler-simulator/postfilter-result":"{}","scheduler-simulator/prebind-result":"{\"VolumeBinding\":\"success\"}","scheduler-simulator/prefilter-result":"{}","scheduler-simulator/prefilter-result-status":"{\"InterPodAffinity\":\"success\",\"NodeAffinity\":\"success\",\"NodePorts\":\"success\",\"NodeResourcesFit\":\"success\",\"PodTopologySpread\":\"success\",\"VolumeBinding\":\"success\",\"VolumeRestrictions\":\"success\"}","scheduler-simulator/prescore-result":"{\"InterPodAffinity\":\"success\",\"NodeAffinity\":\"success\",\"NodeNumber\":\"success\",\"PodTopologySpread\":\"success\",\"TaintToleration\":\"success\"}","scheduler-simulator/reserve-result":"{\"VolumeBinding\":\"success\"}","scheduler-simulator/score-result":"{\"node-282x7\":{\"ImageLocality\":\"0\",\"InterPodAffinity\":\"0\",\"NodeAffinity\":\"0\",\"NodeNumber\":\"0\",\"NodeResourcesBalancedAllocation\":\"76\",\"NodeResourcesFit\":\"73\",\"PodTopologySpread\":\"0\",\"TaintToleration\":\"0\",\"VolumeBinding\":\"0\"},\"node-gp9t4\":{\"ImageLocality\":\"0\",\"InterPodAffinity\":\"0\",\"NodeAffinity\":\"0\",\"NodeNumber\":\"0\",\"NodeResourcesBalancedAllocation\":\"76\",\"NodeResourcesFit\":\"73\",\"PodTopologySpread\":\"0\",\"TaintToleration\":\"0\",\"VolumeBinding\":\"0\"}}","scheduler-simulator/selected-node":"node-282x7"}]
scheduler-simulator/score-result: >-
{"node-282x7":{"ImageLocality":"0","InterPodAffinity":"0","NodeAffinity":"0","NodeNumber":"0","NodeResourcesBalancedAllocation":"76","NodeResourcesFit":"73","PodTopologySpread":"0","TaintToleration":"0","VolumeBinding":"0"},"node-gp9t4":{"ImageLocality":"0","InterPodAffinity":"0","NodeAffinity":"0","NodeNumber":"0","NodeResourcesBalancedAllocation":"76","NodeResourcesFit":"73","PodTopologySpread":"0","TaintToleration":"0","VolumeBinding":"0"}}
scheduler-simulator/selected-node: node-282x7
```

You can utilize these results to understand your scheduler, check/test your configurations or customized scheduler's behavior.

You can create any resources by communicating with kube-apiserver via kubectl, k8s client library, or web UI.
Further expansion, you can export internal state more, change specific behaviours on plugins etc by implementing [PluginExtender](./simulator/docs/plugin-extender.md).

The simulator has its own configuration,
you can refer to the [documentation](./simulator/docs/simulator-server-config.md).

See the following docs to know more about simulator:
- [how-it-works.md](simulator/docs/how-it-works.md): describes about how the simulator works.
- [kube-apiserver.md](simulator/docs/kube-apiserver.md): describe about kube-apiserver in simulator. (how you can configure and access)
- [api.md](simulator/docs/api.md): describes about HTTP server the simulator has.
- [api.md](simulator/docs/api.md): describes about HTTP server the simulator has. (mainly for the webUI)

### Web UI

Web UI is one of the clients of simulator, but it's optimized for simulator.
It's well-optimized Web UI for the simulator.

Web UI is the best way to check the scheduler's behavior very easily.
Nice table view for the scheduling result, the scheduler configuration reload feature, etc...
every core features of the simulator can be access with human-friendly ways!

From the web, you can create/edit/delete these resources to simulate a cluster.

Expand All @@ -48,7 +97,7 @@ From the web, you can create/edit/delete these resources to simulate a cluster.

![list resources](simulator/docs/images/resources.png)

You can create resources with yaml file as usual.
It has a cool yaml editor to create/edit resources.

![create node](simulator/docs/images/create-node.png)

Expand All @@ -60,27 +109,14 @@ And, after pods are scheduled, you can see the results of

![result](simulator/docs/images/result.jpg)

You can configure the scheduler on the simulator through KubeSchedulerConfiguration.

[Scheduler Configuration | Kubernetes](https://kubernetes.io/docs/reference/scheduling/config/)
Also, You can change the configuration of the scheduler through [KubeSchedulerConfiguration](https://kubernetes.io/docs/reference/scheduling/config/).

You can pass a path to KubeSchedulerConfiguration via the environment variable `KUBE_SCHEDULER_CONFIG_PATH` and the simulator will start kube-scheduler with that configuration.

Note: changes to any fields other than `.profiles` are disabled on simulator, since they do not affect the results of the scheduling.
(Note: changes to any fields other than `.profiles` are disabled on simulator, since they do not affect the results of the scheduling.)

![configure scheduler](simulator/docs/images/schedulerconfiguration.png)

If you want to use your custom plugins as out-of-tree plugins in the simulator, please follow [this doc](simulator/docs/how-to-use-custom-plugins/README.md).

## Getting started

You can find more information about environment variables available in the simulator server
[here.](./simulator/docs/environment-variables.md)


The simulator server configuration can also be set through the configuration file,
you can refer to the [documentation](./simulator/docs/simulator-server-config.md).

### Run simulator with Docker

We have [docker-compose.yml](docker-compose.yml) to run the simulator easily. You should install [docker](https://docs.docker.com/engine/install/) and [docker-compose](https://docs.docker.com/compose/install/) firstly.
Expand All @@ -101,7 +137,7 @@ Please allocate enough memory in that case.

You have to run frontend, server and etcd.

#### Run simulator server and etcd
#### 1. Run simulator server and etcd

To run this simulator's server, you have to install Go and etcd.

Expand All @@ -114,12 +150,13 @@ make start

It starts etcd and simulator-server locally.

#### Run simulator frontend
#### 2. Run simulator frontend

To run the frontend, please see [README.md](web/README.md) on ./web dir.

## [Beta] Existing cluster importing
## Beta features

### [Beta] Existing cluster importing

The simulator can import resources from your cluster.
You can use it by setting an `EXTERNAL_IMPORT_ENABLED` environment variable to `1`.
Expand Down
56 changes: 56 additions & 0 deletions simulator/docs/custom-plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
## How to use your custom plugins in the simulator

This doc describes how to use your custom plugins in the scheduler running in the simulator.

### 1. Add your custom plugin's registry in OutOfTreeRegistries function.

Please add your custom plugin's registry in `outOfTreeRegistries` in config package here:

[kube-scheduler-simulator/simulator/scheduler/config/plugin.go](/simulator/scheduler/config/plugin.go)

### 2. Configure the scheduler to enable your custom plugin

You can configure the scheduler to use your custom plugins through KubeSchedulerConfiguration.

[Scheduler Configuration | Kubernetes](https://kubernetes.io/docs/reference/scheduling/config/)

You can change the scheduler configuration in Web UI or
by passing a default KubeSchedulerConfiguration file via the environment variable `KUBE_SCHEDULER_CONFIG_PATH`.

### Example

We will explain the case where you want to add [nodenumber](../sample/nodenumber/plugin.go) plugin as example.

The nodenumber plugin is an example plugin that favors nodes that have the number suffix which is the same as the number suffix of the pod name.
And we can configure it via `NodeNumberArgs`.

First, you need to add registry for the nodenumber plugin to `outOfTreeRegistries`.

```go
outOfTreeRegistries = runtime.Registry{
// TODO(user): add your plugins registries here.
nodenumber.Name: nodenumber.New,
}
```

Now you can use the nodenumber plugin in the simulator.

If you apply this configuration to the scheduler, you can see the nodenumber plugin is working (and NodeNumberArgs is applied to the nodenumber plugin) in the simulator,
and see the nodenumber plugin's result like other in-tree plugins.

```yaml
kind: KubeSchedulerConfiguration
apiVersion: kubescheduler.config.k8s.io/v1
# ....
profiles:
- schedulerName: default-scheduler
plugins:
# ....
multiPoint:
enabled:
# ....
- name: NodeNumber # added
weight: 10
```

![result](images/result-nodenumber.jpg)
18 changes: 9 additions & 9 deletions simulator/docs/environment-variables.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
# Environment Variables
## [deprecated] Environment Variables

This page describes the environment variables that are used to configure the simulator.

Please refer to [docker-compose.yml](./../../docker-compose.yml) as an example use.
**Deprecation notice**: We're planning to remove the configuration via environment variables.
Until deprecation, the simulator will read the configuration in the environment variable first,
if the environment variable is not set, it will read the configuration in the configuration file.
For config file, please refer to the simulator [config.yaml](./../config.yaml).

## For Simulator
---

**Deprecation notice**: We're planning to remove the configuration via environment variables.
This page describes the environment variables that are used to configure the simulator.

Until deprecation, the simulator will read the configuration in the environment variable first,
if the environment variable is not set, it will read the configuration in the configuration file.
Please refer to [docker-compose.yml](./../../docker-compose.yml) as an example use.

For config file, please refer to the simulator [config.yaml](./../config.yaml).
### For Simulator

`PORT`: (required) This is the port number on which kube-scheduler-simulator
server is started.
Expand Down
90 changes: 90 additions & 0 deletions simulator/docs/external-scheduler.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
## External scheduler

This document describes how to use the external scheduler instead of the scheduler running in the simulator.

We use the [`externalscheduler` package](../pkg/externalscheduler);
the scheduler built with the [`externalscheduler` package](../pkg/externalscheduler) will export the scheduling results on each Pod annotation.

### Use cases

- Running your scheduler instead of the default one in the simulator
- You can still see the scheduling results in web UI as well!
- Running your scheduler with the simulator feature in your cluster
- All Pods, scheduled by this scheduler, will get the scheduler results on its annotation while each scheduling is done as usual.
- Note that it has performance overhead in each scheduling cycle
since the scheduler needs to make additional effort to export the scheduling results.

### Change your scheduler

Here, we assume you're registering your custom plugins in your scheduler like this:

```go
// your scheduler's main package
func main() {
command := app.NewSchedulerCommand(
app.WithPlugin(yourcustomplugin.Name, yourcustomplugin.New),
)

code := cli.Run(command)
os.Exit(code)
}
```

Then, you need to replace few lines to use the [`externalscheduler` package](../pkg/externalscheduler).

```go
func main() {
command, cancelFn, err := externalscheduler.NewSchedulerCommand(
externalscheduler.WithPlugin(yourcustomplugin.Name, yourcustomplugin.New),
externalscheduler.WithPluginExtenders(noderesources.Name, extender.New), // [optional] see plugin-extender.md about PluginExtender.
)
if err != nil {
klog.Info(fmt.Sprintf("failed to build the scheduler command: %+v", err))
os.Exit(1)
}
code := cli.Run(command)
cancelFn()
os.Exit(code)
}
```

As you see, `externalscheduler.NewSchedulerCommand` has much similar interface as the `app.NewSchedulerCommand`.
You can register your plugins by `externalscheduler.WithPlugin` option.

Via this step, all Pods scheduled by this scheduler will get the scheduling results in the annotation like in the simulator!

### Connect your scheduler to the kube-apiserver in the simulator

If you are here to run the scheduler built with [`externalscheduler` package](../pkg/externalscheduler) in your cluster,
you don't need to follow this step.

Let's connect your scheduler into the simulator.

First, you need to set `externalSchedulerEnabled: true` on [the simulator config](../config.yaml)
so that the scheduler in the simulator won't get started.

Next, you need to connect your scheduler into the simulator's kube-apiserver via KubeSchedulerConfig:

```yaml
kind: KubeSchedulerConfiguration
apiVersion: kubescheduler.config.k8s.io/v1
clientConnection:
kubeconfig: ./path/to/kubeconfig.yaml
```

You can use this [kubeconfig.yaml](./kubeconfig.yaml) to communicate with the simulator's kube-apiserver.

### The example external scheduler

We have the sample external scheduler implementation in [./sample/external-scheduler](./sample/external-scheduler).

prerequisite:
1. set `externalSchedulerEnabled: true` on [the simulator config](../config.yaml)
2. run the simulator

```shell
cd sample/external-scheduler
go run main.go --config scheduler.yaml
```

You'll see the simulator is working with the external scheduler.
Loading

0 comments on commit cb2365e

Please sign in to comment.