Kubernetes——调度器Scheduler

一、简介

Scheduler 是 kubernetes 的调度器,主要的任务是把定义的 pod 分配到集群的节点上。听起来非常简单,但有很多要考虑的问题:

  • 公平:如何保证每个节点都能被分配资源
  • 资源高效利用:集群所有资源最大化被使用
  • 效率:调度的性能要好,能够尽快地对大批量的 pod 完成调度工作
  • 灵活:允许用户根据自己的需求控制调度的逻辑

Scheduler 是作为单独的程序运行的,启动之后会一直坚挺 API Server,获取PodSpec.NodeName为空的 pod,对每个 pod 都会创建一个 binding(必须遵守的),表明该 pod 应该放到哪个节点上

Scheduler

当Scheduler通过API server 的watch接口监听到新建Pod副本的信息后,它会检查所有符合该Pod要求的Node列表,开始执行Pod调度逻辑。调度成功后将Pod绑定到目标节点上。Scheduler在整个系统中承担了承上启下的作用,承上是负责接收创建的新Pod,为安排一个落脚的地(Node),启下是安置工作完成后,目标Node上的kubelet服务进程接管后继工作,负责Pod生命周期的后半生。具体来说,Scheduler的作用是将待调度的Pod安装特定的调度算法和调度策略绑定到集群中的某个合适的Node上,并将绑定信息传给API server 写入etcd中。整个调度过程中涉及三个对象,分别是:待调度的Pod列表,可以的Node列表,以及调度算法和策略。

Kubernetes Scheduler 提供的调度流程分三步:

  • 1、预选策略(predicate) 遍历nodelist,选择出符合要求的候选节点,Kubernetes内置了多种预选规则供用户选择。
  • 2、优选策略(priority) 在选择出符合要求的候选节点中,采用优选规则计算出每个节点的积分,最后选择得分最高的。
  • 3、选定(select) 如果最高得分有好几个节点,select就会从中随机选择一个节点。
  • 4、如果其中任何一步有错误,直接返回错误。

如图:


预选策略算法的集合在官方源码

常用的预选策略(代码里的策略不一定都会被使用)

  • CheckNodeConditionPred:检查节点是否正常
  • GeneralPred:通用预选策略,包含多种策略:
  • HostName:(如果pod定义hostname属性,会检查节点是否匹配。pod.spec.hostname)、
  • PodFitsHostPorts:(检查pod要暴露的hostpors是否被占用。pod.spec.containers.ports.hostPort)
  • MatchNodeSelector:pod.spec.nodeSelector 看节点标签能否适配pod定义的nodeSelector
  • PodFitsResources:判断节点的资源能够满足Pod的定义(如果一个pod定义最少需要2C4G node上的低于此资源的将不被调度。用kubectl describe node NODE名称 可以查看资源使用情况)
  • NoDiskConflict:判断pod定义的存储是否在node节点上使用。(默认没有启用)
  • PodToleratesNodeTaints:检查pod上Tolerates的能否容忍污点(pod.spec.tolerations)
  • PodToleratesNodeNoExecuteTaints:Pod允许节点不执行污染;默认不启用。
  • CheckNodeLabelPresence:检查节点上的标志是否存在 (默认没有启动)
  • CheckServiceAffinity:根据pod所属的service。将相同service上的pod尽量放到同一个节点(默认没有启动)
  • CheckVolumeBinding:检查是否可以绑定(默认没有启动)
  • NoVolumeZoneConflict:检查是否在一起区域(默认没有启动)
  • CheckNodeMemoryPressure:检查内存是否存在压力
  • CheckNodeDiskPressure:检查磁盘IO压力是否过大
  • CheckNodePIDPressure:检查pid资源是否过大
  • MatchInterPodAffinity:定义匹配POD间关联的名称
  • MaxEBSVolumeCount:亚马逊弹性存储卷最大数量,默认39
  • MaxGCEPDVolumeCount:谷歌容器引擎最大存储卷数量,默认16
  • MaxAzureDiskVolumeCount:Azure最大磁盘数量,默认16

如果在 predicate 过程中没有合适的节点,pod 会一直在pending状态(pending:等待),不断重试调度,直到有节点满足条件。经过这个步骤,如果有多个节点满足条件,就继续 priorities 过程:按照优先级大小对节点排序

优先级由一系列键值对组成,键是该优先级项的名称,值是它的权重(该项的重要性)。这些优先级选项包括:

