k8s调度器Scheduler

k8s调度器Scheduler

Kubernetes Scheduler(简称k8s Scheduler)是Kubernetes集群中的一个核心组件,它负责将Pod调度到合适的Node上运行,以实现集群资源的优化分配和负载均衡。以下是对k8s Scheduler的详细介绍:

一、基本概念

  • Pod:Kubernetes中的最小可调度单元,包含一个或多个容器,以及这些容器所需的存储、网络等资源。
  • Node:运行Pod的物理或虚拟机器,可以是虚拟机、物理机等。
  • kube-scheduler:Kubernetes中的调度器进程,运行在Master节点上,负责将Pod调度到合适的Node上。

二、主要功能

  • 资源分配:根据Pod的资源请求(如CPU、内存)和Node的当前可用资源情况,选择合适的Node来运行Pod。
  • 满足约束条件:考虑Pod的调度约束条件和亲和性/反亲和性规则,如节点标签、区域、拓扑结构等要求,确保Pod被调度到符合其特定需求的Node上。
  • 服务稳定性与优化:通过预选(Filtering)和优选(Scoring)两个阶段的策略执行,确保Pod能够成功调度,并尽可能地优化集群的整体性能和可靠性。

三、调度流程

k8s Scheduler的调度流程主要包括以下几个关键步骤:

  • 监听事件:Scheduler会持续监听API Server的事件,一旦有新的待调度Pod或已有Pod需要重新调度时,它将介入并进行调度决策。
  • 筛选节点:对于每一个Node,检查该节点是否满足Pod的资源需求和其他硬性约束条件,如节点标签匹配、容忍度(Taints and Tolerations)、存储容量等。如果一个Node通过了所有预选策略,则会被加入候选节点集合。
  • 打分排序:Scheduler对候选节点集合应用一系列优选策略,为每个节点计算一个优先级分数。这些优选策略可以包括默认的系统策略(如基于资源利用率的打分)以及用户自定义的策略。Scheduler根据各个节点得到的积分排序,得分最高的节点将被选为Pod的最佳调度位置。
  • 绑定Pod:当确定了最优节点后,Scheduler会在API Server中为Pod创建一个binding对象,将Pod与选定的Node绑定在一起。调度结果写入etcd,并通知kubelet开始在该Node上启动Pod。

四、调度算法与策略

