成都网站建设设计

将想法与焦点和您一起共享

docker笔记33-调度器、预选策略及优选函数-创新互联

     master是工作平面,上面运行着三个最核心的组件,apiserver、scheduler、controller manager。除此之外,master还依赖于ectd存储节点,最好ectd是有冗余能力的集群,

安岳ssl适用于网站、小程序/APP、API接口等需要进行数据传输应用场景,ssl证书未来市场广阔!成为成都创新互联公司的ssl证书销售渠道,可以享受市场价格4-6折优惠!如果有意向欢迎电话联系或者加微信:18982081108(备注:SSL证书合作)期待与您的合作!

 调度器(scheduler)   

   master上的 scheduler控制着pod运行在哪个node上的。不指定就用default scheduler。

   调度器的功能就是调度Pod在哪个Node上运行,这些调度信息存储在master上的etcd里面。能够和etcd打交道的只有apiserver。

    kubelete运行在node节点上,监控着Node节点上的pod状态,并参与pod的创建等工作。

    kube-proxy也运行在node节点上,它监控着service资源的变动。

    kubelete和kube-proxy都要连接master上的apiserver去获取定义信息。

预选步骤

    default scheduler是通过三个步骤来实现调度的:

        a)、预选策略(predicate):先排除那些完全不符合此pod运行法则的节点,有两个维度来排除,一个是最低资源需求,即节点必须满足此Pod的最低资源;第二个是资源限额,即当前节点最多能给pod提供的资源。

        b)、优选(priority):在符合节点找到最佳匹配的节点。

        c)、绑定(select):把pod绑定到优选的节点上。

[root@master ~]# kubectl explain pod.spec
nodeName:指定pod运行在指定节点上
nodeSelector:用nodeSelector指定Pod运行在拥有某种selector 标签的节点上。

    在k8s上,调度方式有这么几类:   

    1、nodeaffinity,表示node亲和性调度,表示这个pod对这个节点有一定的倾向性。我们通过上面的nodeselector来完成这类调度

    2、podaffinity或podunaffinity:pod亲和性或者pod反亲和性调度,有时我们期望某些Pod运行在同一个节点上或者是相邻的节点上,或者我们期望某些Pod不要运行在某些节点上。

    3、taints和tolerations:污点和污点容忍调度:可以在某些节点上打上污点,表示这些节点不让pod在其上面运行。taints是定义在节点之上的,tolerations是定义在pod之上的。

常用的预选策略

    访问 https://github.com/kubernetes/kubernetes/blob/master/pkg/scheduler/algorithm/predicates/predicates.go  ,查看k8s源代码,可以看到这些策略。

    1、ChekNodeCondition

    表示检查是否可以在节点磁盘、网络不可用,或未准备好的前提下,能够把pod调度到上去。

    2、GeneralPredicate

       a) hostname预选策略,表示如果pod定义了hostname属性(pod.spec.hostname),则检查节点的名字跟pod的hostname是否想匹配,但这里并不是定义这个pod必须运行在这些节点上。

       b)PodFitsHostPorts:如果节点定义了hostport属性(pod.spec.containers.ports.hostPort),表示指定在节点的哪个端口上。如果这个节点的端口被占用了,那么这个节点就不适合运行这个pod。

       c)MatchNodeSelector:检查pods.spec.nodeSelector这个字段你是否定义了,运行在携有这有这些标签选择器的节点上。

       d) PodFitsResources:检查节点是否有足够的资源来支持这个pod运行。

[root@master ~]# kubectl describe nodes node1
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource  Requests(需求量)   Limits(限额)
  --------  --------   ------
  cpu       360m (9%)  110m (2%)
  memory    70Mi (1%)  70Mi (1%)
