kubernetes-sigs / scheduler-plugins

Repository for out-of-tree scheduler plugins based on scheduler framework.
Apache License 2.0
1.05k stars 484 forks source link

schduler-plugin calls the filter interface but does not call (sort,score) types of interfaces #759

Closed guojianyu closed 2 weeks ago

guojianyu commented 3 weeks ago

Area

Other components

No response

What happened?

schduler-plugin calls the filter interface but does not call (sort,score) types of interfaces. The code is as follows:

package main
import (
    "context"
    "fmt"
    "os"
    "time"

    v1 "k8s.io/api/core/v1"
    "k8s.io/apimachinery/pkg/runtime"
    "k8s.io/component-base/cli"
    _ "k8s.io/component-base/logs/json/register" // for JSON log format registration
    _ "k8s.io/component-base/metrics/prometheus/clientgo"
    _ "k8s.io/component-base/metrics/prometheus/version" // for version metric registration
    "k8s.io/klog/v2"
    "k8s.io/kubernetes/cmd/kube-scheduler/app"
    "k8s.io/kubernetes/pkg/scheduler/framework"
)

func main() {
    myPlugin := app.WithPlugin("nodeAge", New)

    command := app.NewSchedulerCommand(myPlugin)
    code := cli.Run(command)
    os.Exit(code)
}

type NodeAge struct {
    handle framework.Handle
}

func (pl *NodeAge) Name() string {
    return "nodeAge"
}

var _ = framework.ScorePlugin(&NodeAge{})
var _ = framework.QueueSortPlugin(&NodeAge{})
var _ = framework.FilterPlugin(&NodeAge{})
var _ = framework.PreScorePlugin(&NodeAge{})
var _ = framework.PreFilterPlugin(&NodeAge{})

func (c *NodeAge) PreFilter(ctx context.Context, state *framework.CycleState, pod *v1.Pod) (*framework.PreFilterResult, *framework.Status) {
    klog.Infof("PreFilter is called for Pod: %s/%s", pod.Namespace, pod.Name)
    return nil, nil
}

func (c *NodeAge) PreFilterExtensions() framework.PreFilterExtensions {
    return nil
}

