ljzsdut
GitHubToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

8.1 K8s调度策略之 Scheduler

Scheduler

调度器调度步骤:预选–>优选–>选定(如果优选得分相同,随机选定),如果某个pod没有被成功调度,则会处于Pending状态。

预选策略

  • CheckNodeCondition
  • GeneralPredicates
    • HostName:检查Pod对象是否定义了pod.spec.hostname
    • PodfitsHostPorts:检查Pod的pod.spec.containers.ports.hostPort定义
    • MatchNodeSelector:检查pods.spec.nodeSelector
    • PodfitsResources:检查节点的资源是否能够满足Pod的资源需求
  • NoDiskConflict:检查节点上是否有合适的存储卷可以满足Pod依赖的存储卷。默认未启用
  • PodToleratesNodeTaints:检查Pods上的pod.spec.tolerations定义的可容忍污点是否完全包含节点的上的污点taints
  • PodToleratesNodeNoExecuteTaints:驱离性污点,默认未启用
  • CheckNodeLabelPresence:检查节点标签存在性,默认未启用
  • CheckServiceAffinity:根据当前Pod对象所属Service已有的其他Pod对象所在节点进行调度,尽可能使属于同一Servie的多个Pod使用同一个节点。默认未启用
  • CheckVolumBinding:
  • NoVolumeZoneConflict:
  • CheckNodeMemeoryPressure:
  • CheckNodePIDPressure:
  • CheckNodeDiskPressure:
  • MatchInterPodAffinity:

所有预选策略都要评估,一票否决的方式。预选之后,会选择出有资格被pod运行的所有node。

优选函数

  • LeastRequested:最少请求,资源使用率低的胜出

    (cpu((capacity-sum(requested))*10/capacity)+memory((capacity-sum(requested))*10/capacity))/2

  • MostRequested:最多请求,资源使用率高的胜出。默认未启用

  • BalancedResourceAllocation:CPU和内存资源被占用率相近的胜出;

  • NodePreferAvoidPods:根据节点注解信息“scheduler.alpha.kubernetes.io/preferAvoidPods”是否存在,没有此信息,得分为10,权重10000。

  • TaintToleration:将Pod对象的spec.tolerations列表项与节点的taints列表项进行匹配度检查,匹配条目越多,得分越低;目的:查找污点少的节点

  • SeletorSpreading:目的:将Selector选择的Pod分散开。

  • InterPodAffinity:Pod亲和性项目多的胜出

  • NodeAffinity:Node亲和性项目多的胜出

  • NodeLabel:节点标签:根据节点是否应用特定标签名来评估得分,只看key,不看value。默认未启用

  • ImageLocality:根据满足当前Pod对象需求的已有镜像的体积大小之和,含镜像体积越多,得分越高。默认未启用

**经过优选策略评分后,根据得分选择出得分最高的,最符合条件的node(如果多个node得分相同且最高,则随机选择其中一个),将该node与该pod进行绑定。**之后由kubelet进行pod的部署。

高级调度方式

概述

标签选择器概述

标签选择器有两种:

  • 等值关系:=或==、!=(不具有key或具有key但value不同)

  • 集合关系:KEY in (value1,value2)、KEY notin (value1,value2)、KEY表示存在某个键、!KEY表示不存在某个键

    同时指定多个标签使用逗号分隔,多个标签关系为逻辑与:release=canary,app=tomcat

许多资源支持内嵌字段定义其使用的标签选择器:

  • matchLabels:直接给定key=value
  • matchExpressions:基于给定的表达式来定义使用的标签选择器,格式为:{key:“KEY”,operator:“OPERATOR”,values:[value1,value2…]}
    • OPERATOR为In、NotIn:values字段的值必须为非空列表
    • OPERATOR为Exists、NotExists:values字段的值必须为空列表[]
对node进行打标签
[root@k8smaster ~]# kubectl get nodes --show-labels
NAME        STATUS    ROLES     AGE       VERSION   LABELS
k8smaster   Ready     master    48d       v1.11.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=k8smaster,node-role.kubernetes.io/master=
k8snode01   Ready     <none>    48d       v1.11.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=k8snode01
k8snode02   Ready     <none>    48d       v1.11.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=k8snode02


[root@k8smaster ~]# kubectl label nodes k8snode01 disktype=hd 
node/k8snode01 labeled
[root@k8smaster ~]# kubectl label nodes k8snode02 disktype=ssd
node/k8snode02 labeled
[root@k8smaster ~]# kubectl get nodes --show-labels           
NAME        STATUS    ROLES     AGE       VERSION   LABELS
k8smaster   Ready     master    48d       v1.11.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=k8smaster,node-role.kubernetes.io/master=
k8snode01   Ready     <none>    48d       v1.11.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=hd,kubernetes.io/hostname=k8snode01
k8snode02   Ready     <none>    48d       v1.11.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/hostname=k8snode02