k8s Scheduler的调度算法和策略可以是多样化的,包括但不限于:

  • 优先级调度:根据Pod的优先级和Node的资源情况,优先调度高优先级的Pod。

  • 公平调度:尽量平衡各个Node的资源使用情况,避免资源过度集中或浪费。

  • 权重调度:根据预设的权重因子,综合考虑多种因素进行调度决策。

  • 节点亲和性:根据节点的标签或特性,将Pod调度到具有相似特性的节点上。
    在Kubernetes(k8s)中,节点亲和性(Node Affinity)是一种控制Pod调度到特定节点的高级策略。通过定义节点亲和性规则,管理员可以确保Pod被调度到符合特定条件的节点上,从而实现资源优化、性能提升、故障隔离以及满足特定的合规或业务需求。

    • 一、节点亲和性的基本概念

      节点亲和性允许Pod根据节点的标签(Labels)和选择器(Selectors)来指定其应该被调度到的节点。这有助于将Pod与具有特定特征(如硬件类 型、地理位置、操作系统等)的节点关联起来。

    • 二、节点亲和性的类型

      节点亲和性可以分为两种类型:

      • 硬亲和性(requiredDuringSchedulingIgnoredDuringExecution):
        Pod必须被调度到满足特定条件的节点上。
        如果没有满足条件的节点,Pod将不会被调度,并且将保持Pending状态直到有符合条件的节点出现。
        Pod一旦被调度到某个节点上,即使节点的标签或条件发生变化,Pod也不会被重新调度。
      • 软亲和性(preferredDuringSchedulingIgnoredDuringExecution):
        Pod倾向于被调度到满足特定条件的节点上,但这不是强制性的。
        如果没有满足条件的节点,Pod仍然可以被调度到其他节点上。
        同样,Pod一旦被调度到某个节点上,即使节点的标签或条件发生变化,Pod也不会被重新调度。
    • 三、节点亲和性的配置

    节点亲和性的配置通常通过YAML文件实现,在Pod的spec字段下添加affinity字段,并设置nodeAffinity子字段。以下是一个简单的配置示例:

    apiVersion: v1  
    kind: Pod  
    metadata:  
      name: my-pod  
    spec:  
      affinity:  
        nodeAffinity:  
          requiredDuringSchedulingIgnoredDuringExecution:  
            nodeSelectorTerms:  
            - matchExpressions:  
              - key: disktype  
                operator: In  
                values:  
                - ssd  
      containers:  
      - name: my-container  
        image: my-image
    

    在上述示例中,我们定义了一个Pod,它要求被调度到具有disktype=ssd标签的节点上。这是通过硬亲和性规则实现的,因此如果没有满足条件的 节点,Pod将不会被调度。

    • 四、节点亲和性的应用场景

      • 节点亲和性在多种场景下都非常有用,例如:

        • 硬件要求:某些Pod可能对硬件有特殊要求(如需要SSD存储、特定的CPU类型等),这时可以使用节点亲和性将Pod调度到满足这些要求的节点上。
        • 地理位置:在分布式系统中,可能希望将某些Pod调度到特定的地理位置(如靠近用户的数据中心),以提高访问速度和降低延迟。
          资源优化:通过节点亲和性,可以将具有相似资源需求的Pod调度到同一节点上,以优化资源使用并减少资源浪费。
    • 五、总结

    Kubernetes的节点亲和性提供了一种灵活且强大的方式来控制Pod的调度行为。通过合理配置节点亲和性规则,管理员可以确保Pod被调度到符合特 定条件的节点上,从而实现资源优化、性能提升、故障隔离以及满足特定的合规或业务需求。

  • Pod亲和性/反亲和性:根据Pod之间的依赖关系或排斥关系,将相关的Pod调度到相同或不同的节点上。
    在Kubernetes(简称K8S)中,Pod亲和性(Pod Affinity)是集群调度策略的一个重要组成部分,它允许用户指定某种规则,使得Pod更倾向 于被调度到满足特定条件的节点上运行,或者与已经在特定节点上运行的其他Pod部署在一起。以下是对K8S Pod亲和性的详细解释:

    • 一、Pod亲和性的基本概念

    Pod亲和性主要用于处理Pod与Pod之间的关系,它允许Pod表达出希望与其相同或不同标签集的Pod共存于同一节点或拓扑域(如机架、可用区等) 的愿望。这种机制有助于优化资源使用、提高服务可靠性,并满足特定的应用程序需求。
    - 二、Pod亲和性的类型

      - Pod亲和性可以分为两种类型:
        - 硬策略(requiredDuringSchedulingIgnoredDuringExecution):
              必须满足条件,比较强硬。
              如果没有满足条件的节点,调度器会不断重试,直到找到满足条件的节点为止。
              一旦Pod被调度到某个节点,即使后续节点上的Pod标签发生变化,也不会影响已调度Pod的位置。
        - 软策略(preferredDuringSchedulingIgnoredDuringExecution):
              尽量满足条件,但不强制要求。
              如果没有满足条件的节点,Pod仍然可以被调度到其他节点上。
              同样,一旦Pod被调度到某个节点,后续节点上的Pod标签变化也不会影响已调度Pod的位置。
    
    • 三、Pod亲和性的配置

    Pod亲和性的配置通常通过YAML文件实现,在Pod的spec字段下添加affinity字段,并设置podAffinity子字段。以下是一个简单的配置示例:

    apiVersion: apps/v1  
    kind: Deployment  
    metadata:  
      name: my-deployment  
    spec:  
      replicas: 2  
      selector:  
        matchLabels:  
          app: my-app  
      template:  
        metadata:  
          labels:  
            app: my-app  
        spec:  
          containers:  
          - name: my-container  
            image: my-image  
          affinity:  
            podAffinity:  
              requiredDuringSchedulingIgnoredDuringExecution:  
              - labelSelector:  
                  matchExpressions:  
                  - key: app  
                    operator: In  
                    values:  
                    - my-app  
                topologyKey: kubernetes.io/hostname
    

    在上述示例中,我们定义了一个Deployment,它包含两个副本的Pod。这些Pod通过Pod亲和性的硬策略被要求调度到与标签app=my-app相同的Pod 所在的节点上。topologyKey设置为kubernetes.io/hostname,表示Pod将被调度到与已存在Pod相同的物理节点上。

    • 四、Pod亲和性的应用场景

    Pod亲和性在多种场景下都非常有用,例如:
    1. 数据本地性:对于需要频繁访问本地存储的Pod,可以将其与存储服务所在的Pod部署在同一节点上,以减少网络延迟和提高性能。
    2. 服务依赖:当Pod之间存在服务依赖关系时,可以使用Pod亲和性将它们部署在同一节点上,以确保服务的可用性和稳定性。
    3. 资源优化:通过Pod亲和性,可以将具有相似资源需求的Pod部署在同一节点上,以优化资源使用并减少资源浪费。

    • 五、总结
      K8S Pod亲和性是一种强大的调度策略,它允许用户根据Pod之间的依赖关系或资源需求来优化Pod的部署位置。通过合理配置Pod亲和性,可以提高 集群的资源利用率、服务可靠性和应用程序性能。

