theboyaply
theboyaply
发布于 2020-04-02 / 1421 阅读
0
0

k8s 调度流程介绍

k8s 调度过程介绍

Scheduler 是 k8s 中的调度器,主要的让你无是把定义的 Pod 分配到集群的节点上。听起来很简单,但有很多要考虑的问题:

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

Scheduler 是作为一个单独的程序运行的,启动之后会一直监听 apiserver。

调度过程

调度过程分为几个部分:

  1. 首先是过滤掉不满足条件的节点,这个过程称为 predicate
  2. 然后对满足条件的节点按照优先级排序,这个是 priority
  3. 最后从中选择优先级最高的节点。

上面步骤中任何步骤有错误,就直接返回错误。

predicate 有一系列的算法可以使用:

  • PodFitsResources:节点上剩余的资源是否大于 Pod 请求的资源。
  • PodFitsHost:如果 Pod 指定了 NodeName,检查节点名称是否和 NodeName 匹配。
  • PodFitsHostPorts:节点上已经使用的 port 是否和 Pod 申请的 port 冲突。
  • PodSelectorMatches:过滤掉和 Pod 指定的 labels 不匹配的节点。
  • NoDiskConflict:已经 mount 的 volume 和 Pod 指定的 volume 不冲突,除非他们都是只读。

如果在 predicate 过程中没有适合的节点,Pod 会一直处于 pending 状态,不断重试调度,直到有节点满足条件。

经过 predicate 后,如果有多个节点满足条件,就继续 priority 过程:按照优先级大小对节点排序。

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

  • LeastRequestedPriority:通过计算 CPU 和 Memory 的使用率来决定权重,使用率越低权重越高。
  • BalancedResourceAllocation:节点上 CPU 和 Memory 使用率越接近,权重越高。这个应该和 BalancedResourceAllocation 一起使用。
  • ImageLocalityPriority:如果 Pod 要使用的镜像节点中已经有了,并且 镜像总大小值越大,权重越高。

亲和性

亲和性策略,可以设置 Pod 在创建时更倾向创建在哪个节点上。其有两个维度可设置:

  • 节点亲和性:设置节点的相关信息,在创建 Pod 的时候与节点信息进行匹配。
  • Pod 亲和性:设置 Pod 的相关信息,在创建 Pod 的时候与已存在的 Pod 信息进行匹配。

不管是节点亲和性还是 Pod 亲和性,其设置策略中都有 软策略硬策略 两种方案。

  • 软策略:Pod 倾向于创建在某个节点上,如果条件不符合,可以创建在其它节点上。
  • 硬策略:Pod 必须被创建某个节点上,如果条件不符合,那么 Pod 会一直处于 pending 状态。

节点亲和性

pod.spec.affinity.nodeAffinity

  • preferredDuringSchedulingIgnoredDuringExecution:软策略
  • requiredDuringSchedulingIgnoredDuringExecution:硬策略

软策略示例

apiVersion: v1
kind: Pod
metadata:
  name: node-pre-pod
spec:
  containers:
  - name: nginx-container
    image: hub.xixihaha.com/library/mynginx:v1
    ports:
    - containerPort: 80
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: kubernetes.io/hostname
            operator: In
            values:
            - k8s-node01

说明:

  • weight:权重值。一个 Pod 可能会有多个软策略,按照权重值从大到小选择。
  • preference:固定格式。
  • matchExpressions:固定格式。
  • key:节点的标签名称。可使用 kubectl get node --show-labels 查看。
  • values:标签的值。
  • operator:键值运算关系符。

以上资源清单创建的 Pod,会优先在节点标签中含有 kubernetes.io/hostname=k8s-node01 的节点上创建。如果在整个 k8s 集群找不到符合条件的节点,那么 scheduler 会根据一些其它条件自动分配到某一节点中。

硬策略示例

apiVersion: v1
kind: Pod
metadata:
  name: node-req-pod
spec:
  containers:
  - name: nginx-container
    image: hub.xixihaha.com/library/mynginx:v1
    ports:
    - containerPort: 80
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/hostname
            operator: NotIn
            values:
            - k8s-node01

以上资源清单创建的 Pod,不会在节点标签中有 kubernetes.io/hostname=k8s-node01 的节点上创建,如果集群中没有符合的节点,那么该 Pod 会一直处于 pending 状态。

当然我们也可以将这两个策略放在一起:

apiVersion: v1
kind: Pod
metadata:
  name: node-pod
spec:
  containers:
  - name: nginx-container
    image: hub.xixihaha.com/library/mynginx:v1
    ports:
    - containerPort: 80
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/hostname
            operator: NotIn
            values:
            - k8s-node01
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: kubernetes.io/hostname
            operator: In
            values:
            - k8s-node02

这样的 Pod 会先匹配符合硬策略的节点,然后再匹配符合软策略的节点。

Pod 亲和性

pod.spec.affinity.podAffinity/podAntiAffinity

  • preferredDuringSchedulingIgnoredDuringExecution:软策略
  • requiredDuringSchedulingIgnoredDuringExecution:硬策略

