更新时间:2025-01-06 GMT+08:00

负载感知调度

Volcano调度器提供节点CPU、Memory的负载感知调度能力,感知集群内节点CPU、Memory的负载情况,将Pod优先调度到负载较低的节点,实现节点负载均衡,避免出现因单个节点负载过高而导致的应用程序或节点故障。

前提条件

功能介绍

原生Kubernetes调度器只能基于资源的申请值进行调度,然而Pod的真实资源使用率,往往与其所申请资源的Request/Limit差异很大,这直接导致了集群负载不均的问题:

  1. 集群中的部分节点,资源的真实使用率远低于资源申请值的分配率,却没有被调度更多的Pod,这造成了比较大的资源浪费。
  2. 集群中的另外一些节点,其资源的真实使用率事实上已经过载,却无法为调度器所感知到,这极大可能影响到业务的稳定性。

Volcano提供基于真实负载调度的能力,在资源满足的情况下,Pod优先被调度至真实负载低的节点,集群各节点负载趋于均衡。

随着集群状态,工作负载流量与请求的动态变化,节点的利用率也在实时变化,为防止Pod调度完成后,集群再次出现负载极端不均衡的情况下,Volcano同时提供重调度能力,通过负载感知和热点打散重调度结合使用,可以获得集群最佳的负载均衡效果。关于热点打散重调度能力的使用请参见重调度(Descheduler)

工作原理

负载感知调度能力由Volcano与CCE云原生监控插件配合完成,开启该能力时,按照Prometheus adapt规则定义负载感知调度所需的CPU、Memory指标信息,CCE云原生监控系统按照定义的指标规则采集并保存各节点的CPU、Memory的真实负载信息,Volcano根据CCE云原生监控系统提供的CPU、Memory真实负载信息对节点进行打分排序,优先选择负载更低的节点参与调度。

负载感知调度能力考虑CPU和Memory两个维度,采用加权平均的方式对各节点打分,包括CPU、Memory资源维度权重和负载感知策略自身权重,优先选择得分最高的节点参与调度。CPU、Memory和插件自身权重可以通过“配置中心>调度配置”自定义配置。