预选算法

在Kubernetes(k8s)中,Scheduler的预选算法是Pod调度过程中的一个重要阶段,负责从集群中所有可用的节点中筛选出能够满足Pod资源需求和其他特定条件的候选节点。这一过程主要基于一系列的预选策略(Predicates)进行。以下是关于k8s Scheduler预选算法的详细解析:
预选算法的工作流程

  • 获取节点列表:

    • Scheduler首先会从kube-apiserver获取集群中所有可用的节点列表。
  • 遍历节点并筛选:

    • 接着,Scheduler会遍历每一个节点,并根据预定义的预选策略(Predicates)对节点进行筛选。
    • 每个预选策略都会检查节点是否满足特定的条件,例如资源是否充足、是否匹配Pod的标签选择器、是否有端口冲突等。
      确定候选节点:
    • 只有通过了所有预选策略的节点才会被视为候选节点,进入后续的优选阶段。
  • 默认的预选策略

  • Kubernetes默认加载了多个预选策略,包括但不限于以下几个:

    • PodFitsPorts:判断Pod所使用的端口在备选节点中是否被占用。
    • PodFitsResources:判断备选节点的资源(如CPU、内存)是否满足Pod的需求。
    • PodSelectorMatches(MatchNodeSelector):判断备选节点是否包含Pod的标签选择器指定的标签。
    • PodFitHost(HostName):判断Pod的spec.nodeName所指定的节点名称和备选节点的名称是否一致。
    • NoDiskConflict:判断Pod的存储卷(如GCEPersistentDisk或AwsElasticBlockStore)和备选节点中已存在的Pod是否存在冲突。
  • 预选算法的优化

    为了提高预选过程的效率,Kubernetes采用了多种优化措施,如:

    • 并行筛选:默认会启动多个goroutine来并行地进行节点的筛选,以提高筛选速度。
    • 局部最优解:通过缩小筛选节点的数量范围,来避免在大型集群中遍历所有节点导致的性能问题。
    • 平均分布:通过分配索引的方式,确保集群中的节点能够均匀地被分配Pod,以维持集群的负载均衡。
  • 自定义预选策略

    • 除了默认的预选策略外,用户还可以根据实际需求自定义预选策略,并将其注册到Scheduler中。自定义预选策略需要实现Kubernetes提供的接口,并在Scheduler的配置文件中进行指定。
  • 总结
    k8s Scheduler的预选算法是Pod调度过程中的关键一环,通过一系列的预选策略对集群中的节点进行筛选,从而确定能够满足Pod需求的候选节点。这一过程不仅确保了Pod能够被调度到合适的节点上运行,还通过多种优化措施提高了调度的效率和性能。

