k8s 调度过程介绍
Scheduler 是 k8s 中的调度器,主要的让你无是把定义的 Pod 分配到集群的节点上。听起来很简单,但有很多要考虑的问题:
- 公平:如何保证每个节点都能被分配资源。
- 资源利用率高:集群所有资源最大化被使用。
- 效率:调度的性能要好,能够尽快的对大批量的 Pod 完成调度工作。
- 灵活:允许用户根据自己的需求控制调度逻辑。
Scheduler 是作为一个单独的程序运行的,启动之后会一直监听 apiserver。
调度过程
调度过程分为几个部分:
- 首先是过滤掉不满足条件的节点,这个过程称为
predicate
。 - 然后对满足条件的节点按照优先级排序,这个是
priority
。 - 最后从中选择优先级最高的节点。
上面步骤中任何步骤有错误,就直接返回错误。
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 | 否 | 指定主机 |
podAffinity | Pod | In、NotIn、Exists、DoesNotExist | 是 | Pod 与指定 Pod 在同一拓扑域 |
podAnitAffinity | Pod | In、NotIn、Exists、DoesNotExist | 是 | Pod 与指定 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 --