基于nodeSelector的调度:

用于选择满足条件的节点。

[root@k8smaster schedule]# cat pod-demo.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
  labels: 
    app: myapp
    tier: frontend
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
  nodeSelector:
    disktype: harddisk

基于affinity.nodeAffinity的调度:

node只有affinity,没有antiaffinity,用于选择满足条件的节点,类似于nodeSelector,但是比nodeSelector更加灵活。

说明:

当nodeAffinity和nodeSelector同时出现时,要二者同时满足条件才能被调度。

  • 硬亲和(required):
[root@k8smaster schedule]# cat pod-nodeaffinity-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-node-affinity-demo
  labels: 
    app: myapp
    tier: frontend
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
  affinity:
    nodeAffinity:  #没有node反亲和性
      requiredDuringSchedulingIgnoredDuringExecution:   #required表示硬亲和性,表示必须匹配
        nodeSelectorTerms:
        - matchExpressions:
          - key: zone
            operator: In
            values: 
            - foo
            - bar
  • 软亲和(preferred)
[root@k8smaster schedule]# cat pod-nodeaffinity-demo-2.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-node-affinity-demo-2
  labels: 
    app: myapp
    tier: frontend
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:  #preferred倾向,表示软亲和性,尽量满足,可不满足匹配,优先运行在满足条件的节点上。
      - weight: 60  #多个策略之间的比重。最优选的节点是权重总和最大的节点
        preference:
          matchExpressions:
          - key: zone
            operator: In
            values: 
            - foo
            - bar

基于affinity.podAffinity的调度:

Pod亲和性,第一个Pod随机调度,后面的Pod都以此为标准,运行在相近节点上,不需要考虑节点标签配置。但仍需要有一个逻辑条件预先设置好哪些节点是属于相近节点,同一位置,让pod去判断。

硬亲和:

[root@k8smaster schedule]# cat pod-required-affinity-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-first
  labels: 
    app: myapp
    tier: frontend
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-second
  labels: 
    app: backend
    tier: db
spec:
  containers:
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command: ["sh","-c","sleep 3600"]
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:  #硬亲和
      - labelSelector:  #与哪个pod进行亲和
          matchExpressions:
          - {key: app, operator: In, values: ["myapp"]}
        topologyKey: kubernetes.io/hostname  #拓扑键,表示节点标签kubernetes.io/hostname相同的为相近节点。本例中的含义是:具有app=myapp标签的pod要调度到同一台主机上(topologyKey: kubernetes.io/hostname)

软亲和:

[root@k8smaster schedule]# cat pod-required-affinity-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-first
  labels: 
    app: myapp
    tier: frontend
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-second
  labels: 
    app: backend
    tier: db
spec:
  containers:
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command: ["sh","-c","sleep 3600"]
  affinity:
    podAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:  #软亲和
      - weight: 60
        podAffinityTerm:
          labelSelector:  #与哪个pod进行亲和
            matchExpressions:
            - {key: app, operator: In, values: ["myapp"]}
          topologyKey: kubernetes.io/hostname  #拓扑键,表示节点标签kubernetes.io/hostname相同的为相近节点。

基于affinity.podAntiAffinity的调度:

调度器更倾向于将 pod 调度到满足该字段指定的反亲和性表达式的节点,但它可能会选择违反一个或多个表达式的节点。最优选的节点是权重总和最大的节点,即对于满足所有调度要求(比如资源请求、requiredDuringScheduling等)的每个节点,通过遍历podAffinityTerm元素来计算权重总和,如果节点满足podAffinityTerm,则在权重总和中添加该podAffinityTerm的“权重”; 具有最高和的节点是最优选的。

定义与podAffinity定义一致,只是改为podAntiAffinity即可。

硬亲和:

[root@k8smaster schedule]# cat pod-required-anti-affinity-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-first
  labels: 
    app: myapp
    tier: frontend
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-second
  labels: 
    app: backend
    tier: db
spec:
  containers:
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command: ["sh","-c","sleep 3600"]
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:  #硬亲和
      - labelSelector:
          matchExpressions:
          - {key: app, operator: In, values: ["myapp"]}
        topologyKey: zone
      preferredDuringSchedulingIgnoredDuringExecution:  #软亲和,多个podAffinityTerm进行评估,权重之和最大的节点为最优
      - weight: 60  #podAffinityTerm的权重,取值1-100,只在preferredDuringSchedulingIgnoredDuringExecution中存在
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - {key: zone, operator: In, values: ["zone1"]}
          topologyKey: kubernetes.io/hostname