其中的软策略和硬策略与节点亲和性是一个意思。

你可能会注意到,上面的两个可选项:

  • podAffinity:Pod 与指定的 Pod 在同一拓扑域。
  • podAntiAffinity:Pod 与指定的 Pod 不在同一拓扑域。

拓扑域:使用 topologyKey 属性(下方示例中有)定义的值,通俗来讲就是节点的标签。

如果使用的是 podAffinity,那么在 Pod 创建时,如果寻找到了符合条件的 Pod(假设该 Pod 所在的节点标签为 node-x),那么我们要创建的 Pod 就可以在任一含有 node-x 标签的节点中创建。

podAntiAffinity 则反之。

因为软策略与硬策略上面已经介绍过了,这里直接一起放入示例中:

apiVersion: v1
kind: Pod
metadata:
  name: pod-aff-pod
spec:
  containers:
  - name: nginx-container
    image: hub.xixihaha.com/library/mynginx:v1
    ports:
    - containerPort: 80
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: pod-name
            operator: In
            values:
            - nginx-pod
        topologyKey: kubernetes.io/hostname
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: pod-name
              operator: In
              valuses:
              - nginx-pod
        topologyKey: kubernetes.io/hostname

operator 及调度目标

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

污点和容忍

上面介绍的 亲和性,是 Pod 的一种属性(偏好或者硬性要求),它使 Pod 被吸引到一类特点的节点。污点(taint)则相反,它使 Pod 排斥一类特点的节点。

污点(taint)和容忍(toleration)相互相互配合,可以用来避免 Pod 被分配到不适合的节点上。每个节点可以有0个或者多个 taint,这表示对于那些不能容忍这些 taint 的 Pod,是不会被创建在这些节点上的。如果将 toleration 应用于 Pod 上,则表示这些 Pod 可以被调度到具有匹配 taint 的节点上。

通俗来讲,就是节点上可以设置污点(taint),如果创建 Pod 时不设置 Pod 的容忍(toleration)的话,那么该 Pod 就不可能被创建在有污点的节点上。

master 节点默认有一个污点,这也就是为什么前面我们创建的 Pod 一直没有被创建在 master 节点上的原因。

[root@k8s-master01 ~]# kubectl get node
NAME           STATUS   ROLES    AGE   VERSION
k8s-master01   Ready    master   15d   v1.15.1
k8s-node01     Ready    <none>   15d   v1.15.1
k8s-node02     Ready    <none>   15d   v1.15.1

[root@k8s-master01 ~]# kubectl describe node k8s-master01
......
Taints:             node-role.kubernetes.io/master:NoSchedule
......

污点(taint)

污点的组成

key=value:effect
  • key=value:每一个污点都有一个 key 和 value 作为 污点的标签,其中 value 可以为空,即 key:effect。我们上面列出的 master 默认污点就是这种格式。

  • effect:描述污点的作用,支持以下三种选项:

    • NoSchedule:k8s 不会将 Pod 调度到具有该污点的 Node 上。
    • PreferNoSchedule:k8s 尽量避免将 Pod 调度到具有该污点的 Node 上。
    • NoExecute:k8s 不会将 Pod 调度到具有该污点的 Node 上,同时会将 Node 上已存在的 Pod 驱逐出去。

    注意:上面描述的前提是所有 Pod 是没有设置容忍的。

设置、去除污点

# 设置污点
kubectl taint nodes k8s-node01 check=test:NoSchedule

# 去除污点,只需要在最后加一个 '-'
kubectl taint nodes k8s-node01 check=test:NoSchedule-

# 查看某个node的污点,其结果中的 Taints 字段便是污点
kubectl describe node k8s-node01

容忍(toleration)

设置了污点的 node 将根据污点的作用(effect)与 Pod 之间产生互斥的关系。但是我们可以在创建 Pod 时为 Pod 设置容忍,意味着 Pod 在创建时可以容忍污点的存在,可以被调度到存在污点的 node 上。

污点的格式如下:

spec:
  tolerations:
  - effect: "NoSchedule"
    key: "key"
    operator: "Exists"
    tolerationSeconds: 3600
    value: "value"

说明:

  • key、value、effect:需要与 node 上的污点标签信息一致。

  • operator:表示 key 与 value 的关系。

    • Exists:忽略 value 的值,只要 key 匹配上即可。
    • Equal:默认为 Equal。
  • tolerationSeconds:如果 effect 的值为 Noexecute,那么 tolerationSeconds 表示 Pod 在被驱逐之前还可以保留运行的时间。

注意:

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

spec:
  tolerations:
  - operator: "Exists"

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

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

有多个 master 存在时,防止资源浪费,可以如下设置:

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

指定调度节点

如果你想要指定 Pod 被调度到具体的 node 上,那么你可以这样做:

kind: Pod
spec:
  nodeName: k8s-node01
# 或者
spec:
  nodeSelector:
    kubernetes.io/hostname: k8s-node01

nodeName:将 Pod 直接调度到指定的 node 节点上,会跳过 Scheduler 的调度策略,是强制匹配。

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

-- end --


评论