优选策略

  • LeastRequested:选择消耗最小的节点(根据空闲比率评估 cpu(总容量-sum(已使用)*10/总容量) )
  • BalancedResourceAllocation:从节点列表中选出各项资源使用率最均衡的节点(CPU和内存)
  • NodePreferAvoidPods:节点倾向
  • TaintToleration:将pod对象的spec.toleration与节点的taints列表项进行匹配度检查,匹配的条目越多,得分越低。
  • SelectorSpreading:与services上其他pod尽量不在同一个节点上,节点上通一个service的pod越少得分越高。
  • InterpodAffinity:遍历node上的亲和性条目,匹配项越多的得分越高
  • NodeAffinity:根据节点亲和性。
  • MostRequested:选择消耗最大的节点上(尽量将一个节点上的资源用完)
  • NodeLabel:根据节点标签得分,存在标签既得分,没有标签没得分。标签越多 得分越高。
  • ImageLocality:节点上有所需要的镜像既得分,所需镜像越多得分越高。(根据已有镜像体积大小之和)

高级调度方式

当我们想把调度到预期的节点,我们可以使用高级调度分为:

  • 节点选择器: nodeSelector、nodeName
  • 节点亲和性调度: nodeAffinity
  • Pod亲和性调度:PodAffinity
  • Pod反亲和性调度:podAntiAffinity

自定义调度器

除了 kubernetes 自带的调度器,你也可以编写自己的调度器。通过spec:schedulername参数指定调度器的名字,可以为 pod 选择某个调度器进行调度。比如下面的 pod 选择my-scheduler进行调度,而不是默认的default-scheduler:

apiVersion: v1
kind: Pod
metadata:
  name: annotation-second-scheduler
  labels:
    name: multischeduler-example
spec:
  schedulername: my-scheduler
  containers:
  - name: pod-with-second-annotation-container
    image: gcr.io/google_containers/pause:2.0

NodeSelector

我们定义一个pod,让其选择带有node=ssd这个标签的节点

apiVersion: v1
kind: Pod
metadata:
  name: pod-1
  labels:
    name: myapp
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
  nodeSelector:
    node: ssd

kubectl apply -f test.yaml

查看信息

#get一下pod 一直处于Pending状态
$ kubectl get pod 
NAME      READY     STATUS    RESTARTS   AGE
pod-1     0/1       Pending   0          7s
#查看详细信息,是没有可用的selector
$ kubectl describe pod pod-1
...
Events:
  Type     Reason            Age                From               Message
  ----     ------            ----               ----               -------
  Warning  FailedScheduling  9s (x14 over 36s)  default-scheduler  0/4 nodes are available: 4 node(s) didn't match node selector.
#我们给node2打上这个标签
$  kubectl label node k8s-node02 node=ssd
node/k8s-node02 labeled
#Pod正常启动
$  kubectl describe pod pod-1
....
Events:
  Type     Reason            Age                From                 Message
  ----     ------            ----               ----                 -------
  Warning  FailedScheduling  2m (x122 over 8m)  default-scheduler    0/4 nodes are available: 4 node(s) didn't match node selector.
  Normal   Pulled            7s                 kubelet, k8s-node02  Container image "ikubernetes/myapp:v1" already present on machine
  Normal   Created           7s                 kubelet, k8s-node02  Created container
  Normal   Started           7s                 kubelet, k8s-node02  Started container

nodeAffinity 节点亲和性

kubectl explain pod.spec.affinity.nodeAffinity

  • requiredDuringSchedulingIgnoredDuringExecution:硬亲和性 必须满足亲和性。
  • matchExpressions:匹配表达式,这个标签可以指定一段,例如pod中定义的key为zone,operator为In(包含那些),values为 foo和bar。就是在node节点中包含foo和bar的标签中调度
  • matchFields:匹配字段 和上面的意思 不过他可以不定义标签值,可以定义
  • preferredDuringSchedulingIgnoredDuringExecution:软亲和性 能满足最好,不满足也没关系。
  • preference:优先级
  • weight:权重1-100范围内,对于满足所有调度要求的每个节点,调度程序将通过迭代此字段的元素计算总和,并在节点与对应的节点匹配时将“权重”添加到总和。