Events:     

    e)NoDiskConfict:检查Pod所请求存储卷在此节点上是否不可用。 注意NoDiskConfict调度策略默认没有启用。

    f) PodToleratesNodeTaints:如果Pod定义了Tolerates(容忍度),即 pods.spec.tolerations,那么就看pod能不能容忍节点上的污点,如果是,表示这个节点可以被选定;

    g) PodToleratesNodeNoExecuteTaints:检查pod是否容忍节点上有NoExecute污点。NoExecute这个污点是啥意思呢。如果一个pod上运行在一个没有污点的节点上后,这个节点又给加上污点了,那么NoExecute表示这个新加污点的节点会祛除其上正在运行的pod;不加NoExecute不会祛除节点上运行的pod,表示接受既成事实,这是默认策略。

    h) CheckNodeLabelPresence:检查节点上指定标签的存在性,如果节点有pod指定的标签,那么这个节点就被选中。 这个调度策略默认没有启用。

    i) CheckServceAffinity:一个service下可以有多个pod,比如这些pod都运行在1、2、3机器上,而没有运行在4、5、6机器上,那么CheckServceAffinity就表示新加入的pod都集中运行在1、2、3机器上,这样集中好处是一个service下pod之间内部通信的效率变高了。 这个策略默认也是没有启用的。

    j)MaxEBSVolumeCountPred:检查节点上挂载的EBS存储器(亚马逊的弹性块存储)的数量是否超出了大的设定值。一般来说,一个节点上最多可以挂载39个存储卷。可以编译安装k8s指定该值。

    k) MaxGCEPDVolumeCountPred:GCE是谷歌的云存储引擎。可以编译安装k8s指定该值。

    l) MaxAzureDiskVolumeCountPred:pod会选择挂载足量Azure存储卷的节点。

    m) CheckVolumeBinding:检查节点上的pvc是否被别的pod绑定了

    n) NoVolumeZoneConfict: 检查节点上的pod是否与pod的需求冲突。

    o) CheckNodeMemoryPressure:检查节点内存是否存在压力

    p)  CheckNodePIDpressure:检查节点pid数量是否压力过大,但一般pid是可以重复使用的。

    q)CheckNodeDiskPressure:

    r) MatchInterPodAffinity:检查Pod是否满足亲和性或者反亲和性。

    如果一个节点的第一个策略符合了,k8s

   常用的优选函数 

        访问 https://github.com/kubernetes/kubernetes/tree/master/pkg/scheduler/algorithm/priorities  可以看到k8s源码里面的优选函数。

    balanced_resource_allocation.go:均衡资源的使用方式,表示以cpu和内存占用率的相近程度作为评估标准,二者占用越接近,得分就越高,得分高的胜出。

    image_locality.go:表示根据满足当前pod对象需求的已有镜的体积大小之和来选择节点的。

    least_requested.go:最少请求,空闲比例。对cpu来说,用cpu((capacity-sum(requested))*10/capacity)方式进行评估,得分最高的胜出。

    most_requested.go:表示尽可能的把一个节点的资源先用完,这个和least_requested相反,二者不能同时使用。

    node_prefer_avoid_pods.go:看节点是否有注解信息"scheduler.alpha.kubernetes.io/preferAvoidPods"。没有这个注解信息,说明这个节点是适合运行这个pod的。

    node_affinity.go:根据pod对象中的nodeselector,对节点进行匹配度检查,能够成功匹配的数量越多,得分就越高。

    taint_toleration.go:将pod对象的spec.toleration与节点的taint列表项进行匹配度检查,匹配的条目越多,得分越低。

    selector_spreading.go:spreading是散开的意思,查找当前pod对象对应的service,statefulset,replicatset等所匹配的标签选择器,在节点上运行的带有这样标签的pod越少得分越高,这样的pod优选被选出。 这就是说我们要把同一个标签选择器下运行的pod散开(spreading)到多个节点上。

    interpod_affinity.go:遍历Pod对象亲和性的条目,并将那些能够匹配到节点权重相加,值越大的得分越高,得分高的胜出。

    node_label.go:根据节点是否拥有标签,来评估分数。

高级调度方式

    包括两类:

    节点选择器:nodeSelector(给node打上标签,pod通过标签预选节点),nodeName

    节点亲和调度:nodeAffinity

节点选择器(nodeSelector,nodeName)

[root@master ~]# kubectl explain pods.spec.nodeSelector
[root@master scheduler]# cat pod-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
  namespace: default
  labels:
    app: myapp  #kv格式的,也可以用花括号表示
    tier: frontend #定义所属的层次
  annotations:
    chenzx.com/created-by: "cluster-admin" #这是注解的键值对
spec:
  containers: 
  - name: myapp  #前面的-号表示这是一个列表格式的,也可以用中括号表示
    image: ikubernetes/myapp:v1
  nodeSelector: #指定该pod运行在有disktype=ssd标签的node节点上
    disktype: harddisk #自己随便写
[root@master scheduler]# kubectl apply -f pod-demo.yaml 
pod/pod-demo created

[root@master scheduler]# kubectl get pods -o wide |grep demo

pod-demo                           0/1       Pending            0          14s              

    可见,我们的pod-demo没有运行起来,这是因为没有任何节点打了disktype=harddisk标签。

    那我们就给node2节点打disktype=harddisk标签试试:

[root@master scheduler]# kubectl  label nodes node2 disktype=harddisk
node/node2 labeled
[root@master scheduler]# kubectl get nodes --show-labels  
NAME      STATUS    ROLES     AGE       VERSION   LABELS
master    Ready     master    34d       v1.11.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=master,node-role.kubernetes.io/master=
node1     Ready         34d       v1.11.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/hostname=node1
node2     Ready         34d       v1.11.2   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=harddisk,kubernetes.io/hostname=node2
[root@master scheduler]#  kubectl get pods -o wide |grep demo
pod-demo                           1/1       Running            0          5m        10.244.2.29   node2         0          3m        10.244.1.51   node1

    可以看到,我们新建立的pod-demo运行在node2节点上,因为只有node2才有disktype=harddisk标签。

  节点亲和调度(nodeAffinity)

[root@master scheduler]# kubectl explain pods.spec.affinity
[root@master scheduler]# kubectl explain pods.spec.affinity.nodeAffinity
preferredDuringSchedulingIgnoredDuringExecution:软亲和,可以满足,也可以不满足
requiredDuringSchedulingIgnoredDuringExecution:硬亲和,表示必须满足
[root@master ~]# kubectl explain pods.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms.matchExpressions
[root@master scheduler]# cat pod-nodeaffinity-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-node-affinity-demo
  namespace: default
  labels:
    app: myapp  #kv格式的,也可以用花括号表示
    tier: frontend #定义所属的层次
  annotations:
    chenzx.com/created-by: "cluster-admin" #这是注解的键值对
spec:
  containers: 
  - name: myapp  #前面的-号表示这是一个列表格式的,也可以用中括号表示
    image: ikubernetes/myapp:v1
  affinity:
     nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution: #硬亲和性
        nodeSelectorTerms:
        - matchExpressions:
          - key: zone
            operator: In
            values:
            - foo
            - bar
[root@master scheduler]# kubectl apply -f pod-nodeaffinity-demo.yaml 
pod/pod-node-affinity-demo created
[root@master ~]# kubectl get pods -o wide |grep affinity
pod-node-affinity-demo             0/1       Pending            0          15s               

    因为我们没有任何一个节点拥有zone这个标签,所以pod-node-affinity-demo运行不起来。

    下面我们再创建一个软亲和性的pod。

[root@master scheduler]# cat pod-nodeaffinity-demo2.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-node-affinity-demo-2
  namespace: default
  labels:
    app: myapp  #kv格式的,也可以用花括号表示
    tier: frontend #定义所属的层次
  annotations:
    chenzx.com/created-by: "cluster-admin" #这是注解的键值对
spec:
  containers: 
  - name: myapp  #前面的-号表示这是一个列表格式的,也可以用中括号表示
    image: ikubernetes/myapp:v1
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution: #软亲和性
      - preference:
          matchExpressions:
          - key: zone
            operator: In
            values:
            - foo
            - bar
        weight: 60
 
[root@master scheduler]# kubectl apply -f pod-nodeaffinity-demo2.yaml 
pod/pod-node-affinity-demo-2 created
[root@master scheduler]# kubectl get pods -o wide |grep affinity-demo-2
pod-node-affinity-demo-2           1/1       Running            0          1m        10.244.2.30   node2

    看到pod-node-affinity-demo-2 运行起来了,因为这个pod我们是定义的软亲和性,表示即使任何节点都没有zone标签,也会找个节点让Pod运行起来。

pod亲和调度 

docker笔记33-调度器、预选策略及优选函数   

    rack是机柜的意思,如果是用rack1、rack2、rack3、rack4这样表示节点,那么每个节点都是不同的位置;可是我们给每个节点都打一个row=row1,即表示这些机器都在第一排机柜上,那么这四个机器又在同一位置,即第一排。所以我们可以用row这样的方式来做亲和性。

 [root@master scheduler]# kubectl explain pods.spec.affinity.podAffinity
 preferredDuringSchedulingIgnoredDuringExecution:软亲和,尽量满足标签
 requiredDuringSchedulingIgnoredDuringExecution:硬亲和,必须满足标签
[root@master scheduler]# kubectl explain pods.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution
topologyKey:定义上图row那样的键
labelSelector:表示选定一组资源,跟哪些pod进行亲和;
namespaces:指定Pod属于哪个名称空间中,一般我们不跨名称空间去引用。
[root@master scheduler]# cat  pod-required-affnity-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-first
  namespace: default
  labels:
    app: myapp  #kv格式的,也可以用花括号表示
    tier: frontend #定义所属的层次
  annotations:
    chenzx.com/created-by: "cluster-admin" #这是注解的键值对
