十五,K8S集群调度原理及调度策略

k8s调度器Scheduler

Scheduler工作原理

请求及Scheduler调度步骤:

  • 节点预选(Predicate):排除完全不满足条件的节点,如内存大小,端口等条件不满足。
  • 节点优先级排序(Priority):根据优先级选出最佳节点
  • 节点择优(Select):根据优先级选定节点
  1. 首先用户通过 Kubernetes 客户端 Kubectl 提交创建 Pod 的 Yaml 的文件,向Kubernetes 系统发起资源请求,该资源请求被提交到

  2. Kubernetes 系统中,用户通过命令行工具 Kubectl 向 Kubernetes 集群即 APIServer 用 的方式发送“POST”请求,即创建 Pod 的请求。

  3. APIServer 接收到请求后把创建 Pod 的信息存储到 Etcd 中,从集群运行那一刻起,资源调度系统 Scheduler 就会定时去监控 APIServer

  4. 通过 APIServer 得到创建 Pod 的信息,Scheduler 采用 watch 机制,一旦 Etcd 存储 Pod 信息成功便会立即通知APIServer,

  5. APIServer会立即把Pod创建的消息通知Scheduler,Scheduler发现 Pod 的属性中 Dest Node 为空时(Dest Node=””)便会立即触发调度流程进行调度。

  6. 而这一个创建Pod对象,在调度的过程当中有3个阶段:节点预选、节点优选、节点选定,从而筛选出最佳的节点
    • 节点预选:基于一系列的预选规则对每个节点进行检查,将那些不符合条件的节点过滤,从而完成节点的预选
    • 节点优选:对预选出的节点进行优先级排序,以便选出最合适运行Pod对象的节点
    • 节点选定:从优先级排序结果中挑选出优先级最高的节点运行Pod,当这类节点多于1个时,则进行随机选择

k8s的调用工作方式

Kubernetes调度器作为集群的大脑,在如何提高集群的资源利用率、保证集群中服务的稳定运行中也会变得越来越重要Kubernetes的资源分为两种属性。

  1. 可压缩资源(例如CPU循环,Disk I/O带宽)都是可以被限制和被回收的,对于一个Pod来说可以降低这些资源的使用量而不去杀掉Pod。
  2. 不可压缩资源(例如内存、硬盘空间)一般来说不杀掉Pod就没法回收。未来Kubernetes会加入更多资源,如网络带宽,存储IOPS的支持。

常用预选策略

预选策略 作用
CheckNodeCondition 检查是否可以在节点报告磁盘、网络不可用或未准备好时将Pod调度其上
HostName 如果Pod对象拥有spec.hostname属性,则检查节点名称字符串是否和该属性值匹配。
PodFitsHostPorts Pod的spec.hostPort属性时,检查端口是否被占用
MatchNodeSelector Pod的spec.nodeSelector属性时,检查节点标签
NoDiskConflict Pod依赖的存储卷在此节点是否可用,默认没有启用
PodFitsResources 检查节点上的资源(CPU、内存)可用性是否满足Pod对象的运行需求。
PodToleratesNodeTaints Pod的spec.tolerations属性,仅关注NoSchedule和NoExecute两个效用标识的污点
PodToleratesNodeNoExecuteTaints Pod的spec.tolerations属性,是否能接纳节点的NoExecute类型污点,默认没有启用
CheckNodeLabelPresence 仅检查节点上指定的所有标签的存在性,默认没有启用
CheckServiceAffinity 将相同Service的Pod对象放置在同一个或同一类节点上以提高效率,默认没有启用
MaxEBSVolumeCount 检查节点已挂载的EBS(亚马逊弹性块存储)存储卷数量是否超过设置的最大值,默认为39
MaxGCEPDVolumeCount 检查节点上已挂载的GCE PD(谷歌云存储) 存储卷数量是否超过最大值,默认为16
MaxAzureDiskVolumeCount 检查节点上已挂载的Azure Disk存储卷数量是否超过最大值,默认为16
CheckVolumeBinding 检查节点上已绑定和未绑定的PVC是否满足需求
NoVolumeZoneConflict 在给定区域zone限制下,检查此节点部署的Pod对象是否存在存储卷冲突
CheckNodeMemoryPressure 检查节点内存压力,如果压力过大,那就不会讲pod调度至此
CheckPodePIDPressure 检查节点PID资源压力
CheckNodeDiskPressure 检查节点磁盘资源压力
MatchInterPodAffinity 检查节点是否满足Pod对象亲和性或反亲和性条件