基于污点(容忍)调度:

污点taint:定义在node上的键值属性数据(键值属性数据:标签、注解、污点),实现让node拒绝pod。定义在node.spec.taints中

容忍度:定义在pod上的键值属性数据,是个列表,表示可以容忍的污点的列表

node.spec.taints.effect 定义node对pod的排斥效果:

  • NoSchedule:仅影响调度过程,对现存pod不产生影响,不能容忍就不能调度过来。

    除非Pod配置了容忍,否则不允许Pod被调度到该节点上。需要注意的是:kubernetes允许通过Kubelet命令绕过调度器(静态Pod),直接在这些节点上启动Pod,即使这些Pod没有配置容忍。Kubernetes允许已经运行的Pod继续运行,即使这些Pod不满足容忍条件。 (仅影响调度过程,后加的污点对现存的Pod不产生影响。)

  • NoExecute:既影响调度过程,也影响现存的Pod对象。不容忍污点的Pod对象将被驱逐。

    对已经运行的Pod,如果node后期又添加了一些污点,当这些Pod不容忍节点上的污点时,强制驱逐到其他满足容忍条件的节点上。如果配置了NoExecute,那么可以选择是否配置tolerationSeconds参数。也就是说Pod会在tolerationSeconds时间过后进行被驱逐。 (不仅不会调度,还会驱逐Node上已有的Pod)

  • PreferNoSchedule: 仅影响调度过程,不能容忍就不能调度过来,但是实在没办法也是能调度过来的。对节点新加了污点,那么对节点上现存的pod没有影响。

    这个配置相比NoSchedule来说,在功能作用上是一样的,只不过不是强制执行的,只是参考执行。 (尽量不去满足不合要求的 pod 调度到 node 上来)

pod上定义容忍度的两种方式:

  • 等值比较(Equal):表示key和value都要一致。默认值
  • 存在性判断(Exists):key和effect必须匹配,value可以为空。

匹配逻辑:

pod必须容忍Node上的所有污点,如果有不被容忍的污点,要看该污点的effect。如果effect为PreferNoSchedule,也可以被调度,如果为NoSchedule或NoExecute,则不能被调度。总之, k8s 在处理 taints and tolerations 时类似一个过滤器:

  • 对比一个 node 上所有的 taints
  • 忽略掉和 pod 中 toleration 匹配的 taints
  • 遗留下来未被忽略掉的所有 taints 将对 pod 产生 effect

给Node打上污点:

Usage: kubectl taint node Node_NAME KEY_1=VAL_1:EFFECT_1 … KEY_N=VAL_N:EFFECT_N

将Node上的污点删除:在key或key:effect后加一个减号

Usage: kubectl taint node Node_NAME KEY-|key:effect-

[root@k8smaster ~]# kubectl taint node k8snode01 node-type=production:NoSchedule
node/k8snode01 tainted
#表示只有容忍node-type=production:NoSchedule这个属性的pod才能调度到k8snode01上
[root@k8smaster ~]# kubectl describe node k8snode01
Name:               k8snode01
Roles:              <none>
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    disktype=hd
                    kubernetes.io/hostname=k8snode01
Annotations:        flannel.alpha.coreos.com/backend-data={"VtepMAC":"1e:98:ec:57:26:42"}
                    flannel.alpha.coreos.com/backend-type=vxlan
                    flannel.alpha.coreos.com/kube-subnet-manager=true
                    flannel.alpha.coreos.com/public-ip=192.168.5.37
                    kubeadm.alpha.kubernetes.io/cri-socket=/var/run/dockershim.sock
                    node.alpha.kubernetes.io/ttl=0
                    volumes.kubernetes.io/controller-managed-attach-detach=true
CreationTimestamp:  Fri, 27 Jul 2018 17:36:30 +0800
Taints:             node-type=production:NoSchedule
.....

定义Pod的容忍度tolerations:

[root@k8smaster schedule]# cat deploy-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deploy
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
      release: canary
  template:
    metadata:
      labels:
        app: myapp
        release: canary
    spec:
      containers:
      - name: myapp
        image: ikubernetes/myapp:v2
        ports:
        - name: http
          containerPort: 80
      tolerations: 
      - key: "node-type"
        operator: "Exists"   #如果操作符为Exists,那么value属性可省略,表示通配任何value;如果为Equal,表示key和value都要一致;如果不指定operator,则默认为Equal;
        value: "" #operator为Exists时,value为"",表示通配所有值。
        effect: ""  #如果为空,表示匹配所有的effect,即NoSchedule+PreferNoSchedule+NoExecute
        #tolerationSeconds: 60   #只有effect为NoExecute才能使用,表示pod运行多久后后被驱逐