spec:
  containers: 
  - name: myapp  #前面的-号表示这是一个列表格式的,也可以用中括号表示
    image: ikubernetes/myapp:v1
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-second
  namespace: default
  labels:
    app: db  #kv格式的,也可以用花括号表示
    tier: backend #定义所属的层次
  annotations:
    chenzx.com/created-by: "cluster-admin" #这是注解的键值对
spec:
  containers: 
  - name: busybox  #前面的-号表示这是一个列表格式的,也可以用中括号表示
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command: ["sh","-c","sleep 3600"]
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution: #硬亲和性
      - labelSelector:
          matchExpressions: #匹配pod的,而不是匹配节点
          - {key: app, operator: In, values: ["myapp"]} #意思是当前这个pod要跟一个有着标签app=myapp(要和上面pod-first的metadata里面的标签一致)的pod在一起
        topologyKey: kubernetes.io/hostname
[root@master scheduler]# kubectl apply -f pod-required-affnity-demo.yaml 
pod/pod-first unchanged
pod/pod-second created
[root@master scheduler]# kubectl get pods -o wide
NAME                               READY     STATUS             RESTARTS   AGE       IP            NODE
pod-first                          1/1       Running            0          2m        10.244.2.33   node2
pod-second                         1/1       Running            0          1m        10.244.2.34   node2

    上面看到我们的两个pod都运行在同一个节点了,这是因为pod-second会和pod-first运行在同一个节点上。

pod反亲和调度

[root@master ~]# kubectl explain pods.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution
[root@master scheduler]# cat  pod-required-anti-affnity-demo.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-first
  namespace: default
  labels:
    app: myapp  #kv格式的,也可以用花括号表示
    tier: frontend #定义所属的层次
  annotations:
    chenzx.com/created-by: "cluster-admin" #这是注解的键值对
spec:
  containers: 
  - name: myapp  #前面的-号表示这是一个列表格式的,也可以用中括号表示
    image: ikubernetes/myapp:v1
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-second
  namespace: default
  labels:
    app: db  #kv格式的,也可以用花括号表示
    tier: backend #定义所属的层次
  annotations:
    chenzx.com/created-by: "cluster-admin" #这是注解的键值对
spec:
  containers: 
  - name: busybox  #前面的-号表示这是一个列表格式的,也可以用中括号表示
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command: ["sh","-c","sleep 3600"]
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution: #硬亲和性
      - labelSelector:
          matchExpressions: #匹配pod的,而不是匹配节点
          - {key: app, operator: In, values: ["myapp"]} #意思是当前这个pod要跟一个有着标签app=myapp(要和上面pod-first的metadata里面的标签一致)的pod在一起
        topologyKey: kubernetes.io/hostname
[root@master scheduler]# kubectl apply -f pod-required-anti-affnity-demo.yaml 
pod/pod-first created
pod/pod-second created
[root@master scheduler]# kubectl get pods -o wide
NAME                               READY     STATUS             RESTARTS   AGE       IP            NODE
pod-first                          1/1       Running            0          1m        10.244.2.35   node2
pod-second                         0/1       Running            0          1m        10.244.2.36        node1

    上面可以看到pod-first和pod-second就不会被调度到同一个节点上。

    下面给两个节点打标签,给两个节点打一样的标签

[root@master scheduler]# kubectl label nodes node2 zone=foo
node/node2 labeled
[root@master scheduler]# kubectl label nodes node1 zone=foo
node/node1 labeled
[root@master scheduler]# cat  pod-required-anti-affnity-demo.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-first
  namespace: default
  labels:
    app: myapp  #kv格式的,也可以用花括号表示
    tier: frontend #定义所属的层次
  annotations:
    chenzx.com/created-by: "cluster-admin" #这是注解的键值对
spec:
  containers: 
  - name: myapp  #前面的-号表示这是一个列表格式的,也可以用中括号表示
    image: ikubernetes/myapp:v1
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-second
  namespace: default
  labels:
    app: db  #kv格式的,也可以用花括号表示
    tier: backend #定义所属的层次
  annotations:
    chenzx.com/created-by: "cluster-admin" #这是注解的键值对