运算符包含:

  • In:label 的值在某个列表中
  • NotIn:label 的值不在某个列表中
  • Gt:label 的值大于某个值
  • Lt:label 的值小于某个值
  • Exists:某个 label 存在
  • DoesNotExist:某个 label 不存在
  • 可以使用NotIn和DoesNotExist实现节点反关联行为。

硬亲和性:

apiVersion: v1
kind: Pod
metadata:
  name: node-affinity-pod
  labels:
    name: myapp
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: zone
            operator: In
            values:
            - foo
            - bar
$ kubectl apply -f pod-affinity-demo.yaml 
$ kubectl describe pod node-affinity-pod 
.....
Events:
  Type     Reason            Age                From               Message
  ----     ------            ----               ----               -------
  Warning  FailedScheduling  33s (x25 over 1m)  default-scheduler  0/4 nodes are available: 4 node(s) didn't match node selector.
# 给其中一个node打上foo的标签
$ kubectl label node k8s-node03 zone=foo
$ kubectl get pods
NAME                READY     STATUS    RESTARTS   AGE
node-affinity-pod   1/1       Running   0          8m

软亲和性:

requiredDuringSchedulingIgnoredDuringExecution比较,这里需要注意的是preferredDuringSchedulingIgnoredDuringExecution是个列表项,而preference不是一个列表项了。

apiVersion: v1
kind: Pod
metadata:
  name: node-affinity-pod-2
  labels:
    name: myapp
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 50
        preference:
          matchExpressions:
          - key: zone
            operator: In
            values:
            - foo
            - bar
$ kubectl get pod -o wide 
NAME                  READY     STATUS    RESTARTS   AGE       IP           NODE
node-affinity-pod     1/1       Running   0          3h        10.244.3.2   k8s-node03
node-affinity-pod-2   1/1       Running   0          1m        10.244.3.3   k8s-node03

podAffinity

Pod亲和性场景,我们的k8s集群的节点分布在不同的区域或者不同的机房,当服务A和服务B要求部署在同一个区域或者同一机房的时候,我们就需要亲和性调度了。

kubectl explain pod.spec.affinity.podAffinity 和NodeAffinity是一样的,都是有硬亲和性和软亲和性

  • labelSelector:选择跟那组Pod亲和
  • namespaces:选择哪个命名空间
  • topologyKey:指定节点上的哪个键

硬亲和性:

apiVersion: v1
kind: Pod
metadata:
  name: node-affinity-pod1
  labels:
    name: podaffinity-myapp
    tier: service
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
---
apiVersion: v1
kind: Pod
metadata:
  name: node-affinity-pod2
  labels:
    name: podaffinity-myapp
    tier: front
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: name
            operator: In
            values:
            - podaffinity-myapp
        topologyKey: kubernetes.io/hostname

查看

kubectl get pods -o wide 
NAME                  READY     STATUS    RESTARTS   AGE       IP           NODE
node-affinity-pod1    1/1       Running   0          12s       10.244.2.6   k8s-node02
node-affinity-pod2    1/1       Running   0          12s       10.244.2.5   k8s-node02

podAntiAffinity

Pod反亲和性场景,当应用服务A和数据库服务B要求尽量不要在同一台节点上的时候。
kubectl explain pod.spec.affinity.podAntiAffinity也分为硬反亲和性和软反亲和性调度(和podAffinity一样的配置)

#首先把两个node打上同一个标签。
kubectl label node k8s-node02 zone=foo 
kubectl label node k8s-node03 zone=foo
#反硬亲和调度
apiVersion: v1
kind: Pod
metadata:
  name: node-affinity-pod1
  labels:
    name: podaffinity-myapp
    tier: service
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
---
apiVersion: v1
kind: Pod
metadata:
  name: node-affinity-pod2
  labels:
    name: podaffinity-myapp
    tier: front
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: name
            operator: In
            values:
            - podaffinity-myapp
        topologyKey: zone

查看一下(因为zone这个key在每个node都有会,所以第二个Pod没有办法调度,所以一直Pending状态)

$ kubectl get  pod 
NAME                 READY     STATUS    RESTARTS   AGE
node-affinity-pod1   1/1       Running   0          11s
node-affinity-pod2   0/1       Pending   0          11s

亲和性/反亲和性调度策略比较如下:

调度策略 匹配标签 操作符 拓扑域支持 调度目标
nodeAffinity 主机 In、NotIn、Exists、
DoesNotExist、Gt、Lt
指定主机
podAffinity POD In、NotIn、Exists、
DoesNotExist
POD与指定POD同一拓扑域
podAntiAffinity POD In、NotIn、Exists、
DoesNotExist
POD与指定POD不在同一拓扑域

污点容忍调度(Taint和Toleration)

前两种方式都是pod选择那个pod,而污点调度是node选择的pod,污点就是定义在节点上的键值属性数据。主要作用是让节点拒绝pod,拒绝不合法node规则的pod。Taint(污点)和 Toleration(容忍)是相互配合的,可以用来避免 pod 被分配到不合适的节点上,每个节点上都可以应用一个或多个 taint ,这表示对于那些不能容忍这些 taint 的 pod,是不会被该节点接受的。如果将 toleration 应用于 pod上,则表示这些 pod 可以(但不要求)被调度到具有匹配 taint 的节点上

Taint(污点)

Taint是节点上属性,我们看一下Taints如何定义
kubectl explain node.spec.taints(对象列表)

每个污点有一个 key 和 value 作为污点的标签,其中 value 可以为空,effect 描述污点的作用。当前 taint effect 支持如下三个选项:

  • key:定义一个key
  • value:定义一个值
  • effect:pod不能容忍这个污点时,他的行为是什么,行为分为三种:
  • NoSchedule:仅影响调度过程,对现存的pod不影响。
  • PreferNoSchedule:系统将尽量避免放置不容忍节点上污点的pod,但这不是必需的。就是软版的NoSchedule
  • NoExecute:既影响调度过程,也影响现存的pod,不满足的pod将被驱逐。

污点的设置、查看和去除
使用kubectl taint命令可以给某个 Node 节点设置污点,Node 被设置上污点之后就和 Pod 之间存在了一种相斥的关系,可以让 Node 拒绝 Pod 的调度执行,甚至将 Node 已经存在的 Pod 驱逐出去

#查看节点污点
kubectl describe node node-name

# 设置污点
kubectl taint nodes node1 key1=value1:NoSchedule
# 节点说明中,查找 Taints 字段
kubectl describe pod pod-name 
# 去除污点
kubectl taint nodes node1 key1=value1:NoSchedule-

tolerations

  • key:被容忍的key
  • tolerationSeconds:被驱逐的宽限时间,默认是0 就是立即被驱逐
  • value:被容忍key的值
  • operator:Exists只要key在就可以调度,Equal(等值比较)必须是值要相同
  • effect:节点调度后的操作

创建一个容忍:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deploy
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
      release: dev
  template:
    metadata:
      labels:
        app: myapp
        release: dev
    spec:
      containers:
      - name: myapp-containers
        image: ikubernetes/myapp:v2
        ports:
        - name: http
          containerPort: 80
      tolerations:
      - key: "node-type"
        operator: "Equal"
        value: "prod"
        effect: "NoSchedule"

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

tolerations:
- operator: "Exists"

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

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

3、有多个 Master 存在时,防止资源浪费,可以如下设置

kubectl taint nodes Node-Name node-role.kubernetes.io/master=:PreferNoSchedule

指定调度节点

1、Pod.spec.nodeName:将 Pod 直接调度到指定的 Node 节点上,会跳过 Scheduler 的调度策略,该匹配规则是强制匹配

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: myweb
spec:
  replicas: 7
  template:
    metadata:
      labels:
        app: myweb
    spec:
      nodeName: k8s-node01
      containers:
      - name: myweb
        image: hub.yibo.com/library/myapp:v1
        ports:
        - containerPort: 80

2、Pod.spec.nodeSelector:通过 kubernetes 的 label-selector 机制选择节点,由调度器调度策略匹配 label,而后调度 Pod 到目标节点,该匹配规则属于强制约束

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: myweb
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: myweb
    spec:
      nodeSelector:
        type: backEndNode1
      containers:
      - name: myweb
        image: harbor/tomcat:8.5-jre8
        ports:
        - containerPort: 80

参考:
https://www.cnblogs.com/xzkzzz/p/9963511.html

https://www.cnblogs.com/easonscx/p/10601699.html

https://www.cnblogs.com/LiuQizhong/p/11905486.html

https://www.cnblogs.com/Smbands/p/10949478.html

https://www.cnblogs.com/cocowool/p/taints_and_tolerations.html

https://www.cnblogs.com/Smbands/p/10949478.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345