优选算法

k8s Scheduler的优选算法是在预选算法之后进行的,旨在从通过预选的候选节点中选择出最优的节点来运行Pod。这一过程主要基于一系列的优选函数(Priorities)进行,每个函数都会为候选节点打分,最终选择得分最高的节点作为Pod的运行节点。

  • 优选算法的工作流程

    • 获取候选节点:
      • 优选算法首先会接收到预选算法筛选出的候选节点列表。
    • 并发打分:
      • 对于每个候选节点,优选算法会并发地根据配置的优选函数进行打分。
      • 每个优选函数都会根据特定的评估标准(如资源利用率、节点标签、亲和性等)为节点计算一个分数。
    • 加权求和:
      • 在所有优选函数打分完成后,Scheduler会根据每个优选函数的权重(如果设置了的话)对节点的各个分数进行加权求和,得到节点的最终得分。
    • 选择最优节点:
      • 最终,Scheduler会选择得分最高的节点作为Pod的运行节点,并将其绑定到该节点上。

常用的优选函数

  • Kubernetes提供了多种内置的优选函数,包括但不限于以下几个:

    • LeastRequestedPriority:
      • 计算Pods需要的CPU和内存在当前节点可用资源的百分比,具有最小百分比的节点就是最优的。
      • 得分计算公式:(cpu(capacity-sum(requested))*10/capacity)+(memory(capacity-sum(requested))*10/capacity)/2
    • BalanceResourceAllocation:
      • 选出资源使用率最均匀的节点,即CPU和内存占用率相近的节点。
    • NodePreferAvoidPodsPriority:
      • 如果节点上有注解信息"scheduler.alpha.kubernetes.io/preferAvoidPods",则该节点对该Pod的得分会很低(甚至为0),反之则得分很高。
    • TaintToleration:
      • 将Pod对象的spec.tolerations与节点的taints列表项进行匹配度检查,匹配的条目越多得分越低。
    • ImageLocalityPriority:
      • 根据Node上是否存在Pod的容器运行所需镜像以及镜像的大小对优先级打分。如果Node上存在Pod所需全部镜像,则得分为10分;如果Node上存在Pod容器部分所需镜像,则根据这些镜像的大小来决定分值;如果Node上一个镜像都不存在,则分值为0。
    • InterPodAffinityPriority:
      • 基于Pod亲和性(affinity)和反亲和性(anti-affinity)计算分数。遍历Pod对象亲和性的条目,并将那些能够匹配到节点的权重相加,值越大的得分越高。
    • NodeAffinityPriority:
      • 根据Pod对象中的nodeSelector,对节点进行匹配度检查,能够成功匹配的数量越多,得分就越高。
  • 自定义优选函数

    • 除了内置的优选函数外,用户还可以根据实际需求自定义优选函数,并将其注册到Scheduler中。自定义优选函数需要实现Kubernetes提供的接口,并在Scheduler的配置文件中进行指定。

综上所述,k8s Scheduler的优选算法通过一系列的优选函数为候选节点打分,并选择得分最高的节点作为Pod的运行节点,从而实现了Pod的优化调度。

五、自定义调度器

Kubernetes支持通过自定义调度器来满足特定的调度需求。开发者可以根据业务需求编写自定义调度器,并将其集成到Kubernetes集群中。Kubernetes提供了调度器框架(Scheduler Framework),简化了自定义调度器的开发。

示例:
创建一个pod

vim 01.deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
 labels:
   app: myapp
 name: myapp
spec:
 replicas: 1
 selector:
   matchLabels:
     app: myapp
 template:
   metadata:
     labels:
       app: myapp
   spec:
     schedulerName: my-scheduler
     containers:
     - image: wangyanglinux/myapp:v1.0
       name: myapp