常用优先函数

函数名称 详细说明
LeastRequestedPriority 节点的优先级就由节点空闲资源与节点总容量的比值,即由(总容量-节点上Pod的容量总和-新Pod的容量)/总容量)来决定。 CPU和内存具有相同权重,资源空闲比越高的节点得分越高。 cpu((capacity – sum(requested)) * 10 / capacity) + memory((capacity – sum(requested)) * 10 / capacity) / 2
BalancedResourceAllocation CPU和内存使用率越接近的节点权重越高,该策略不能单独使用,必须和LeastRequestedPriority组合使用,尽量选择在部署Pod后各项资源更均衡的机器。 如果请求的资源(CPU或者内存)需求大于节点的capacity,那么该节点永远不会被调度到。
InterPodAffinityPriority 通过迭代 weightedPodAffinityTerm 的元素计算和,并且如果对该节点满足相应的PodAffinityTerm,则将 “weight” 加到和中,具有最高和的节点是最优选的。
SelectorSpreadPriority 为了更好的容灾,对同属于一个service、replication controller或者replica的多个Pod副本,尽量调度到多个不同的节点上。 如果指定了区域,调度器则会尽量把Pod分散在不同区域的不同节点上。当一个Pod的被调度时,会先查找Pod对于的service或者replication controller, 然后查找service或replication controller中已存在的Pod,运行Pod越少的节点的得分越高。本质就是往运行同类pod少的节点上分配。
NodeAffinityPriority 亲和性机制。Node Selectors(调度时将pod限定在指定节点上), 支持多种操作符(In, NotIn, Exists, DoesNotExist, Gt, Lt),而不限于对节点labels的精确匹配。 另外支持两种类型的选择器,一种是“hard(requiredDuringSchedulingIgnoredDuringExecution)”选择器, 它保证所选的主机必须满足所有Pod对主机的规则要求。 这种选择器更像是之前的nodeselector,在nodeselector的基础上增加了更合适的表现语法。 另一种是“soft(preferresDuringSchedulingIgnoredDuringExecution)”选择器, 它作为对调度器的提示,调度器会尽量但不保证满足NodeSelector的所有要求。
NodePreferAvoidPodsPriority(权重1W) 如果 节点的 Anotation (注解信息)没有设置 key-value:scheduler. alpha.kubernetes.io/ preferAvoidPods = "...",则节点对该 policy 的得分就是10分, 加上权重10000,那么该node对该policy的得分至少10W分。如果Node的Anotation设置了, scheduler.alpha.kubernetes.io/preferAvoidPods = "..." ,如果该 pod 对应的 Controller 是 ReplicationController 或 ReplicaSet, 则该 node 对该 policy 的得分就是0分。
TaintTolerationPriority 使用 Pod 中 tolerationList 与 节点 Taint 列表项进行匹配,配对成功的项越多,则得分越低。污点越匹配,得分越低
ImageLocalityPriority 根据Node上是否存在一个pod的容器运行所需镜像大小对优先级打分,分值为0-10。遍历全部Node, 如果某个Node上pod容器所需的镜像一个都不存在,分值为0; 如果Node上存在Pod容器部分所需镜像,则根据满足当前需求的镜像的大小来决定分值,镜像越大,分值就越高;如果Node上存在pod所需全部镜像,分值为10。默认没有启用
EqualPriority 是一个优先级函数,它给予所有节点相等权重。
MostRequestedPriority 在 ClusterAutoscalerProvider 中,替换 LeastRequestedPriority,给使用多资源的节点,更高的优先级。 计算公式为: (cpu(10 sum(requested) / capacity) + memory(10 sum(requested) / capacity)) / 2默认没有启用

节点亲和性调度

节点亲和性规则:硬亲和性 required 、软亲和性 preferred。

  • 硬亲和性规则不满足时,Pod会置于Pending状态,软亲和性规则不满足时,会选择一个不匹配的节点
  • 当节点标签改变而不再符合此节点亲和性规则时,不会将Pod从该节点移出,仅对新建的Pod对象生效

