K8S之Pod调度

一、Schedule

1.1 简介

  调度器通过kubernetes的Watch机制来发现集群中新创建且尚未被调度到Node上的Pod。调度器会将发现的每一个未调度的Pod调度到一个合适 Node上来运行,从而更加合理、更加充分的利用集群的资源。

1.2 调度流程

  kube-scheduler的主要作用就是根据特定的调度算法和调度策略将Pod调度到合适的Node节点上去,是一个独立的二进制程序,启动之后会一直监听API Server,获取Pod Spec.NodeName为空的Pod,对每个Pod都会创建一个binding。
  kube-scheduler给一个pod做调度包含两个步骤,具体流程如下:
  1.)首先,客户端通过API Server的REST API或者kubectl 工具创 Pod资源API Server收到用户请求后,存储相关数据到etcd数据库中
  2.)调度器监听API Server查看到还未被调度(bind)的Pod列表,循环遍历地为每个 Pod 尝试分配节点。
    a.)预选阶段(Predicates),过滤节点,调度器用一组规则过滤掉不符合要求的 Node 节点,比如 Pod 设置了资源的 request,那么可用资源比 Pod 需要的资源少的主机显然就会被过滤掉
    b.)优选阶段(Priorities),为节点的优先级打分,将上一阶段过滤出来的 Node 列表进行打分,调度器会考虑一些整体的优化策略,比如把 Deployment 控制的多个 Pod 副本尽量分布到不同的主机上,使用最低负载的主机等等策略
经过上面的阶段过滤后选择打分最高的 Node 节点和 Pod 进行 binding 操作,然后将结果存储到 etcd 中 最后被选择出来的 Node 节点对应的 kubelet 去执行创建 Pod 的相关操作(当然也是 watch APIServer 发现的)。
  最后,kube-scheduler会将Pod调度到得分最高的Node上。如果存在多个得分最高的Node,kube-scheduler会从中随机选取一个。最后,调度器将这个调度决定告知kube-apiserver,这个过程叫做绑定(Binding)。

二、亲和性和反亲和性

  默认情况下集群使用自动调度策略调度Pod,保证每个节点的资源,负载情况一致。但是有的时候需要调度Pod到指定节点,比如针对数据库这类对IO要求高的Pod调度到全闪服务器;人工智能(深度学习)调度到带有GPU的节点等等,这些就需要亲和性和反亲和性。

2.1 nodeSelector

  nodeSelector是最长使用且简单的方式,通过label标签选着调度到某个节点。

# cat pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app: web
spec:
  containers:
  - name: nginx
    image: nginx
  nodeSelector:
    servicetype: DELL-sc7020

# kubectl get pod nginx   可以看到pod一致处于pending状态,因为找不到存在‘servicetype: DELL-sc7020’的节点
NAME    READY   STATUS    RESTARTS   AGE
nginx   0/1     Pending   0          76s
# kubectl describe pod nginx
Events:
  Type     Reason            Age    From               Message
  ----     ------            ----   ----               -------
  Warning  FailedScheduling  2m34s  default-scheduler  0/1 nodes are available: 1 node(s) didn't match Pod's node affinity.
  Warning  FailedScheduling  2m34s  default-scheduler  0/1 nodes are available: 1 node(s) didn't match Pod's node affinity.

# kubectl label nodes yc servicetype=DELL-sc7020
node/yc labeled
# kubectl get pod nginx 给节点打上标签之后就正常了
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          4m4s

2.2 节点亲和性

  亲和性分为nodeAffinity和podAffinity。亲和性存在requiredDuringSchedulingIgnoredDuringExecution(硬策略,如果没有满足条件的节点的话,就不断重试直到满足条件为止)和preferredDuringSchedulingIgnoredDuringExecution(软策略,如果现在没有满足调度要求的节点的话,Pod 就会忽略这条规则,继续完成调度过程)。

# cat pod-affinity.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-affinity
  labels:
    app: pod-affinity
spec:
  replicas: 2
  selector:
    matchLabels:
      app: pod-affinity
  template:
    metadata:
      labels:
        app: pod-affinity
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
          name: nginxweb
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                - key: kubernetes.io/hostname
                  operator: In
                  values:
                  - yc
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 1
            preference:
              matchExpressions:
              - key: environment
                operator: In
                values:
                - dev