#一直pending状态 因为没有名为my-scheduler的调度器
[root@master Scheduler]# kubectl get pod
NAME                     READY   STATUS    RESTARTS   AGE
myapp-75cdc5c7df-7dj56   0/1     Pending   0          4m33s

解决方案

# 在 kubernetes Master 节点开启 apiServer 的代理
[root@master Scheduler]# kubectl proxy --port=8001
Starting to serve on 127.0.0.1:8001
#将当前的apiservice的服务端点暴露出来,暴露出来的端点http直接访问

基于 shell 编写一个自定义调度器

vi my-scheduler.sh
#!/bin/bash
#制定当前服务器地址
SERVER='localhost:8001'

while true;
do
    for PODNAME in $(kubectl --server $SERVER get pods -o json | jq '.items[] | select(.spec.schedulerName =="my-scheduler") | select(.spec.nodeName == null) | .metadata.name' | tr -d '"')
    do
        NODES=($(kubectl --server $SERVER get nodes -o json | jq '.items[].metadata.name' | tr -d '"'))
        NUMNODES=${#NODES[@]}
        CHOSEN=${NODES[$[ $RANDOM % $NUMNODES]]}
        curl --header "Content-Type:application/json" --request POST --data '{"apiVersion":"v1","kind":"Binding","metadata": {"name":"'$PODNAME'"},"target": {"apiVersion":"v1","kind": "Node", "name": "'$CHOSEN'"}}' http://$SERVER/api/v1/namespaces/default/pods/$PODNAME/binding/
        echo "Assigned $PODNAME to $CHOSEN"
    done
    sleep 1
done

[root@master Scheduler]# yum install -y epel-release
[root@master Scheduler]# yum install -y jq
[root@master Scheduler]# chmod a+x my-scheduler.sh 
[root@master Scheduler]# kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
myapp-75cdc5c7df-8ldv9   0/1     Pending   0          18s
[root@master Scheduler]# ./my-scheduler.sh 
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Success",
  "code": 201
}Assigned myapp-75cdc5c7df-gbt6t to master
[root@master calico]# kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
myapp-75cdc5c7df-gbt6t   1/1     Running   0          44s

六、污点和容忍

在Kubernetes(k8s)中,污点(Taints)和容忍(Tolerations)是两种相互配合的机制,用于控制Pod的调度行为,确保Pod不会被调度到不合适的节点上。以下是对污点和容忍的详细解释:

一、污点(Taints)

  1. 定义与作用

污点是定义在节点(Node)上的键值对属性,用于让节点拒绝将Pod调度运行于其上,除非Pod有接纳节点污点的容忍度。污点的主要作用是标识节点具有某些不希望的属性,从而阻止Pod被调度到该节点上。

  1. 组成

每个污点由一个key、一个可选的value以及一个effect组成,格式为key=value:effect。其中,value可以为空,而effect描述了污点对Pod的作用效果。

  1. Effect的类型

    NoSchedule:表示Kubernetes不会将Pod调度到具有该污点的节点上,但已经调度到该节点的Pod不受影响。
    PreferNoSchedule:软策略版本的NoSchedule,表示Kubernetes将尽量避免将Pod调度到具有该污点的节点上,但不是强制的。
    NoExecute:表示Kubernetes不仅不会将Pod调度到具有该污点的节点上,还会将已经存在于该节点的Pod驱逐出去。
    PreferNoSchedule: 表示k8s将尽量避免将pod调度到具有该污点的Node上

  2. 设置与去除

污点可以通过kubectl taint命令进行设置和去除。例如,设置污点使用kubectl taint nodes =:命令,去除污点则使用kubectl taint nodes :-命令。

#设置污点
kubectl taint nodes nodeName key=value1:NoSchedule
# 查找污点
kubectl describe node nodeName
Taints:             node-role.kubernetes.io/control-plane:NoSchedule
# 删除污点
kubectl taint nodes nodeName key=value1:NoSchedule-

二、容忍(Tolerations)

  1. 定义与作用