func (pl *NodeAge) Filter(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status {
    klog.Infof("filter pod: %v", pod.Name)
    //klog.Infof("filter state: %v", state)
    node := nodeInfo.Node()
    if node == nil {
        return framework.NewStatus(framework.Error, "node not found")
    }

    return framework.NewStatus(framework.Success, "Node: "+node.Name)
}

func getPriority(pod *v1.Pod) int {

    return 0 // TODO: Replace with actual logic
}

func (pl *NodeAge) Less(pod1, pod2 *framework.QueuedPodInfo) bool {
    klog.Infof("Less: %v,%v", pod1, pod2)
    priority1 := getPriority(pod1.Pod)
    priority2 := getPriority(pod2.Pod)
    if emer, ok := pod1.Pod.Labels["emergency"]; ok && emer == "red" {
        return true
    } else {
        return priority1 > priority2
    }
    //return priority1 > priority2 
}

func (p *NodeAge) PreScore(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodes []*v1.Node) *framework.Status {
    klog.Infof("PreScore plugin executed for Pod %s/%s", pod.Namespace, pod.Name)
    for _, node := range nodes {
        klog.Infof("Node available: %s", node.Name)
    }
    return framework.NewStatus(framework.Success, "")
}

func (pl *NodeAge) Score(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) {
    nodeInfo, err := pl.handle.SnapshotSharedLister().NodeInfos().Get(nodeName)
    if err != nil {
        return 0, framework.AsStatus(fmt.Errorf("getting node %q from Snapshot: %w", nodeName, err))
    }
    tsCreation := nodeInfo.Node().ObjectMeta.CreationTimestamp.Second()
    tsCurrent := time.Now().Second()
    score := 0
    if tsCurrent-tsCreation < 15*3600*24 {
        score += 1
        klog.InfoS("node get score", "pod_name", pod.Name, "current node", nodeInfo.Node().Name, "node age(days)", (tsCurrent-tsCreation)/3600/24)
    } else {
        klog.InfoS("node can't get score", "pod_name", pod.Name, "current node", nodeInfo.Node().Name, "node age(days)", (tsCurrent-tsCreation)/3600/24)
    }
    return int64(score), nil
}

func (pl *NodeAge) ScoreExtensions() framework.ScoreExtensions {
    return pl
}

func (pl *NodeAge) NormalizeScore(ctx context.Context, state *framework.CycleState, pod *v1.Pod, scores framework.NodeScoreList) *framework.Status {
    return framework.NewStatus(framework.Success, "")
}

func New(_ runtime.Object, h framework.Handle) (framework.Plugin, error) {
    return &NodeAge{handle: h}, nil
}

The Scheduler_config.yaml is as follows:

apiVersion: kubescheduler.config.k8s.io/v1
leaderElection:
  leaderElect: true
clientConnection:
  kubeconfig: "/etc/kubernetes/scheduler.conf"
kind: KubeSchedulerConfiguration
profiles:
  - schedulerName: my-scheduler
    plugins:
      preFilter:
        enabled:
        - name: nodeAge
      filter:
        enabled:
        - name: nodeAge
      preScore:
        enabled:
        - name: nodeAge
      score:
        enabled:
        - name: nodeAge
      queueSort:
        enabled:
        - name: nodeAge
        disabled:
        - name: "*"

scheduler-deploy.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: test
  template:
    metadata:
      labels:
        app: test
    spec:
      schedulerName: my-scheduler
      containers:
      - image: nginx
        imagePullPolicy: IfNotPresent       
        name: nginx

The scheduler log output is as follows: image

Is this a problem with my code or something else? If it is a code problem, how can I fix it? Thank you very much!! 🙏

What did you expect to happen?

Sort and Score interface can be called normally.

How can we reproduce it (as minimally and precisely as possible)?

No response

Anything else we need to know?

I upgraded the scheduler-plugin to the latest version with the same problem

Kubernetes version

```console $ kubectl version # paste output here ``` ![image](https://github.com/kubernetes-sigs/scheduler-plugins/assets/21234963/482ade87-5c9a-4ef2-8aff-1d846e29a85a)

Scheduler Plugins version

release-1.26

googs1025 commented 3 weeks ago

I would like to confirm the core issue. Your question is, you wrote a custom plugin, and then found that only the filter method was executed, but the score method was not executed, right? You can first check how many nodes your test cluster has. If there is only a single node, the scheduler will only use this node for scheduling by default and will not execute scoring process. refer to: https://github.com/kubernetes/kubernetes/blob/master/pkg/scheduler/schedule_one.go#L428

Please forgive me if my understanding is wrong.

guojianyu commented 3 weeks ago

I would like to confirm the core issue. Your question is, you wrote a custom plugin, and then found that only the filter method was executed, but the score method was not executed, right? You can first check how many nodes your test cluster has. If there is only a single node, the scheduler will only use this node for scheduling by default and will not execute scoring process. refer to: https://github.com/kubernetes/kubernetes/blob/master/pkg/scheduler/schedule_one.go#L428

Please forgive me if my understanding is wrong.

Thank you very much for your reply,You understood me well, but my questions were not clear enough.According to the code you provided, I can understand that the scoring method did not execute, but the queuesort method did not execute, I understand that queuesort has nothing to do with the number of nodes.I hope you can guide me,Thank you!!

googs1025 commented 2 weeks ago

queueSort: enabled:

  • name: nodeAge disabled:
  • name: "*"

You can confirm whether the configuration is correct.

guojianyu commented 2 weeks ago

queueSort: enabled:

  • name: nodeAge disabled:
  • name: "*"

You can confirm whether the configuration is correct.

I0617 10:38:41.617760   29590 configfile.go:101] "Using component config" config=<
    apiVersion: kubescheduler.config.k8s.io/v1
    clientConnection:
      acceptContentTypes: ""
      burst: 100
      contentType: application/vnd.kubernetes.protobuf
      kubeconfig: /etc/kubernetes/scheduler.conf
      qps: 50
    enableContentionProfiling: true
    enableProfiling: true
    kind: KubeSchedulerConfiguration
    leaderElection:
      leaderElect: false
      leaseDuration: 15s
      renewDeadline: 10s
      resourceLock: leases
      resourceName: kube-scheduler
      resourceNamespace: kube-system
      retryPeriod: 2s
    parallelism: 16
    percentageOfNodesToScore: 0
    podInitialBackoffSeconds: 1
    podMaxBackoffSeconds: 10
    profiles:
    - pluginConfig:
      - args:
          apiVersion: kubescheduler.config.k8s.io/v1
          kind: DefaultPreemptionArgs
          minCandidateNodesAbsolute: 100
          minCandidateNodesPercentage: 10
        name: DefaultPreemption
      - args:
          apiVersion: kubescheduler.config.k8s.io/v1
          hardPodAffinityWeight: 1
          ignorePreferredTermsOfExistingPods: false
          kind: InterPodAffinityArgs
        name: InterPodAffinity
      - args:
          apiVersion: kubescheduler.config.k8s.io/v1
          kind: NodeAffinityArgs
        name: NodeAffinity
      - args:
          apiVersion: kubescheduler.config.k8s.io/v1
          kind: NodeResourcesBalancedAllocationArgs
          resources:
          - name: cpu
            weight: 1
          - name: memory
            weight: 1
        name: NodeResourcesBalancedAllocation
      - args:
          apiVersion: kubescheduler.config.k8s.io/v1
          kind: NodeResourcesFitArgs
          scoringStrategy:
            resources:
            - name: cpu
              weight: 1
            - name: memory
              weight: 1
            type: LeastAllocated
        name: NodeResourcesFit
      - args:
          apiVersion: kubescheduler.config.k8s.io/v1
          defaultingType: System
          kind: PodTopologySpreadArgs
        name: PodTopologySpread
      - args:
          apiVersion: kubescheduler.config.k8s.io/v1
          bindTimeoutSeconds: 600
          kind: VolumeBindingArgs
        name: VolumeBinding
      plugins:
        bind: {}
        filter:
          enabled:
          - name: nodeAge
            weight: 0
        multiPoint:
          enabled:
          - name: PrioritySort
            weight: 0
          - name: NodeUnschedulable
            weight: 0
          - name: NodeName
            weight: 0
          - name: TaintToleration
            weight: 3
          - name: NodeAffinity
            weight: 2
          - name: NodePorts
            weight: 0
          - name: NodeResourcesFit
            weight: 1
          - name: VolumeRestrictions
            weight: 0
          - name: EBSLimits
            weight: 0
          - name: GCEPDLimits
            weight: 0
          - name: NodeVolumeLimits
            weight: 0
          - name: AzureDiskLimits
            weight: 0
          - name: VolumeBinding
            weight: 0
          - name: VolumeZone
            weight: 0
          - name: PodTopologySpread
            weight: 2
          - name: InterPodAffinity
            weight: 2
          - name: DefaultPreemption
            weight: 0
          - name: NodeResourcesBalancedAllocation
            weight: 1
          - name: ImageLocality
            weight: 1
          - name: DefaultBinder
            weight: 0
          - name: SchedulingGates
            weight: 0
        permit: {}
        postBind: {}
        postFilter: {}
        preBind: {}
        preEnqueue: {}
        preFilter: {}
        preScore: {}
        queueSort:
          disabled:
          - name: '*'
            weight: 0
          enabled:
          - name: nodeAge
            weight: 0
        reserve: {}
        score:
          enabled:
          - name: nodeAge
            weight: 0
      schedulerName: my-scheduler
 >

This is the scheduler output configuration, configuration should be no problem, I tested https://github.com/kubernetes-sigs/scheduler-plugins/tree/master/pkg/qos also no log output, this is very strange

guojianyu commented 2 weeks ago

I've found the cause, the pod schedule is too fast to cause the problem, the queuesort method will not be called if the queue length is equal to 1