节点得分计算公式: 负载感知策略权重 *((1 - CPU资源利用率) * CPU权重 + (1 - Memory资源利用率) * 内存权重)/(CPU权重 + 内存权重)

  • CPU资源利用率:所有节点最近10分钟的CPU平均利用率,采集频率可通过Prometheus adapt规则进行修改。
  • Memory资源利用率:所有节点最近10分钟的Memory平均利用率
  1. 安装CCE云原生监控插件后,您需要开启Metrics API以提供容器资源指标的能力,如CPU、内存使用量。

    仅云原生监控插件开启本地数据存储时,可通过Metrics API提供资源指标。

    首先执行以下命令查询集群中是否已开启Metrics API,如果已开启则可跳过本步骤。

    kubectl get APIServices | grep v1beta1.metrics.k8s.io

    若存在回显,则表示Metrics API已开启,可跳过本步骤进行下一步添加指标采集规则。

    若未查询到Metrics API,要将其开启,可手动创建对应APIService对象。

    1. 创建一个文件,命名为metrics-apiservice.yaml。文件内容如下:
      apiVersion: apiregistration.k8s.io/v1
      kind: APIService
      metadata:
        labels:
          app: custom-metrics-apiserver
          release: cceaddon-prometheus
        name: v1beta1.metrics.k8s.io
      spec:
        group: metrics.k8s.io
        groupPriorityMinimum: 100
        insecureSkipTLSVerify: true
        service:
          name: custom-metrics-apiserver
          namespace: monitoring
          port: 443
        version: v1beta1
        versionPriority: 100
    2. 然后执行以下命令创建对应APIService对象。
      kubectl create -f metrics-apiservice.yaml
    3. 再次执行以下命令查询集群中是否已开启Metrics API。
      kubectl get APIServices | grep v1beta1.metrics.k8s.io

      开启Metrics API后,如果需要卸载云原生监控插件,请执行以下kubectl命令,同时删除APIService对象,否则残留的APIService资源将导致Kubernetes Metrics Server插件安装失败。

      kubectl delete APIService v1beta1.metrics.k8s.io

  2. 添加自定义指标采集规则。

    1. 修改user-adapter-config配置项。
      kubectl edit configmap user-adapter-config -n monitoring
    2. 在rules字段下添加自定义指标采集规则。
      自定义指标采集规则如下,红色为本次添加的指标采集规则,黑色为已有规则,在已有规则基础上添加红色规则即可。
      ...
      data:
        config.yaml: >
          rules:
          - seriesQuery: '{__name__=~"node_cpu_seconds_total"}'
            resources:
              overrides:
                instance:
                  resource: node
            name:
              matches: node_cpu_seconds_total
              as: node_cpu_usage_avg
            metricsQuery: avg_over_time((1 - avg (irate(<<.Series>>{mode="idle"}[5m])) by (instance))[10m:30s])
          - seriesQuery: '{__name__=~"node_memory_MemTotal_bytes"}'
            resources:
              overrides:
                instance:
                  resource: node
            name:
              matches: node_memory_MemTotal_bytes
              as: node_memory_usage_avg
            metricsQuery: avg_over_time(((1-node_memory_MemAvailable_bytes/<<.Series>>))[10m:30s])
          resourceRules:
            cpu:
              containerQuery: sum(rate(container_cpu_usage_seconds_total{<<.LabelMatchers>>,container!="",pod!=""}[1m])) by (<<.GroupBy>>)
              nodeQuery: sum(rate(container_cpu_usage_seconds_total{<<.LabelMatchers>>, id='/'}[1m])) by (<<.GroupBy>>)
              resources:
                overrides:
                  instance:
                    resource: node
                  namespace:
                    resource: namespace
                  pod:
                    resource: pod
              containerLabel: container
            memory:
              containerQuery: sum(container_memory_working_set_bytes{<<.LabelMatchers>>,container!="",pod!=""}) by (<<.GroupBy>>)
              nodeQuery: sum(container_memory_working_set_bytes{<<.LabelMatchers>>,id='/'}) by (<<.GroupBy>>)
              resources:
                overrides:
                  instance:
                    resource: node
                  namespace:
                    resource: namespace
                  pod:
                    resource: pod
              containerLabel: container
            window: 1m
      ...
      • CPU平均利用率采集规则
        • node_cpu_usage_avg:表示节点的CPU平均利用率,该指标名不可修改。
        • metricsQuery: avg_over_time((1 - avg (irate(<<.Series>>{mode="idle"}[5m])) by (instance))[10m:30s]):为节点CPU平均利用率的查询语句。

          当前metricsQuery表示查询所有节点最近10分钟的CPU平均利用率,如果希望调整平均值的计算周期为最近5分钟或者30分钟,可以修改上述标红的10m为5m或者30m即可。

      • Memory平均利用率采集规则
        • node_memory_usage_avg:表示节点的Memory利用率,该指标名不可修改。
        • metricsQuery: avg_over_time(((1-node_memory_MemAvailable_bytes/<<.Series>>))[10m:30s]):为节点Memory平均利用率的查询语句。

          当前metricsQuery表示查询所有节点最近10分钟的Memory平均利用率,如果希望调整平均值的计算周期为最近5分钟或者30分钟,可以修改上述标红的10m为5m或者30m即可。

    3. 重新部署monitoring命名空间下的custom-metrics-apiserver工作负载。
      kubectl rollout restart deployment custom-metrics-apiserver -n monitoring
    4. 验证自定义规则配置成功。
      1. 执行以下命令,若可以正常返回自定义指标信息,表示Prometheus侧指标采集配置成功。
        kubectl get --raw=/apis/custom.metrics.k8s.io/v1beta1

      2. 执行以下命令,查询集群内节点信息。
        kubectl get nodes 

        然后任选一个节点执行以下命令,其中xxxx替换为查询到的node_name,如果需要查询所有节点资源信息,可以使用*代替xxxx

        kubectl get --raw=/apis/custom.metrics.k8s.io/v1beta1/nodes/xxxx/node_cpu_usage_avg

        查询结果示例如下:

  3. 开启负载感知调度能力。

    安装Volcano后,您可通过“配置中心 > 调度配置”选择开启或关闭负载感知调度能力,默认关闭。
    1. 登录CCE控制台。
    2. 单击集群名称进入集群,在左侧选择“配置中心”,在右侧选择“调度配置”页签。
    3. 在“资源利用率优化调度”配置中,修改负载感知调度配置。

      为达到最优的负载感知调度效果,可以选择关闭装箱(binpack)策略。装箱策略(binpack)根据Pod的Request资源信息,将Pod优先调度到资源消耗较多的节点,在一定程度上会影响负载感知调度的效果。多种策略的结合使用案例可参考资源利用率优化调度配置案例

      参数

      说明

      默认值

      负载感知调度策略权重

      增大该权重值,可提高负载感知策略在整体调度中的影响力。

      5

      CPU权重

      增大该权重值,优先均衡CPU资源。

      1

      内存权重

      增大该权重值,优先均衡内存资源。

      1

      真实负载阈值生效方式

      • 软约束:节点CPU、内存真实负载达到阈值后,新的任务优先被分配至真实负载未达到阈值的节点,但是该节点依然允许调度。
      • 硬约束:节点CPU、内存真实负载达到阈值后,该节点不允许调度新的任务。

      硬约束

      CPU真实负载阈值

      节点CPU真实利用率超过该阈值后,会根据真实负载阈值生效方式中的约束调度工作负载。新下发的工作负载将被优先或强制调度到其他节点,节点中已经运行的工作负载不受影响。

      80

      内存真实负载阈值

      节点内存真实利用率超过该阈值后,会根据真实负载阈值生效方式中的约束调度工作负载。新下发的工作负载将被优先或强制调度到其他节点,节点中已经运行的工作负载不受影响。

      80

  1. 在集群中安装prometheus-adapter。

    1. 执行如下命令安装:
      git clone https://github.com/kubernetes-sigs/prometheus-adapter.git
      cd prometheus-adapter/deploy/manifests
      kubectl apply -f .
    2. 修改prometheus-adapter负载连接prometheus-server的配置。
      kubectl edit deployment prometheus-adapter -n monitoring

      修改prometheus-url参数值如下:

      • HTTPS协议修改为HTTP协议。
      • 默认域名修改为Prometheus Service的IP和端口,可通过kubectl get service -n monitoring命令查询。
      ...
            containers:
              - name: prometheus-adapter
                image: registry.k8s.io/prometheus-adapter/prometheus-adapter:v0.12.0
                args:
                  - --cert-dir=/var/run/serving-cert
                  - --config=/etc/adapter/config.yaml
                  - --prometheus-url=http://10.21.72.124:9090/
                  - --secure-port=6443
                  - --tls-cipher-suites=TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA
      ...
    3. 确认在Prometheus可以查询到原生的node_cpu_seconds_total和node_memory_MemAvailable_bytes指标。

  2. 开启自定义指标Metrics API以提供容器资源指标的能力。

    首先执行以下命令查询集群中是否已开启自定义指标Metrics API,如果已开启则可跳过本步骤。

    kubectl get APIServices | grep v1beta1.custom.metrics.k8s.io

    若存在回显,表示已开启自定义指标Metrics API;若不存在回显,则按照以下方式进行APIService对象注册(这里需要注意Service的名称prometheus-adapter和命名空间要与实际安装的环境对应,如下是kube-prometheus默认安装值)。

    1. 创建一个文件,命名为custom-metrics-apiservice.yaml。文件内容如下:
      apiVersion: apiregistration.k8s.io/v1
      kind: APIService
      metadata:
        labels:
          app.kubernetes.io/component: metrics
          app.kubernetes.io/name: prometheus-adapter
          app.kubernetes.io/part-of: prometheus-adapter
        name: v1beta1.custom.metrics.k8s.io
      spec:
        group: custom.metrics.k8s.io
        groupPriorityMinimum: 100
        insecureSkipTLSVerify: true
        service:
          name: prometheus-adapter
          namespace: monitoring
          port: 443
        version: v1beta1
        versionPriority: 100
    2. 然后执行以下命令创建对应APIService对象。
      kubectl create -f custom-metrics-apiservice.yaml
    3. 再次执行以下命令查询集群中是否已开启自定义指标Metrics API。
      kubectl get APIServices | grep v1beta1.custom.metrics.k8s.io

  3. 添加自定义指标采集规则。

    1. 修改adapter-config配置项。
      kubectl edit configmap adapter-config -n monitoring
    2. 在rules字段下添加自定义指标采集规则。
      自定义指标采集规则如下:
      ...
      data:
        config.yaml: >
          rules:
          - seriesQuery: '{__name__=~"node_cpu_seconds_total"}'
            resources:
              overrides:
                instance:
                  resource: node
            name:
              matches: node_cpu_seconds_total
              as: node_cpu_usage_avg
            metricsQuery: avg_over_time((1 - avg (irate(<<.Series>>{mode="idle"}[5m])) by (instance))[10m:30s])
          - seriesQuery: '{__name__=~"node_memory_MemTotal_bytes"}'
            resources:
              overrides:
                instance:
                  resource: node
            name:
              matches: node_memory_MemTotal_bytes
              as: node_memory_usage_avg
            metricsQuery: avg_over_time(((1-node_memory_MemAvailable_bytes/<<.Series>>))[10m:30s])
      ...
    3. 重新部署monitoring命名空间下的prometheus-adapter工作负载。
      kubectl rollout restart deployment prometheus-adapter -n monitoring
    4. 验证自定义规则配置成功。
      1. 执行以下命令,若可以正常返回自定义指标信息,表示Prometheus侧指标采集配置成功。
        kubectl get --raw=/apis/custom.metrics.k8s.io/v1beta1

      2. 执行以下命令,查询集群内节点信息。
        kubectl get nodes 

        然后任选一个节点执行以下命令,其中xxxx替换为查询到的node_name,如果需要查询所有节点资源信息,可以使用*代替xxxx

        kubectl get --raw=/apis/custom.metrics.k8s.io/v1beta1/nodes/xxxx/node_cpu_usage_avg

        查询结果示例如下:

  4. 开启负载感知调度能力。

    1. 登录CCE控制台。
    2. 单击集群名称进入集群,在左侧选择“配置中心”,在右侧选择“调度配置”页签。
    3. 在专家模式配置中单击“开始使用”。

    4. 修改专家模式中的参数配置,配置负载感知调度策略,红色字体为开启负载感知调度能力增加的配置项,详情如下:
      admission_kube_api_qps: 200
      admissions: /jobs/mutate,/jobs/validate,/podgroups/mutate,/pods/validate,/pods/mutate,/queues/mutate,/queues/validate,/eas/pods/mutate,/eas/pods/validate,/npu/jobs/validate,/resource/validate,/resource/mutate,/workloadbalancer/balancer/validate,/workloadbalancer/balancerpolicytemplate/validate
      annotations: {}
      colocation_enable: "true"
      controller_kube_api_qps: 200
      default_scheduler_conf:
        actions: allocate, backfill, preempt
        metrics:
          interval: 30s
          type: prometheus_adaptor
        tiers:
          - plugins:
              - name: priority
              - enableJobStarving: false
                enablePreemptable: false
                name: gang
              - name: conformance
              - name: oversubscription
          - plugins:
              - enablePreemptable: false
                name: drf
              - name: predicates
              - name: nodeorder
              - arguments:
                   cpu.weight: 1
                   memory.weight: 1
                   thresholds:
                      cpu: 80
                      mem: 80
                  usage.weight: 5
                enablePredicate: true
                name: usage
          - plugins:
              - name: cce-gpu-topology-predicate
              - name: cce-gpu-topology-priority
              - name: xgpu
          - plugins:
              - name: nodelocalvolume
              - name: nodeemptydirvolume
              - name: nodeCSIscheduling
              - name: networkresource
      deschedulerPolicy:
        profiles:
          - name: ProfileName
            pluginConfig:
              - args:
                  nodeFit: true
                name: DefaultEvictor
              - args:
                  evictableNamespaces:
                    exclude:
                      - kube-system
                  thresholds:
                    cpu: 20
                    memory: 20
                name: HighNodeUtilization
              - args:
                  evictableNamespaces:
                    exclude:
                      - kube-system
                  metrics:
                    type: prometheus_adaptor
                  nodeFit: true
                  targetThresholds:
                    cpu: 80
                    memory: 85
                  thresholds:
                    cpu: 30
                    memory: 30
                name: LoadAware
            plugins:
              balance:
                enabled: null
      descheduler_enable: "false"
      deschedulingInterval: 10m
      enable_workload_balancer: false
      oversubscription_method: nodeResource
      oversubscription_profile_period: 300
      oversubscription_ratio: 60
      recommendation_enable: ""
      scheduler_kube_api_qps: 200
      update_pod_status_qps: 50
      workload_balancer_score_annotation_key: ""
      workload_balancer_third_party_types: ""

      集群负载感知调度关键参数如下:

      • usage.weight:表示负载感知调度策略权重,增大该权重值,可提高负载感知策略在整体调度中的影响力。
      • cpu.weight:表示CPU权重,增大该权重值,优先均衡CPU资源。
      • memory.weight:表示内存权重,增大该权重值,优先均衡内存资源。
      • thresholds:
        • cpu:表示CPU真实负载阈值。
        • mem:表示内存真实负载阈值。
      • enablePredicate:表示真实负载阈值生效方式。
        • 为true时表示硬约束:节点CPU、内存真实负载达到阈值后,该节点不允许调度新的任务。
        • 为false时表示软约束:节点CPU、内存真实负载达到阈值后,新的任务优先被分配至真实负载未达到阈值的节点,但是该节点依然允许调度。