节点硬亲和性

requiredDuringSchedulingIgnoredDuringExecution
  • 方式一:Pod使用 spec.nodeSelector (基于等值关系)
  • 方式二:Pod使用 spec.affinity 支持matchExpressions属性 (复杂标签选择机制)
# 调度至 zone = foo 的节点
kubectl label nodes kube-node1 zone=foo
apiVersion: v1
kind: Pod
metadata:
  name: with-required-nodeaffinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:  # 定义硬亲和性
        nodeSelectorTerms:
        - matchExpressions:   #集合选择器
          - {key: zone,operator: In,values: ["foo"]}
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1

节点软亲和性

preferredDuringSchedulingIgnoredDuringExecution
  • 柔性控制逻辑,当条件不满足时,能接受被编排于其他不符合条件的节点之上
  • 权重 weight 定义优先级,1-100 值越大优先级越高
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deploy-with-node-affinity
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      name: myapp-pod
      labels:
        app: myapp
    spec:
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:   #节点软亲和性
          - weight: 60
            preference:
              matchExpressions:
              - {key: zone, operator: In, values: ["foo"]}
          - weight: 30
            preference:
              matchExpressions:
              - {key: ssd, operator: Exists, values: []}
      containers:
      - name: myapp
        image: ikubernetes/myapp:v1

Pod资源亲和调度

  • Pod对象间亲和性,将一些Pod对象组织在相近的位置(同一节点、机架、区域、地区)
  • Pod对象间反亲和性,将一些Pod在运行位置上隔开

调度器将第一个Pod放置于任何位置,然后与其有亲和或反亲和关系的Pod据此动态完成位置编排
# 基于MatchInterPodAffinity预选策略完成节点预选,基于InterPodAffinityPriority优选函数进行各节点的优选级评估

位置拓扑,定义"同一位置"

Pod硬亲和调度

requiredDuringSchedulingIgnoredDuringExecution

Pod亲和性描述一个Pod与具有某特征的现存Pod运行位置的依赖关系;即需要事先存在被依赖的Pod对象

# 被依赖Pod
kubectl run tomcat -l app=tomcat --image tomcat:alpine
kubectl explain pod.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution.topologyKey
apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:  # 硬亲和调度
      - labelSelector:
          matchExpressions:    #集合选择器
          - {key: app, operator: In, values: ["tomcat"]}  # 选择被依赖Pod
          # 上面意思是,当前pod要跟标签为app值为tomcat的pod在一起
        topologyKey: kubernetes.io/hostname  # 根据挑选出的Pod所有节点的hostname作为同一位置的判定
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1

Pod软亲和调度

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-with-preferred-pod-affinity
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      name: myapp
      labels:
        app: myapp
    spec:
      affinity:
        podAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 80
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - {key: app, operator: In, values: ["cache"]}
              topologyKey: zone
          - weight: 20
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - {key: app, operator: In, values: ["db"]}
              topologyKey: zone
      containers:
      - name: myapp
        image: ikubernetes/myapp:v1

Pod反亲和调度

Pod反亲和调度用于分散同一类应用,调度至不同的区域、机架或节点等
spec.affinity.podAffinity替换为 spec.affinity.podAntiAffinity

反亲和调度也分为柔性约束和强制约束

apiVersion: v1
kind: Pod
metadata:
    name: pod-first
    labels: 
        app: myapp
        tier: fronted
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: ["/bin/sh", "-c", "sleep 3600"]
    affinity:
      podAntiAffinity:
        requiredDuringSchedulingIgnoredDuringExecution:
        - labelSelector:
            matchExpressions:
            - {key: app, operator: In, values: ["myapp"]}
          topologyKey: zone

污点和容忍度

污点 taints 是定义在节点上的键值型属性数据,用于让节点拒绝将Pod调度运行于其上,除非Pod有接纳节点污点的容忍度容忍度 tolerations 是定义在Pod上的键值属性数据,用于配置可容忍的污点,且调度器将Pod调度至其能容忍该节点污点的节点上或没有污点的节点上