spec:
  containers: 
  - name: busybox  #前面的-号表示这是一个列表格式的,也可以用中括号表示
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command: ["sh","-c","sleep 3600"]
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution: #硬亲和性
      - labelSelector:
          matchExpressions: #匹配pod的,而不是匹配节点
          - {key: app, operator: In, values: ["myapp"]} #意思是当前这个pod要跟一个有着标签app=myapp(要和上面pod-first的metadata里面的标签一致)的pod在一起
        topologyKey: zone

    上面把topologyKey(标签选择器的值)的值改成zone,而pod调度策略是podAntiAffinity反亲和性,所以pod-first和pod-second不能同时运行在标有zone标签的节点上。所以最终出现的现场就是有一个pod-first能成功运行,而另外一个pod-second因为是反亲和的,没有节点可以运行而处于pending状态。

[root@master scheduler]# kubectl get pods -o wide
NAME                               READY     STATUS             RESTARTS   AGE       IP            NODE
pod-first                          1/1       Running            0          10m       10.244.2.37   node2
pod-second                         0/1       Pending            0          10m               

污点调度

    前面几种调度方式都是让pod来选择节点。污点是让节点来选择哪些pod能运行在其上面。

    污点也是键值对。

[root@master scheduler]# kubectl explain nodes.spec.taints
effect:表示当pod不能容忍节点上污点时的行为是什么,主要有三种行为:
NoSchedule:仅影响调度过程,不影响现存pod。没调度过来的就调度不过来了。如果对节点新加了污点,那么对节点上现存的Pod没有影响。
NoExecute:既影响调度过程,也影响现存Pod,没调度过来的就调度不过来了,如果对节点新加了污点,那么对现存的pod对象将会被驱逐
PreferNoSchedule:不能容忍就不能调度过来,但是实在没办法也是能调度过来的。对节点新加了污点,那么对节点上现存的pod没有影响。

    查看节点的污点:

[root@master scheduler]# kubectl describe node node1
Taints:             

    查看pod的容忍度:

[root@master scheduler]# kubectl describe pods myapp-0
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s

    每次都要检查pod的容忍度是否和节点的污点匹配。

    下面我们给node1打上污点node-type=production:NoSchedule,注意污点和容忍度都是自定义的键值对。

[root@master scheduler]# kubectl taint node node1 node-type=production:NoSchedule
node/node1 tainted
[root@master scheduler]# kubectl explain pods.spec.tolerations
tolerationSeconds:表示宽限多长时间pod被驱逐
operator:操作符,其值有Exists表示只要节点有这个污点的key,pod都能容忍;Equal表示只要节点必须精确匹配污点的key和value才能容忍,
[root@master scheduler]# cat pod-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:v1
        ports:
        - name: http
          containerPort: 80
      tolerations: 
        - key: "node-type"
          operator: "Equal" #表示只要节点必须精确匹配污点的key和value才能容忍
          value: "production"
          effect: "NoSchedule"
[root@master scheduler]# kubectl get pods -o wide
NAME                            READY     STATUS              RESTARTS   AGE       IP            NODE
myapp-deploy-98fddd79f-6dzbg    1/1       Running             0          9s        10.244.1.58   node1
myapp-deploy-98fddd79f-8fqn4    1/1       Running             0          12s       10.244.1.57   node1
myapp-deploy-98fddd79f-cqnbj    1/1       Running             0          6s        10.244.1.59   node1

    看到pod能容忍node1的污点node-type=production:NoSchedule,所以pod可以被调度到node1上运行。

    下面我们把operator: "Equal"改成operator: "Exists"

[root@master scheduler]# cat pod-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:v1
        ports:
        - name: http
          containerPort: 80
      tolerations: 
        - key: "node-type"
          operator: "Exists" #表示只要节点匹配key: node-type,不管其value是什么,pod都能容忍节点上的污点
          value: ""
          effect: "NoSchedule"
[root@master scheduler]# kubectl apply -f pod-demo.yaml
[root@master scheduler]# kubectl get pods -o wide
NAME                            READY     STATUS              RESTARTS   AGE       IP            NODE
myapp-deploy-7dd988dc9d-747t4   1/1       Running             0          51s       10.244.2.45   node2
myapp-deploy-7dd988dc9d-cl4ft   1/1       Running             0          47s       10.244.1.60   node1
myapp-deploy-7dd988dc9d-hnrr5   1/1       Running             0          49s       10.244.2.46   node2

    最后,我们去掉节点上的污点:

[root@master metrics]# kubectl taint node node1 node-type-
node/node1 untainted


当前标题:docker笔记33-调度器、预选策略及优选函数-创新互联
文章转载:http://chengdu.cdxwcx.cn/article/dopoip.html