# kubectl apply -f pod-affinity.yaml
deployment.apps/pod-affinity created
# kubectl get deployment pod-affinity
NAME           READY   UP-TO-DATE   AVAILABLE   AGE
pod-affinity   2/2     2            2           84s
# kubectl get pods -l app=pod-affinity -o wide
NAME                           READY   STATUS    RESTARTS   AGE    IP            NODE   NOMINATED NODE   READINESS GATES
pod-affinity-747b4d579-b2wrk   1/1     Running   0          7m7s   10.255.0.38   yc     <none>           <none>
pod-affinity-747b4d579-xb4gc   1/1     Running   0          7m7s   10.255.0.39   yc     <none>           <none>

  上面例子首先要满足kubernetes.io/hostname=yc,不满足则pod一直处于pending状态,然后检查是否满足environment=dev,不满足依然调度至该节点。
  亲和性设置中的operator字段可以成:
  In:label 的值在某个列表中
  NotIn:label 的值不在某个列表中
  Gt:label 的值大于某个值
  Lt:label 的值小于某个值
  Exists:某个 label 存在
  DoesNotExist:某个 label 不存在

  需要注意的是如果nodeSelectorTerms下面有多个选项的话,满足任何一个条件就可以;如果matchExpressions有多个选项的话,则必须同时满足这些条件才能正常调度 Pod。

2.3 Pod亲和性

  Pod 亲和性(podAffinity)主要解决 Pod 可以和哪些 Pod部署在同一个拓扑域中的问题(其中拓扑域用主机标签实现,可以是单个主机,也可以是多个主机组成的 cluster、zone 等等),而 Pod 反亲和性主要是解决 Pod 不能和哪些 Pod部署在同一个拓扑域中的问题,它们都是处理的 Pod 与 Pod 之间的关系,比如一个Pod在一个节点上了,那么我这个也得在这个节点,或者你这个Pod在节点上了,那么我就不想和你待在同一个节点上。

# cat pod-affinity.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-affinity
  labels:
    app: pod-affinity
spec:
  replicas: 3
  selector:
    matchLabels:
      app: pod-affinity
  template:
    metadata:
      labels:
        app: pod-affinity
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
          name: nginxweb
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:  # 硬策略
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - nginx
            topologyKey: kubernetes.io/hostname

2.4 Pod反亲和性

  反着来,不和某个pod调度到同一节点

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-antiaffinity
  labels:
    app: pod-antiaffinity
spec:
  replicas: 3
  selector:
    matchLabels:
      app: pod-antiaffinity
  template:
    metadata:
      labels:
        app: pod-antiaffinity
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
          name: nginxweb
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:  # 硬策略
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - busybox-pod
            topologyKey: kubernetes.io/hostname

三、污点和容忍度

  污点和亲和和反亲和不一样,污点是拒绝某些Pod调度到该节点。
  容忍度(Toleration)是应用于Pod上的,允许(但并不要求)Pod 调度到带有与之匹配的污点的节点上。比如说Master节点通过污点node-role.kubernetes.io/master:NoSchedule使得pod不会调度到master。

# kubectl describe pod client1
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                 node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type     Reason            Age    From               Message
  ----     ------            ----   ----               -------
  Warning  FailedScheduling  7m41s  default-scheduler  0/1 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate.
  Warning  FailedScheduling  7m41s  default-scheduler  0/1 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate.
  Warning  FailedScheduling  116s   default-scheduler  0/1 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate.

  污点影响策略有三种:
  1.)NoSchedule:只有拥有和这个污点相匹配的容忍度的Pod才能够被分配到该节点
  2.)PreferNoSchedule:NoSchedule的软策略版本,表示尽量不调度到污点节点上去
  3.)NoExecute:该选项意味着一旦Taint生效,如该节点内正在运行的 Pod 没有对应容忍(Tolerate)设置,则会直接被驱逐。
  如果需要调度Pod调度到有污点的节点可以定义Toleration 。例如:

# cat taint.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: taint
  labels:
    app: taint
spec:
  replicas: 3
  selector:
    matchLabels:
      app: taint
  template:
    metadata:
      labels:
        app: taint
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - name: http
          containerPort: 80
      tolerations:
      - key: "node-role.kubernetes.io/master"
        operator: "Exists"
        effect: "NoSchedule

# kubectl apply -f taint.yaml
# kubectl get pod -l app=taint
NAME                     READY   STATUS    RESTARTS   AGE
taint-7bd54b74c8-mp2s8   1/1     Running   0          106s
taint-7bd54b74c8-q8w2b   1/1     Running   0          106s
taint-7bd54b74c8-vfg2m   1/1     Running   0          106s

  对于tolerations属性的写法,其中的key、value、effect 与Node的Taint设置需保持一致, 还有以下几点说明:
  a.)如果 operator 的值是 Exists,则 value 属性可省略
  a.)如果 operator 的值是 Equal,则表示其 key 与 value 之间的关系是 equal(等于)
  c.)如果不指定operator属性,则默认值为Equal

  以上说明还存在两种特殊情况:
  a.)空的key如果再配Exists就能匹配所有的key与value,也就是是能容忍所有节点的所有Taints
  b.)空的effect匹配所有的effect

  污点标记及取消,可以使用下面的命令:

# kubectl taint nodes node1 key1=value1:NoSchedule
给节点yc打污点,
# kubectl taint node yc key1=value1:NoSchedule-
Logo

开源、云原生的融合云平台

更多推荐