使用PodToleratesNodeTaints预选策略和TaintTolerationPriority优选函数完成该机制

  • 节点亲和性使得Pod对象被吸引到一类特定的节点 (nodeSelector和affinity)
  • 污点提供让节点排斥特定Pod对象的能力

定义污点和容忍度

污点定义于nodes.spec.taints容忍度定义于pods.spec.tolerations
语法: key=value:effect

effect定义排斥等级:

  • NoSchedule,不能容忍,但仅影响调度过程,已调度上去的pod不受影响,仅对新增加的pod生效。
  • PreferNoSchedule,柔性约束,节点现存Pod不受影响,如果实在是没有符合的节点,也可以调度上来
  • NoExecute,不能容忍,当污点变动时,Pod对象会被驱逐

在Pod上定义容忍度时:

  1. 等值比较 容忍度与污点在key、value、effect三者完全匹配
  2. 存在性判断 key、effect完全匹配,value使用空值

一个节点可配置多个污点,一个Pod也可有多个容忍度

管理节点的污点

同一个键值数据,effect不同,也属于不同的污点

给节点添加污点:

kubectl taint node <node-name> <key>=<value>:<effect>
kubectl taint node node2 node-type=production:NoShedule  #举例

查看节点污点:

kubectl get nodes <nodename> -o go-template={{.spec.taints}}

删除节点污点:

kubectl taint node <node-name> <key>[:<effect>]-
kubectl patch nodes <node-name> -p '{"spec":{"taints":[]}}'

kubectl taint node kube-node1 node-type=production:NoSchedule
kubectl get nodes kube-node1 -o go-template={{.spec.taints}}
# 删除key为node-type,effect为NoSchedule的污点
kubectl taint node kube-node1 node-type:NoSchedule-

# 删除key为node-type的所有污点
kubectl taint node kube-node1 node-type-

# 删除所有污点
kubectl patch nodes kube-node1 -p '{"spec":{"taints":[]}}'

给Pod对象容忍度

spec.tolerations字段添加
tolerationSeconds用于定义延迟驱逐Pod的时长

# 等值判断
tolerations:
- key: "key1"
  operator: "Equal"  #判断条件为Equal
  value: "value1"
  effect: "NoExecute"
  tolerationSeconds: 3600
 
# 存在性判断
tolerations:
- key: "key1"
  operator: "Exists"    #存在性判断,只要污点键存在,就可以匹配
  effect: "NoExecute"
  tolerationSeconds: 3600
apiVersion: 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:v1
            ports:
            - name: http
              containerPort: 80
            tolerations:
            - key: "node-type"
              operator: "Equal"
              value: "production":
              effect: "NoExecute"
              tolerationSeconds: 3600

问题节点标识

自动为节点添加污点信息,使用NoExecute效用标识,会驱逐现有Pod
K8s核心组件通常都容忍此类污点

node.kubernetes.io/not-ready 节点进入NotReady状态时自动添加
node.alpha.kubernetes.io/unreachable 节点进入NotReachable状态时自动添加
node.kubernetes.io/out-of-disk 节点进入OutOfDisk状态时自动添加
node.kubernetes.io/memory-pressure 节点内存资源面临压力
node.kubernetes.io/disk-pressure 节点磁盘面临压力
node.kubernetes.io/network-unavailable 节点网络不可用
node.cloudprovider.kubernetes.io/uninitialized kubelet由外部云环境程序启动时,自动添加,待到去控制器初始化此节点时再将其删除

Pod优选级和抢占式调度

优选级,Pod对象的重要程度
优选级会影响节点上Pod的调度顺序和驱逐次序
一个Pod对象无法被调度时,调度器会尝试抢占(驱逐)较低优先级的Pod对象,以便可以调度当前Pod

Pod优选级和抢占机制默认处于禁用状态
启用:同时为kube-apiserver、kube-scheduler、kubelet程序的 --feature-gates 添加 PodPriority=true
使用:
事先创建优先级类别,并在创建Pod资源时通过 priorityClassName属性指定所属的优选级类别

posted @ 2019-10-25 18:01  戴红领巾的少年  阅读(11979)  评论(0编辑  收藏  举报
感谢您的阅读,如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮。本文欢迎各位转载,但是转载文章之后必须在文章页面中给出作者和原文连接