容忍是定义在Pod上的键值对属性,用于配置Pod可以容忍哪些污点。只有当Pod具有对节点污点的容忍度时,它才有可能被调度到该节点上。

  1. 组成

每个容忍由一个key、一个可选的operator、一个可选的value以及一个effect组成。其中,operator用于指定与污点value的比较方式,默认为Equal;value用于与污点的value进行匹配;effect则指定了容忍的污点效果。

  1. 匹配规则

    如果operator是Exists,则无需指定value,表示Pod可以容忍所有具有该key的污点。
    如果operator是Equal,则Pod的容忍必须与污点的key和value都匹配。
    如果不指定operator,则默认为Equal。

  2. 示例

apiVersion: v1  
kind: Pod  
metadata:  
  name: tolerant-pod  
spec:  
  containers:  
  - name: nginx-container  
    image: nginx:latest  
  tolerations:  
  - key: "special"  
    operator: "Equal"  
    value: "gpu"  
    effect: "NoSchedule"
    tolerationSeconds: 3600 # 3600秒后驱除pod

当不指定value时,表示容忍所有污点value

- key: "key2"
  operator: "Exists"
  effect: "NoSchedule"

当不指定key值时,表示容忍所有的污点key

tolerations: 
- operator: "Exists"

当不指定effect值时,表示容忍所有污点的作用

tolerations: 
- key: "key"
  operator: "Exists"

多个master存在,防止资源浪费,使用一下命令可将pod部署至master

kubectl taint nodes nodeName node-role.kubenetes.io/master=:NoSchedule-
kubectl taint nodes nodeName node-role.kubenetes.io/master=:PreferNoSchedule

在上面的示例中,Pod tolerant-pod 定义了一个容忍度,表示它可以被调度到具有special=gpu:NoSchedule污点的节点上。

三、指定节点调度

设置pod.spec.nodeName 指定pod强制调度到master上

apiVersion: apps/v1
kind: Deployment
metadata:
 name: nodename-test
spec:
 replicas: 7
 selector:
   matchLabels:
     app: nodename
 template:
   metadata:
     labels:
       app: nodename
   spec:
     nodeName: master
     containers:
     - name: myweb
       image: wangyanglinux/myapp:v1.0
       ports:
       - containerPort: 80

设置pod.spec.nodeSelector 通过label-selecotr机制选择节点,由Scheduler策略匹配label,而后调度pod到目标节点,该规则属于强制约束 master如果有noSchedule污点选项 则master有label type=nodeselect也不能在master上创建

apiVersion: apps/v1
kind: Deployment
metadata:
 name: nodeselect-test
spec:
 replicas: 2
 selector: # 选择器
   matchLabels:
     app: nodeselect
 template:
   metadata:
     labels:
       app: nodeselect
   spec:
     nodeSelector:
       type: nodeselect # key:value 节点需要有此标签
     containers:
     - name: myweb
       image: wangyanglinux/myapp:v1.0
       ports:
       - containerPort: 80

四、总结

污点和容忍是Kubernetes中用于控制Pod调度行为的两种重要机制。通过给节点设置污点,可以阻止不希望的Pod被调度到该节点上;通过在Pod上定义容忍度,可以使得Pod能够容忍节点的污点,从而被调度到该节点上。这两种机制相互配合,实现了对Pod调度行为的灵活控制。

七、高可用性与扩展性

  • 高可用性:k8s Scheduler支持多实例部署,通过负载均衡确保调度服务的连续性。
  • 扩展性:k8s Scheduler提供了丰富的扩展点(Extension Points),允许用户开发自己的插件来扩展调度器的功能。这些扩展点包括预选、优选、绑定等多个阶段,用户可以在这些阶段中插入自定义的逻辑来满足特定的调度需求。

综上所述,k8s Scheduler是Kubernetes集群中至关重要的组件之一,它通过精细的调度策略和高可用性配置,确保了集群资源的合理分配和高效利用。

你可能感兴趣的:(kubernetes,容器,云原生)